From 4e50b9af14cd2cf057247ca958bce193e2b32f9b Mon Sep 17 00:00:00 2001 From: Ramesh Padmanabhaiah <22363102+codeforester@users.noreply.github.com> Date: Wed, 28 Oct 2026 00:15:59 +0530 Subject: [PATCH 1/2] ci: pin PyPI publisher action by commit --- .github/workflows/package.yml | 4 ++-- tests/test_package_workflow.py | 14 +++++++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index d47ddb7..77785c8 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -336,14 +336,14 @@ jobs: - name: Publish to TestPyPI if: ${{ github.event_name == 'workflow_dispatch' && inputs.publish_target == 'testpypi' }} - uses: pypa/gh-action-pypi-publish@v1.14.2 # v1.14.2 image tag is required by this composite action + uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # v1.14.3 with: packages-dir: dist repository-url: https://test.pypi.org/legacy/ - name: Publish to PyPI if: ${{ (github.event_name == 'push' && github.ref_type == 'tag') || (github.event_name == 'workflow_dispatch' && inputs.publish_target == 'pypi') }} - uses: pypa/gh-action-pypi-publish@v1.14.2 # v1.14.2 image tag is required by this composite action + uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # v1.14.3 with: packages-dir: dist diff --git a/tests/test_package_workflow.py b/tests/test_package_workflow.py index 38dd089..db3381b 100644 --- a/tests/test_package_workflow.py +++ b/tests/test_package_workflow.py @@ -1,5 +1,6 @@ from __future__ import annotations +import re from pathlib import Path import yaml @@ -24,11 +25,18 @@ def test_package_workflow_can_recover_an_immutable_tag_from_default_branch() -> assert '--source-commit "$SOURCE_COMMIT"' in workflow -def test_package_workflow_uses_a_published_pypi_publisher_image_tag() -> None: +def test_package_workflow_uses_the_pinned_pypi_publisher_release() -> None: workflow = (Path(__file__).resolve().parents[1] / ".github/workflows/package.yml").read_text(encoding="utf-8") - assert workflow.count("uses: pypa/gh-action-pypi-publish@v1.14.2") == 2 - assert "pypa/gh-action-pypi-publish@a892a5a61159132606e93a2fa6f4358831b04d26" not in workflow + assert workflow.count("uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086") == 2 + + +def test_package_workflow_pins_every_third_party_action_to_a_full_sha() -> None: + workflow = (Path(__file__).resolve().parents[1] / ".github/workflows/package.yml").read_text(encoding="utf-8") + refs = re.findall(r"^\s*-?\s*uses:\s+[^@\s]+@([^\s#]+)", workflow, flags=re.MULTILINE) + + assert refs + assert all(re.fullmatch(r"[0-9a-f]{40}", ref) for ref in refs), refs def test_package_workflow_does_not_replace_published_release_assets() -> None: From f317ad82d41429207a0ec18e09b53866e76cb77a Mon Sep 17 00:00:00 2001 From: Ramesh Padmanabhaiah <22363102+codeforester@users.noreply.github.com> Date: Wed, 28 Oct 2026 07:39:56 +0530 Subject: [PATCH 2/2] test: cover action pins across all workflows --- tests/test_package_workflow.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/tests/test_package_workflow.py b/tests/test_package_workflow.py index db3381b..959c048 100644 --- a/tests/test_package_workflow.py +++ b/tests/test_package_workflow.py @@ -31,12 +31,18 @@ def test_package_workflow_uses_the_pinned_pypi_publisher_release() -> None: assert workflow.count("uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086") == 2 -def test_package_workflow_pins_every_third_party_action_to_a_full_sha() -> None: - workflow = (Path(__file__).resolve().parents[1] / ".github/workflows/package.yml").read_text(encoding="utf-8") - refs = re.findall(r"^\s*-?\s*uses:\s+[^@\s]+@([^\s#]+)", workflow, flags=re.MULTILINE) - - assert refs - assert all(re.fullmatch(r"[0-9a-f]{40}", ref) for ref in refs), refs +def test_all_workflows_pin_every_third_party_action_to_a_full_sha() -> None: + workflow_dir = Path(__file__).resolve().parents[1] / ".github/workflows" + workflows = sorted(workflow_dir.glob("*.yml")) + assert workflows + + unpinned: list[tuple[str, str]] = [] + for workflow_path in workflows: + workflow = workflow_path.read_text(encoding="utf-8") + refs = re.findall(r"^\s*-?\s*uses:\s+[^@\s]+@([^\s#]+)", workflow, flags=re.MULTILINE) + unpinned.extend((workflow_path.name, ref) for ref in refs if not re.fullmatch(r"[0-9a-f]{40}", ref)) + + assert unpinned == [] def test_package_workflow_does_not_replace_published_release_assets() -> None: