From 92f9c4e35061d76844f0dd301c068933fb4d5505 Mon Sep 17 00:00:00 2001 From: Becky Smith Date: Wed, 4 Mar 2026 16:04:59 +0000 Subject: [PATCH 1/4] Restrict GITHUB_TOKEN permissions in workflows Ensure workflows have the minimum required permissions and silence the security alerts --- .github/workflows/build_and_publish.yaml | 3 +++ .github/workflows/tests.yaml | 2 ++ 2 files changed, 5 insertions(+) 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: From 61e9fd3118eca8e82de1c80b61a19f1ccc759333 Mon Sep 17 00:00:00 2001 From: Becky Smith Date: Wed, 4 Mar 2026 16:06:20 +0000 Subject: [PATCH 2/4] Remove dependabot automerge workflow We've removed this in most other repos and generally concluded it's a bad idea. It allows merging of PRs with broken builds, e.g. https://github.com/opensafely-core/python-docker/pull/119 --- .github/workflows/automerge.yaml | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 .github/workflows/automerge.yaml 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}} From efde8e5bf2071ac09f4bd1d09e91d4fde3887484 Mon Sep 17 00:00:00 2001 From: Becky Smith Date: Thu, 5 Mar 2026 09:06:03 +0000 Subject: [PATCH 3/4] Replace deprecated pkg_resources and pin setuptools setuptools v82.0.0 removed the deprecated pkg_resources. Our Dockerfile installs the most recent version of build tools, including setuptools, which breaks tests. We can fix the tests to use importlib instead of pkg_resources. However, we still can't just install the latest setuptools, because various packages also depend on a version that still includes pkg_resources, so we pin it to <82.0.0. --- Dockerfile | 3 ++- tests/test_import.py | 36 +++++++++++++++++++++++------------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/Dockerfile b/Dockerfile index 1c3ddb3..ca9fcd3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -36,8 +36,9 @@ 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) +# Pin setuptools to <82.0.0 (which removed pkg_resources, which some dependencies require) # hadolint ignore=DL3013,DL3042 -RUN --mount=type=cache,target=/root/.cache python -m pip install -U pip setuptools wheel pip-tools +RUN --mount=type=cache,target=/root/.cache python -m pip install -U pip wheel pip-tools "setuptools<82.0.0" ################################################# 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] != "_"] From 7cfd323d3e7da49153c186cd87bc71bbd0fbc513 Mon Sep 17 00:00:00 2001 From: Becky Smith Date: Thu, 5 Mar 2026 11:37:01 +0000 Subject: [PATCH 4/4] Pin build tools in Dockerfile to avoid breaking changes --- Dockerfile | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index ca9fcd3..876eaa5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,11 +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) +# Ensure recent build tools # Pin setuptools to <82.0.0 (which removed pkg_resources, which some dependencies require) -# hadolint ignore=DL3013,DL3042 -RUN --mount=type=cache,target=/root/.cache python -m pip install -U pip wheel pip-tools "setuptools<82.0.0" - +# 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 ################################################# #