diff --git a/.github/workflows/pypi-deploy.yml b/.github/workflows/pypi-deploy.yml deleted file mode 100644 index 6407021c..00000000 --- a/.github/workflows/pypi-deploy.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Deploy delocate to PyPI - -on: - push: - tags: - - "*.*.*" - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 - with: - python-version: "3.x" - - name: Install deployment dependencies - run: | - pip install twine build - - name: Build distribution - run: | - python -m build - - name: Upload to PyPI - env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} - run: | - twine upload dist/* --non-interactive --skip-existing diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index e1ff90b2..0eb7f28b 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -3,6 +3,10 @@ name: Python Package on: push: + branches: + - "*" + tags: + - "*.*.*" pull_request: defaults: @@ -13,19 +17,19 @@ jobs: pre-commit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: - python-version: "3.12" - - uses: pre-commit/action@v3.0.0 + python-version: "3.x" + - uses: pre-commit/action@v3.0.1 mypy: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: - python-version: "3.8" + python-version: "3.x" - name: Install MyPy run: | pip install mypy @@ -34,26 +38,22 @@ jobs: pip install --requirement test-requirements.txt pip install --editable . - name: MyPy - uses: liskin/gh-problem-matcher-wrap@v2 + uses: liskin/gh-problem-matcher-wrap@v3 with: linters: mypy run: mypy --show-column-numbers delocate/ tests: needs: [pre-commit, mypy] - runs-on: macos-11 + runs-on: macos-12 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: true - uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: "12.4" - - name: Workaround ARM64 issues - # https://github.com/actions/virtual-environments/issues/2557 - run: | - sudo rm -Rf /Library/Developer/CommandLineTools/SDKs/* - - uses: actions/setup-python@v4 + xcode-version: "14.0" + - uses: actions/setup-python@v5 with: python-version: "3.x" - name: Build libs and wheels @@ -65,8 +65,10 @@ jobs: env: MB_PYTHON_VERSION: "3.9" MB_PYTHON_OSX_VER: "10.9" + SOURCE_DATE_EPOCH: "0" + PYTHONHASHSEED: "0" - name: Upload test data - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: delocate-tests-data path: | @@ -83,19 +85,26 @@ jobs: run: | pip install -e . - name: Upload wheel - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: delocate-wheel - path: | - dist/delocate-*.whl + path: dist/delocate-*.whl + retention-days: 3 + if-no-files-found: error + compression-level: 0 + - name: Upload sdist + uses: actions/upload-artifact@v4 + with: + name: delocate-sdist + path: dist/delocate-*.tar.gz retention-days: 3 if-no-files-found: error + compression-level: 0 - name: Upload requirements - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: test-requirements - path: | - test-requirements.txt + path: test-requirements.txt retention-days: 3 if-no-files-found: error - name: Run tests @@ -105,10 +114,14 @@ jobs: run: | coverage xml - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: - fail_ci_if_error: true + fail_ci_if_error: false token: "624762bf-aaf0-4a75-b450-16f5ebece0b1" + - name: Check reproducable test data + run: | + git status + git diff isolated_tests: needs: tests @@ -117,12 +130,6 @@ jobs: fail-fast: true matrix: include: - - os: "macos-11" - xcode: "12.4" - python: "3.x" - - os: "macos-11" - xcode: "13.0" - python: "3.x" - os: "macos-12" xcode: "14.0" python: "3.x" @@ -139,6 +146,10 @@ jobs: python: "3.10" - os: "ubuntu-latest" python: "3.11" + - os: "ubuntu-latest" + python: "3.12" + - os: "ubuntu-latest" + python: "3.13-dev" - os: "windows-latest" python: "3.x" steps: @@ -146,21 +157,21 @@ jobs: uses: maxim-lobanov/setup-xcode@v1 with: xcode-version: ${{ matrix.xcode }} - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} - if: runner.os == 'macOS' name: Check otool version run: otool --version - name: Download wheel - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: delocate-wheel - name: Install delocate run: | pip install delocate-*.whl - name: Download requirements - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: test-requirements - name: Install test dependencies @@ -169,3 +180,30 @@ jobs: - name: Run isolated tests run: | pytest --pyargs delocate --log-level DEBUG --doctest-modules + + deploy: + needs: isolated_tests + if: startsWith(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.x" + - name: Install deployment dependencies + run: pip install twine + - name: Download wheel + uses: actions/download-artifact@v4 + with: + name: delocate-wheel + path: dist/ + - name: Download sdist + uses: actions/download-artifact@v4 + with: + name: delocate-sdist + path: dist/ + - name: Upload to PyPI + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + run: twine upload dist/* --non-interactive --skip-existing diff --git a/.gitignore b/.gitignore index 72b7cafe..3d038113 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ build/ dist/ MANIFEST .coverage +coverage.xml delocate*info/ .cache _version.py +downloads/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 32958de7..a44999d2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v4.6.0 hooks: - id: check-added-large-files - id: check-case-conflict @@ -18,7 +18,7 @@ repos: - id: fix-byte-order-marker - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.3.4 + rev: v0.4.10 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] diff --git a/delocate/tests/data/make_libs.sh b/delocate/tests/data/make_libs.sh index be943a73..469af2de 100755 --- a/delocate/tests/data/make_libs.sh +++ b/delocate/tests/data/make_libs.sh @@ -3,6 +3,7 @@ # Run in directory containing this file # With thanks to https://dev.lsstcorp.org/trac/wiki/LinkingDarwin export MACOSX_DEPLOYMENT_TARGET=10.9 +export SOURCE_DATE_EPOCH=0 if [ "$CXX" = "" ]; then CXX=c++ diff --git a/delocate/tests/test_wheeltools.py b/delocate/tests/test_wheeltools.py index e3b6dc15..74ec0bf4 100644 --- a/delocate/tests/test_wheeltools.py +++ b/delocate/tests/test_wheeltools.py @@ -1,11 +1,14 @@ """Tests for wheeltools utilities.""" +from __future__ import annotations + import os import re import shutil from email.message import Message from os.path import basename, exists, isfile, realpath, splitext from os.path import join as pjoin +from pathlib import Path from typing import AnyStr, List, Tuple from zipfile import ZipFile @@ -38,7 +41,7 @@ # Template for testing expected wheel information EXP_PLAT = splitext(PLAT_WHEEL)[0].split("-")[-1] EXP_ITEMS = [ - ("Generator", "bdist_wheel {pip_version}"), + ("Generator", "{generator_tool_version}"), ("Root-Is-Purelib", "false"), ("Tag", "{pyver}-{abi}-" + EXP_PLAT), ("Wheel-Version", "1.0"), @@ -48,7 +51,7 @@ # Expected outputs for plat added wheels minus wheel-version (that might # change) EXTRA_EXPS = [ - ("Generator", "bdist_wheel {pip_version}"), + ("Generator", "{generator_tool_version}"), ("Root-Is-Purelib", "false"), ] + [("Tag", "{pyver}-{abi}-" + plat) for plat in (EXP_PLAT,) + EXTRA_PLATS] @@ -124,12 +127,17 @@ def test_in_wheel(): assert_false(isfile(mod_path)) -def _filter_key(items, key): +def _filter_key( + items: list[tuple[str, str]], key: str +) -> list[tuple[str, str]]: + """Return a list of key/value pairs with any instances of `key` removed.""" return [(k, v) for k, v in items if k != key] -def get_info(wheel_path: str) -> Message: - name, version, _, _ = parse_wheel_filename(basename(wheel_path)) +def get_info(wheel_path: str | os.PathLike[str]) -> Message: + """Return the `.dist-info/WHEEL` metadata from `wheel_path`.""" + wheel_path = Path(wheel_path) + name, version, _, _ = parse_wheel_filename(wheel_path.name) with ZipFile(wheel_path) as zip_file: return read_pkg_info_bytes( zip_file.read(f"{name}-{version}.dist-info/WHEEL") @@ -137,25 +145,28 @@ def get_info(wheel_path: str) -> Message: def assert_winfo_similar( - whl_fname: str, exp_items: List[Tuple[str, str]], drop_version: bool = True + wheel_path: str | os.PathLike[str], + expected: List[Tuple[str, str]], + drop_version: bool = True, ) -> None: - match = WHEEL_INFO_RE.match(basename(whl_fname)) + """Assert `wheel_path` has `.dist-info/WHEEL` items matching `expected`. + + Skips `Wheel-Version` check if `drop_version` is True. + """ + wheel_path = Path(wheel_path) + match = WHEEL_INFO_RE.match(wheel_path.name) assert match wheel_parts = match.groupdict() # Info can contain duplicate keys (e.g. Tag) - w_info = sorted(get_info(whl_fname).items()) + wheel_info: list[tuple[str, str]] = get_info(wheel_path).items() if drop_version: - w_info = _filter_key(w_info, "Wheel-Version") - exp_items = _filter_key(exp_items, "Wheel-Version") - assert len(exp_items) == len(w_info) + wheel_info = _filter_key(wheel_info, "Wheel-Version") + expected = _filter_key(expected, "Wheel-Version") # Extract some information from actual values - wheel_parts["pip_version"] = dict(w_info)["Generator"].split()[1] - for (key1, value1), (key2, value2) in zip( - sorted(exp_items), sorted(w_info) - ): - assert key1 == key2 - value1 = value1.format(**wheel_parts) - assert value1 == value2 + wheel_parts["generator_tool_version"] = dict(wheel_info)["Generator"] + # Apply variable metadata to expected items + expected = [(k, v.format(**wheel_parts)) for k, v in expected] + assert sorted(wheel_info) == sorted(expected) def test_add_platforms() -> None: diff --git a/wheel_makers/make_wheels.sh b/wheel_makers/make_wheels.sh index 49c78b54..0eb71c38 100755 --- a/wheel_makers/make_wheels.sh +++ b/wheel_makers/make_wheels.sh @@ -7,6 +7,8 @@ mac_ver=10.9 export MACOSX_DEPLOYMENT_TARGET=${mac_ver} export _PYTHON_HOST_PLATFORM="macosx-${mac_ver}-universal2" +export SOURCE_DATE_EPOCH=0 +export PYTHONHASHSEED=0 rm -f */dist/fakepkg*.whl rm -f */libs/*.dylib