Skip to content

chore: sync release retry fix back to dev - #47

Open
HsiangNianian wants to merge 7 commits into
devfrom
main
Open

chore: sync release retry fix back to dev#47
HsiangNianian wants to merge 7 commits into
devfrom
main

Conversation

@HsiangNianian

@HsiangNianian HsiangNianian commented Jul 30, 2026

Copy link
Copy Markdown
Member

Summary

  • sync the fail-closed GitHub Release retry verification from main
  • keep the PyPI recovery path available in future release branches

Validation

  • release workflow contract tests
  • Ruff lint and format checks
  • workflow shell syntax check
  • strict Sphinx HTML build

Related to #34.

Summary by Sourcery

Make the release workflow resilient to GitHub Release retries by supporting verification of an existing immutable release instead of failing when it already exists.

Enhancements:

  • Update the changelog workflow to verify that an existing GitHub Release exactly matches the sealed release candidate artifacts and metadata before proceeding.

Tests:

  • Extend release workflow contract tests to assert the retry path verifies an existing GitHub Release without editing or clobbering it.

feat: establish production baseline and v2 specification
Verify an existing GitHub Release and its sealed artifacts before retrying PyPI publication.
@HsiangNianian
HsiangNianian requested a review from fu050409 as a code owner July 30, 2026 17:20
@sourcery-ai

sourcery-ai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Reviewer's Guide

This PR updates the release workflow to fail closed when retrying a publication: existing GitHub Releases are now strictly verified (rather than recreated or edited) and a contract test is added to assert the new behavior, while keeping the PyPI trusted publishing path intact for future branches.

Sequence diagram for GitHub Release retry verification in changelog workflow

sequenceDiagram
    participant Workflow
    participant gh_cli
    participant GitHub_API

    Workflow->>gh_cli: gh release view TAG --repo GITHUB_REPOSITORY
    alt [release exists]
        Workflow->>gh_cli: gh release download TAG --dir existing_release_dir
        Workflow->>gh_cli: gh api repos/GITHUB_REPOSITORY/releases/tags/TAG
        gh_cli->>Workflow: release_json
        Workflow->>gh_cli: jq (validate tag_name, draft, prerelease)
        Workflow->>gh_cli: diff -u (expected_files, existing_files)
        Workflow->>gh_cli: cmp release-candidate/SHA256SUMS existing_release_dir/SHA256SUMS
        Workflow->>gh_cli: sha256sum --check SHA256SUMS
        gh_cli->>Workflow: verification_success
        Workflow-->>Workflow: [Release matches sealed candidate]
    else [release does not exist]
        Workflow->>gh_cli: gh release create TAG (release_args)
        gh_cli->>GitHub_API: create release with assets and notes
        GitHub_API-->>gh_cli: release created
        gh_cli-->>Workflow: create_success
    end
Loading

File-Level Changes

Change Details Files
Harden GitHub Release retry behavior by verifying an existing immutable release instead of failing or mutating it.
  • Rename the release step to reflect verification of an existing immutable GitHub Release.
  • When a release already exists, download its assets into a temporary directory and inspect them.
  • Use the GitHub API and jq to assert tag name, non-draft status, and prerelease flag match the candidate.
  • Compare package filenames between the sealed candidate and the existing release with diff.
  • Compare SHA256SUMS file contents with cmp and then validate artifact checksums via sha256sum --check.
  • If no prior release exists, construct release create arguments conditionally on prerelease/latest and invoke gh with those arguments.
.github/workflows/changelog.yml
Add a workflow contract test to lock in the new GitHub Release verification behavior on retries.
  • Load the changelog workflow and extract shell commands for analysis.
  • Assert presence of gh release download, sha256sum --check SHA256SUMS, diff -u, and release-candidate/packages in the workflow.
  • Assert absence of any release edit or --clobber usage to ensure releases remain immutable on retry.
tests/test_release_workflow.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • The inline shell in the release step has grown quite complex; consider extracting the verification logic into a dedicated script file to improve readability and make local iteration/testing easier.
  • The new verification path depends on tools like jq, sha256sum, diff, and cmp; it may be worth adding an explicit check or setup step so failures due to missing tooling are surfaced with a clear error message.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The inline shell in the release step has grown quite complex; consider extracting the verification logic into a dedicated script file to improve readability and make local iteration/testing easier.
- The new verification path depends on tools like `jq`, `sha256sum`, `diff`, and `cmp`; it may be worth adding an explicit check or setup step so failures due to missing tooling are surfaced with a clear error message.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Replace the personal docs deployment token with a job-scoped repository token and cover it with a workflow contract test.
@HsiangNianian

Copy link
Copy Markdown
Member Author

All 37 checks are green. @fu050409, please approve this back-merge when convenient; dev requires one CODEOWNER approval and approval of the latest push.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New security issues found

Comment thread package-lock.json
Comment on lines +648 to +664
"node_modules/@img/sharp-libvips-darwin-arm64": {
"version": "1.3.1",
"resolved": "https://registry.npmmirror.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.1.tgz",
"integrity": "sha512-4V/M3roRMTYjiwZY9IOVQOE8OyeCxFAkYmyZDrZl51uOKjibm3oeEJ4WAmLxutAfzFbC9jqUiPs2gbnGflH+7g==",
"cpu": [
"arm64"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"darwin"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (license/@img/sharp-libvips-darwin-arm64): LGPL-3.0-or-later: Open-source license can require releasing the entire application source

This LGPL-3.0-or-later open-source license can impose strong copyleft or non-commercial obligations that may require releasing your full application source code or restrict commercial use, depending on how the code is used or distributed

Source: trivy

Comment thread package-lock.json
Comment on lines +665 to +681
"node_modules/@img/sharp-libvips-darwin-x64": {
"version": "1.3.1",
"resolved": "https://registry.npmmirror.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.1.tgz",
"integrity": "sha512-c0/DxItpJv2+dGhgycJBBgotdqruGYDvA79drdh0MD1dFpy7JzJ/PlXwi1H4rFf0eTy8tgbI91aHDnZIceY3jQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"darwin"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (license/@img/sharp-libvips-darwin-x64): LGPL-3.0-or-later: Open-source license can require releasing the entire application source

This LGPL-3.0-or-later open-source license can impose strong copyleft or non-commercial obligations that may require releasing your full application source code or restrict commercial use, depending on how the code is used or distributed

Source: trivy

Comment thread package-lock.json
Comment on lines +682 to +698
"node_modules/@img/sharp-libvips-linux-arm": {
"version": "1.3.1",
"resolved": "https://registry.npmmirror.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.1.tgz",
"integrity": "sha512-aGGy9aWzXgHBG7HNyQPWorZthlp7+x6fDRoPAQbGO3ThcttuTyKIx3NuSHb6zb4gBNq6/yNn9f1cy9nFKS/Vmg==",
"cpu": [
"arm"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"linux"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (license/@img/sharp-libvips-linux-arm): LGPL-3.0-or-later: Open-source license can require releasing the entire application source

This LGPL-3.0-or-later open-source license can impose strong copyleft or non-commercial obligations that may require releasing your full application source code or restrict commercial use, depending on how the code is used or distributed

Source: trivy

Comment thread package-lock.json
Comment on lines +699 to +715
"node_modules/@img/sharp-libvips-linux-arm64": {
"version": "1.3.1",
"resolved": "https://registry.npmmirror.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.1.tgz",
"integrity": "sha512-JznefmcK9j1JKPz8AkQDh89kjojubyfOasWBPKfzMIhPwsgDy9evpE/naJTXXXmghS1iFwR8u/kTwh/I2/+GCw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"linux"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (license/@img/sharp-libvips-linux-arm64): LGPL-3.0-or-later: Open-source license can require releasing the entire application source

This LGPL-3.0-or-later open-source license can impose strong copyleft or non-commercial obligations that may require releasing your full application source code or restrict commercial use, depending on how the code is used or distributed

Source: trivy

Comment thread package-lock.json
Comment on lines +716 to +732
"node_modules/@img/sharp-libvips-linux-ppc64": {
"version": "1.3.1",
"resolved": "https://registry.npmmirror.com/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.1.tgz",
"integrity": "sha512-1EkwGNCZk6iWNCMWqrvdJ+r1j0PT1zIz60CNPhYnJlK/zyeWqlsPZIe+ocBVqPF8k/Ssee/NCk+tE9Ryrko6ng==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"linux"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (license/@img/sharp-libvips-linux-ppc64): LGPL-3.0-or-later: Open-source license can require releasing the entire application source

This LGPL-3.0-or-later open-source license can impose strong copyleft or non-commercial obligations that may require releasing your full application source code or restrict commercial use, depending on how the code is used or distributed

Source: trivy

Comment thread package-lock.json
Comment on lines +801 to +817
"node_modules/@img/sharp-libvips-linuxmusl-x64": {
"version": "1.3.1",
"resolved": "https://registry.npmmirror.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.1.tgz",
"integrity": "sha512-yO21HwoUVLN8Qa+/SBjQLMYwBWAVJjeGPNe+hc0OUeMeifEtJqu5a1c4HayE1nNpDih9y3/KkoltfkDodmKAlg==",
"cpu": [
"x64"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"linux"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (license/@img/sharp-libvips-linuxmusl-x64): LGPL-3.0-or-later: Open-source license can require releasing the entire application source

This LGPL-3.0-or-later open-source license can impose strong copyleft or non-commercial obligations that may require releasing your full application source code or restrict commercial use, depending on how the code is used or distributed

Source: trivy

Comment thread package-lock.json
Comment on lines +1002 to +1018
"node_modules/@img/sharp-wasm32": {
"version": "0.35.2",
"resolved": "https://registry.npmmirror.com/@img/sharp-wasm32/-/sharp-wasm32-0.35.2.tgz",
"integrity": "sha512-Mrv4JQNYVQ94xH+jzZ9r+gowleN8mv2FTgKT+PI6bx5C0G8TdNYndu161pg2i7uoBwxy2ImPMHrJOM2LZef7Bw==",
"dev": true,
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
"optional": true,
"dependencies": {
"@emnapi/runtime": "^1.11.1"
},
"engines": {
"node": ">=20.9.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
}
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (license/@img/sharp-wasm32): Apache-2.0 AND LGPL-3.0-or-later AND MIT: Open-source license can require releasing the entire application source

This Apache-2.0 AND LGPL-3.0-or-later AND MIT open-source license can impose strong copyleft or non-commercial obligations that may require releasing your full application source code or restrict commercial use, depending on how the code is used or distributed

Source: trivy

Comment thread package-lock.json
Comment on lines +1039 to +1058
"node_modules/@img/sharp-win32-arm64": {
"version": "0.35.2",
"resolved": "https://registry.npmmirror.com/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.2.tgz",
"integrity": "sha512-BiVRYc/t6/Vl3e1hBx0hugG4oN9Pydf4fgMSpxTQJmwGUg/YoXTWHiFeRymHfCZzifxu4F4rpk/I67D0LQ20wQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "Apache-2.0 AND LGPL-3.0-or-later",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=20.9.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
}
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (license/@img/sharp-win32-arm64): Apache-2.0 AND LGPL-3.0-or-later: Open-source license can require releasing the entire application source

This Apache-2.0 AND LGPL-3.0-or-later open-source license can impose strong copyleft or non-commercial obligations that may require releasing your full application source code or restrict commercial use, depending on how the code is used or distributed

Source: trivy

Comment thread package-lock.json
Comment on lines +1059 to +1078
"node_modules/@img/sharp-win32-ia32": {
"version": "0.35.2",
"resolved": "https://registry.npmmirror.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.2.tgz",
"integrity": "sha512-YYEhx9PImCC7T0tI8JDMi4DB9LwLCXCU5OWNYEXAxh5Q1ShKkyC6byxzoBJ3gEFDnH2lQckWuDe70G7mB2XJog==",
"cpu": [
"ia32"
],
"dev": true,
"license": "Apache-2.0 AND LGPL-3.0-or-later",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": "^20.9.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
}
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (license/@img/sharp-win32-ia32): Apache-2.0 AND LGPL-3.0-or-later: Open-source license can require releasing the entire application source

This Apache-2.0 AND LGPL-3.0-or-later open-source license can impose strong copyleft or non-commercial obligations that may require releasing your full application source code or restrict commercial use, depending on how the code is used or distributed

Source: trivy

Comment thread package-lock.json
Comment on lines +1079 to +1098
"node_modules/@img/sharp-win32-x64": {
"version": "0.35.2",
"resolved": "https://registry.npmmirror.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.2.tgz",
"integrity": "sha512-imoOyBcoM/iiUr4J6VPpCNjPnjvP/Gks95898yB8YqoGGYmHYbOyCuNv9FMhFgtaiHFGbHW8bxKqRV6VjtXThQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "Apache-2.0 AND LGPL-3.0-or-later",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=20.9.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
}
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (license/@img/sharp-win32-x64): Apache-2.0 AND LGPL-3.0-or-later: Open-source license can require releasing the entire application source

This Apache-2.0 AND LGPL-3.0-or-later open-source license can impose strong copyleft or non-commercial obligations that may require releasing your full application source code or restrict commercial use, depending on how the code is used or distributed

Source: trivy

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant