From 9ac645218776c85ba1eb362306878ddd09585a3b Mon Sep 17 00:00:00 2001 From: Zafar Date: Tue, 29 Sep 2026 10:35:15 -0700 Subject: [PATCH] Automatic release workflow (draft) --- .github/workflows/release.yml | 4 ++-- CONTRIBUTING.md | 2 +- tests/test_release.py | 9 +++++---- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2e7d135..cde7914 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -60,14 +60,14 @@ jobs: --repo "$GITHUB_REPOSITORY" \ --signer-workflow "$GITHUB_REPOSITORY/.github/workflows/release.yml" \ --source-digest "$GITHUB_SHA" --source-ref "$GITHUB_REF" - - name: Publish release assets + - name: Create draft release with verified assets shell: bash env: GH_TOKEN: ${{ github.token }} ATTESTATION_BUNDLE: ${{ steps.attest.outputs.bundle-path }} IS_PRERELEASE: ${{ steps.archive.outputs.prerelease }} run: | - release_flags=(--verify-tag --generate-notes) + release_flags=(--draft --verify-tag --generate-notes) case "$IS_PRERELEASE" in true) release_flags+=(--prerelease --latest=false) ;; false) ;; diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 58c7b3a..b7863e7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,7 +31,7 @@ Use `uv lock --upgrade-package NAME` for a deliberate upgrade. Update the `maida ## Tagged release provenance -The release workflow tests the full stable or release-candidate tag, archives its committed source, and publishes `maida-assert.tar.gz`, `SHA256SUMS`, and `provenance.jsonl`. It verifies the attestation against the workflow, source commit and tag before publication. Release candidates use the same checks and provenance as stable releases, with GitHub's prerelease flag and `latest=false`. Protect release tags and require review of the release workflow; do not overwrite full tags or existing releases. +The release workflow tests the full stable or release-candidate tag, archives its committed source, and creates a draft GitHub release containing `maida-assert.tar.gz`, `SHA256SUMS`, and `provenance.jsonl`. It verifies the attestation against the workflow, source commit and tag before creating the draft. Both stable and release-candidate tags remain drafts until you review the tag, notes, assets and provenance and manually publish the release; immutable release protection takes effect on publication. Release candidates use the same checks and provenance as stable releases, with GitHub's prerelease flag and `latest=false`. Protect release tags and require review of the release workflow; do not overwrite full tags or existing releases. To verify an archive, set its reviewed tag and commit, then run in an empty directory: diff --git a/tests/test_release.py b/tests/test_release.py index 5f3ed52..0e3dcab 100644 --- a/tests/test_release.py +++ b/tests/test_release.py @@ -73,7 +73,7 @@ def git(*args): assert archive.read_bytes() == contents -def test_release_attests_and_verifies_before_publishing(): +def test_release_attests_and_verifies_before_creating_draft(): workflow = yaml.safe_load((ROOT / ".github/workflows/release.yml").read_text()) assert workflow.get("on", workflow.get(True)) == {"push": {"tags": ["v*.*.*"]}} assert workflow["permissions"] == {"contents": "read"} @@ -86,7 +86,7 @@ def test_release_attests_and_verifies_before_publishing(): build = next(i for i, s in enumerate(steps) if s.get("id") == "archive") attest = next(i for i, s in enumerate(steps) if s.get("id") == "attest") verify = next(i for i, s in enumerate(steps) if s.get("name") == "Verify provenance") - publish = next(i for i, s in enumerate(steps) if s.get("name") == "Publish release assets") + publish = next(i for i, s in enumerate(steps) if s.get("name") == "Create draft release with verified assets") assert build < attest < verify < publish assert steps[attest]["with"]["subject-path"] == "${{ runner.temp }}/release/maida-assert.tar.gz" assert "--source-digest" in steps[verify]["run"] @@ -98,9 +98,9 @@ def test_release_attests_and_verifies_before_publishing(): @pytest.mark.parametrize("classification", ["true", "false", "", "invalid"]) -def test_publish_marks_only_release_candidates_as_prereleases(tmp_path, classification): +def test_draft_marks_only_release_candidates_as_prereleases(tmp_path, classification): workflow = yaml.safe_load((ROOT / ".github/workflows/release.yml").read_text()) - step = next(s for s in workflow["jobs"]["release"]["steps"] if s.get("name") == "Publish release assets") + step = next(s for s in workflow["jobs"]["release"]["steps"] if s.get("name") == "Create draft release with verified assets") release = tmp_path / "release" release.mkdir() archive = release / "maida-assert.tar.gz" @@ -134,6 +134,7 @@ def test_publish_marks_only_release_candidates_as_prereleases(tmp_path, classifi args = json.loads(arguments.read_text()) assert args[:3] == ["release", "create", tag] assert "--verify-tag" in args + assert "--draft" in args assert ("--prerelease" in args) is (classification == "true") assert ("--latest=false" in args) is (classification == "true") assert args[-3:] == ["maida-assert.tar.gz", "SHA256SUMS", "provenance.jsonl"]