Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ jobs:
matrix:
python-version: ["3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ matrix.python-version }}
cache: pip
Expand Down
20 changes: 10 additions & 10 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ jobs:
name: Build + validate (dry run, no publish)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.12"
- uses: actions/setup-node@v7
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "20"
- name: Install tooling
Expand Down Expand Up @@ -91,13 +91,13 @@ jobs:
sys.exit(1 if errors else 0)
PY
- name: Upload built distributions
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: dist
path: dist/
if-no-files-found: error
- name: Upload local MCPB
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: mcpb
path: mcpb-dist/
Expand All @@ -113,7 +113,7 @@ jobs:
id-token: write # OIDC for PyPI Trusted Publishing
contents: read
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Resolve release version + assert it matches the package
id: ver
shell: bash
Expand All @@ -134,7 +134,7 @@ jobs:
exit 1
fi
- name: Download built distributions
uses: actions/download-artifact@v8
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: dist
path: dist/
Expand All @@ -151,13 +151,13 @@ jobs:
fi
- name: Publish to PyPI (OIDC Trusted Publishing)
if: ${{ vars.PYPI_PUBLISH_METHOD != 'token' }}
uses: pypa/gh-action-pypi-publish@release/v1
uses: pypa/gh-action-pypi-publish@ba38be9e461d3875417946c167d0b5f3d385a247 # v1.14.1
with:
# No password -> the action uses OIDC Trusted Publishing.
print-hash: true
- name: Publish to PyPI (API token fallback)
if: ${{ vars.PYPI_PUBLISH_METHOD == 'token' }}
uses: pypa/gh-action-pypi-publish@release/v1
uses: pypa/gh-action-pypi-publish@ba38be9e461d3875417946c167d0b5f3d385a247 # v1.14.1
with:
password: ${{ secrets.PYPI_API_TOKEN }}
print-hash: true
Expand All @@ -171,7 +171,7 @@ jobs:
id-token: write # OIDC for `mcp-publisher login github-oidc`
contents: read
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Resolve release version
id: ver
shell: bash
Expand Down
43 changes: 43 additions & 0 deletions tests/test_distribution.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
PYPROJECT = REPO_ROOT / "pyproject.toml"
README = REPO_ROOT / "README.md"
RELEASE_WORKFLOW = REPO_ROOT / ".github" / "workflows" / "release.yml"
WORKFLOWS_DIR = REPO_ROOT / ".github" / "workflows"

# `uses: owner/repo@<revision>` with the trailing ` # vX.Y.Z` comment stripped.
USES_REVISION = re.compile(r"(?m)^\s*(?:-\s+)?uses:\s+(\S+)@([^\s#]+)")

REVERSE_DNS_NAME = "io.github.OpenAdaptAI/openadapt-agent"
PYPI_NAME = "openadapt-agent"
Expand Down Expand Up @@ -82,6 +86,45 @@ def test_release_workflow_runs_the_complete_archive_boundary() -> None:
assert "python scripts/check_dist.py dist/*" in workflow


def test_release_actions_are_pinned_to_commits() -> None:
"""Every action on the publish path must be a full 40-char commit SHA.

A floating ref (``@v7``, or worse ``@release/v1``, which is a *branch*)
means whoever controls that ref decides what runs with this repo's PyPI
Trusted Publishing OIDC identity, at a moment nobody is watching. Ported
from openadapt-tray's test of the same name.
"""
workflow = RELEASE_WORKFLOW.read_text(encoding="utf-8")
revisions = [m.group(2) for m in USES_REVISION.finditer(workflow)]
assert revisions
unpinned = [rev for rev in revisions if not re.fullmatch(r"[0-9a-f]{40}", rev)]
assert not unpinned, f"release.yml uses unpinned action revisions: {unpinned}"


def test_every_workflow_action_is_pinned_to_a_commit() -> None:
"""The same rule for the rest of the repo, so release.yml stays the norm.

Each pin also carries a trailing ``# vX.Y.Z`` comment so the human-readable
version is never lost to the SHA.
"""
unpinned: list[str] = []
uncommented: list[str] = []
for workflow_file in sorted(WORKFLOWS_DIR.glob("*.yml")):
text = workflow_file.read_text(encoding="utf-8")
for line in text.splitlines():
match = USES_REVISION.match(line)
if not match:
continue
action, revision = match.group(1), match.group(2)
where = f"{workflow_file.name}: {action}@{revision}"
if not re.fullmatch(r"[0-9a-f]{40}", revision):
unpinned.append(where)
elif not re.search(r"#\s*\S", line.split(revision, 1)[1]):
uncommented.append(where)
assert not unpinned, f"unpinned action revisions: {unpinned}"
assert not uncommented, f"pinned actions missing a version comment: {uncommented}"


def test_serve_is_the_subcommand_and_bundles_is_required() -> None:
args = _server_json()["packages"][0]["packageArguments"]
positional = [a for a in args if a["type"] == "positional"]
Expand Down