diff --git a/.github/workflows/automerge.yaml b/.github/workflows/automerge.yaml deleted file mode 100644 index 6d495b2..0000000 --- a/.github/workflows/automerge.yaml +++ /dev/null @@ -1,28 +0,0 @@ -name: Dependabot auto-approve and enable auto-merge - -on: pull_request - -permissions: - pull-requests: write - contents: write - -jobs: - dependabot: - runs-on: ubuntu-latest - if: ${{ github.actor == 'dependabot[bot]' }} - steps: - - name: Dependabot metadata - id: metadata - uses: dependabot/fetch-metadata@v2.5.0 - with: - github-token: "${{ secrets.GITHUB_TOKEN }}" - - name: Approve a PR - run: gh pr review --approve "$PR_URL" - env: - PR_URL: ${{github.event.pull_request.html_url}} - GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} - - name: Enable auto-merge for Dependabot PRs - run: gh pr merge --auto --merge "$PR_URL" - env: - PR_URL: ${{github.event.pull_request.html_url}} - GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} diff --git a/.github/workflows/build_and_publish.yaml b/.github/workflows/build_and_publish.yaml index f41632c..92cdc73 100644 --- a/.github/workflows/build_and_publish.yaml +++ b/.github/workflows/build_and_publish.yaml @@ -5,6 +5,9 @@ on: branches: [main] env: UBUNTU_PRO_TOKEN: ${{ secrets.UBUNTU_PRO_TOKEN }} +permissions: + contents: read + packages: write jobs: publish: # note: this builds/tests all versions in serial for two reasons. Firstly we diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 654f35c..9274f57 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -1,6 +1,8 @@ name: Run tests on: pull_request: +permissions: + contents: read env: UBUNTU_PRO_TOKEN: ${{ secrets.UBUNTU_PRO_TOKEN }} jobs: diff --git a/Dockerfile b/Dockerfile index 1c3ddb3..876eaa5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,10 +35,13 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ RUN python3 -m venv /opt/venv # "activate" the venv ENV VIRTUAL_ENV=/opt/venv/ PATH="/opt/venv/bin:$PATH" -# We ensure up-to-date build tools (which why we ignore DL3013) -# hadolint ignore=DL3013,DL3042 -RUN --mount=type=cache,target=/root/.cache python -m pip install -U pip setuptools wheel pip-tools - +# Ensure recent build tools +# Pin setuptools to <82.0.0 (which removed pkg_resources, which some dependencies require) +# Pin others to next major/minor version as of 2026-03-05 (major/minor depending on where +# the package tends to include breaking changes) +# hadolint ignore=DL3042 +RUN --mount=type=cache,target=/root/.cache python -m pip install -U "pip<27.0.0" "wheel<0.47.0" "pip-tools<7.6.0" "setuptools<82.0.0" +RUN pip freeze ################################################# # diff --git a/tests/test_import.py b/tests/test_import.py index f877650..0f22406 100644 --- a/tests/test_import.py +++ b/tests/test_import.py @@ -1,11 +1,12 @@ import os -import subprocess +import re + from importlib import import_module from pathlib import Path -import re +from importlib.metadata import distribution + import pytest -from pkg_resources import Requirement, get_provider # packages that have no way to detect their importable name @@ -15,21 +16,30 @@ "qtpy": None, # required dependency of jupyter-lab } + def get_module_names(pkg_name): - """Load pkg metadata to find out its importable module name(s).""" + """Load distribution to find out its importable module name(s).""" # remove any extras - pkg_name = re.sub(r'\[.*\]', '', pkg_name) + pkg_name = re.sub(r"\[.*\]", "", pkg_name) modules = set() - provider = get_provider(Requirement.parse(pkg_name)) # top level package name is typically all we need + dist = distribution(pkg_name) if pkg_name in BAD_PACKAGES: name = BAD_PACKAGES[pkg_name] - if name is None: # unimportably package + if name is None: # unimportable package return [] modules.add(BAD_PACKAGES[pkg_name]) - elif provider.has_metadata("top_level.txt"): - first_line = list(provider.get_metadata_lines("top_level.txt"))[0] - modules.add(first_line) + elif top_level_names := dist.read_text("top_level.txt"): + # Find the first non-_ prefixed module + if first_public_name := next( + ( + name + for name in top_level_names.strip().split("\n") + if not name.startswith("_") + ), + None, + ): + modules.add(first_public_name) else: # badly packaged dependency, make an educated guess name = pkg_name @@ -37,11 +47,11 @@ def get_module_names(pkg_name): name = pkg_name[:-5] elif pkg_name.endswith("-py"): name = pkg_name[:-3] - + modules.add(name.replace("-", "_")) - if provider.has_metadata("namespace_packages.txt"): - modules |= set(provider.get_metadata_lines("namespace_packages.txt")) + if namespace_packages := dist.read_text("namespace_packages.txt"): + modules |= set(namespace_packages.strip().split("\n")) # _ prefixed modules are typically C modules and not directly importable return [n for n in modules if n[0] != "_"]