From df67172373fc005b84dbc651f848075f9ca5d124 Mon Sep 17 00:00:00 2001 From: Thibaut Decombe <68703331+UnknownPlatypus@users.noreply.github.com> Date: Sun, 22 Sep 2024 17:59:02 +0200 Subject: [PATCH] Cross compile and publish to pypi (#1) * Cross compile wheels * skip macos-latest wheel --- .github/workflows/main.yml | 13 ++-- README.md | 4 +- setup.cfg | 32 ++++++++- setup.py | 143 +------------------------------------ tox.ini | 2 +- 5 files changed, 46 insertions(+), 148 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 18a53ab..e167261 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -3,24 +3,29 @@ name: main on: push: branches: [main, test-me-*] - tags: + tags: '*' pull_request: jobs: main-windows: - uses: asottile/workflows/.github/workflows/tox.yml@v1.4.1 + uses: asottile/workflows/.github/workflows/tox.yml@v1.6.1 with: env: '["py38"]' os: windows-latest wheel-tags: true main-macos: - uses: asottile/workflows/.github/workflows/tox.yml@v1.4.1 + uses: asottile/workflows/.github/workflows/tox.yml@v1.6.1 with: env: '["py38"]' os: macos-latest + main-macos-intel: + uses: asottile/workflows/.github/workflows/tox.yml@v1.6.1 + with: + env: '["py38"]' + os: macos-13 wheel-tags: true main-linux: - uses: asottile/workflows/.github/workflows/tox.yml@v1.4.1 + uses: asottile/workflows/.github/workflows/tox.yml@v1.6.1 with: env: '["py38"]' os: ubuntu-latest diff --git a/README.md b/README.md index dcdde7c..10f35ba 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Sample `.pre-commit-config.yaml`: ```yaml - repo: https://github.com/UnknownPlatypus/jpegoptim-py - rev: v1.5.5.1 + rev: v1.5.5.2 hooks: - id: jpegoptim ``` @@ -39,7 +39,7 @@ Sample `.pre-commit-config.yaml` with lossy compression enabled: ```yaml - repo: https://github.com/UnknownPlatypus/jpegoptim-py - rev: v1.5.5.1 + rev: v1.5.5.2 hooks: - id: jpegoptim args: [--strip-all, --all-progressive, --max=85, --threshold=3] diff --git a/setup.cfg b/setup.cfg index accd02f..e29b3b5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,6 @@ [metadata] name = jpegoptim_py +version = 1.5.5.2 description = Python wrapper around invoking jpegoptim (https://github.com/tjko/jpegoptim) long_description = file: README.md long_description_content_type = text/markdown @@ -16,4 +17,33 @@ classifiers = Programming Language :: Python :: Implementation :: PyPy [options] -python_requires = >=3.7 +python_requires = >=3.8 +setup_requires = + setuptools-download + +[setuptools_download] +download_scripts = + [jpegoptim] + group = jpegoptim-binary + marker = sys_platform == "linux" and platform_machine == "x86_64" + url = https://github.com/tjko/jpegoptim/releases/download/v1.5.5/jpegoptim-1.5.5-x64-linux.zip + sha256 = 1b3f9f3e92771f49796b3060c7f338e0bb12c71c668d821446772bd92092d865 + extract = zip + extract_path = jpegoptim + [jpegoptim] + group = jpegoptim-binary + marker = sys_platform == "darwin" and platform_machine == "x86_64" + marker = sys_platform == "darwin" and platform_machine == "arm64" + url = https://github.com/tjko/jpegoptim/releases/download/v1.5.5/jpegoptim-1.5.5-x64-osx.zip + sha256 = 7ab86a5a0e13b3df7ec496c61978e7a71c275cf1a0d2cc35de07ba8ae0c10b95 + extract = zip + extract_path = jpegoptim + [jpegoptim.exe] + group = jpegoptim-binary + marker = sys_platform == "win32" and platform_machine == "AMD64" + marker = sys_platform == "win32" and platform_machine == "ARM64" + marker = sys_platform == "cygwin" and platform_machine == "x86_64" + url = https://github.com/tjko/jpegoptim/releases/download/v1.5.5/jpegoptim-1.5.5-x64-windows.zip + sha256 = ffa037ecc12e002c1c87c334d8ebaed988ec738c3ad606d054272950519b09a8 + extract = zip + extract_path = jpegoptim.exe diff --git a/setup.py b/setup.py index 3c67a93..60a8906 100644 --- a/setup.py +++ b/setup.py @@ -1,149 +1,12 @@ #!/usr/bin/env python3 from __future__ import annotations -import hashlib -import http -import io -import os.path -import platform -import stat -import sys -import urllib.request -import zipfile - -from distutils.command.build import build as orig_build -from distutils.core import Command from setuptools import setup -from setuptools.command.install import install as orig_install - -JPEGOPTIM_VERSION = "1.5.5" -PY_VERSION = "1" # Python wrapper version - -POSTFIX_SHA256 = { - ("linux", "x86_64"): ( - "x64-linux.zip", - "1b3f9f3e92771f49796b3060c7f338e0bb12c71c668d821446772bd92092d865", - ), - ("darwin", "x86_64"): ( - "x64-osx.zip", - "7ab86a5a0e13b3df7ec496c61978e7a71c275cf1a0d2cc35de07ba8ae0c10b95", - ), - ("win32", "AMD64"): ( - "x64-windows.zip", - "ffa037ecc12e002c1c87c334d8ebaed988ec738c3ad606d054272950519b09a8", - ), -} -POSTFIX_SHA256[("darwin", "arm64")] = POSTFIX_SHA256[("darwin", "x86_64")] - - -def get_download_url() -> tuple[str, str]: - postfix, sha256 = POSTFIX_SHA256[(sys.platform, platform.machine())] - url = ( - f"https://github.com/tjko/jpegoptim/releases/download/" - f"v{JPEGOPTIM_VERSION}/jpegoptim-{JPEGOPTIM_VERSION}-{postfix}" - ) - return url, sha256 - - -def download(url: str, sha256: str) -> bytes: - with urllib.request.urlopen(url) as resp: - code = resp.getcode() - if code != http.HTTPStatus.OK: - raise ValueError(f"HTTP failure. Code: {code}") - data = resp.read() - - checksum = hashlib.sha256(data).hexdigest() - if checksum != sha256: - raise ValueError(f"sha256 mismatch, expected {sha256}, got {checksum}") - - return data - - -def extract(url: str, data: bytes) -> bytes: - with io.BytesIO(data) as bio: - with zipfile.ZipFile(bio) as zipf: - for info in zipf.infolist(): - if info.filename.startswith("jpegoptim"): - return zipf.read(info.filename) - - raise AssertionError(f"unreachable {url}") - - -def save_executable(data: bytes, base_dir: str): - exe = "jpegoptim" if sys.platform != "win32" else "jpegoptim.exe" - output_path = os.path.join(base_dir, exe) - os.makedirs(base_dir) - - with open(output_path, "wb") as fp: - fp.write(data) - - # Mark as executable. - # https://stackoverflow.com/a/14105527 - mode = os.stat(output_path).st_mode - mode |= stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH - os.chmod(output_path, mode) - - -class build(orig_build): - sub_commands = orig_build.sub_commands + [("fetch_binaries", None)] - - -class install(orig_install): - sub_commands = orig_install.sub_commands + [("install_jpegoptim", None)] - - -class fetch_binaries(Command): - build_temp = None - - def initialize_options(self): - pass - - def finalize_options(self): - self.set_undefined_options("build", ("build_temp", "build_temp")) - - def run(self): - # save binary to self.build_temp - url, sha256 = get_download_url() - archive = download(url, sha256) - data = extract(url, archive) - save_executable(data, self.build_temp) - - -class install_jpegoptim(Command): - description = "install the jpegoptim executable" - outfiles = () - build_dir = install_dir = None - - def initialize_options(self): - pass - - def finalize_options(self): - # this initializes attributes based on other commands' attributes - self.set_undefined_options("build", ("build_temp", "build_dir")) - self.set_undefined_options( - "install", - ("install_scripts", "install_dir"), - ) - - def run(self): - self.outfiles = self.copy_tree(self.build_dir, self.install_dir) - - def get_outputs(self): - return self.outfiles - - -command_overrides = { - "install": install, - "install_jpegoptim": install_jpegoptim, - "build": build, - "fetch_binaries": fetch_binaries, -} - try: from wheel.bdist_wheel import bdist_wheel as orig_bdist_wheel except ImportError: - pass + cmdclass = {} else: class bdist_wheel(orig_bdist_wheel): @@ -157,6 +20,6 @@ def get_tag(self): # We don't contain any python source, nor any python extensions return "py2.py3", "none", plat - command_overrides["bdist_wheel"] = bdist_wheel + cmdclass = {"bdist_wheel": bdist_wheel} -setup(version=f"{JPEGOPTIM_VERSION}.{PY_VERSION}", cmdclass=command_overrides) +setup(cmdclass=cmdclass) diff --git a/tox.ini b/tox.ini index 77e373f..9b68ce0 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py38,pre-commit +envlist = py,pre-commit [testenv] commands =