Skip to content

Automate miner release versioning - #523

Open
n13 wants to merge 2 commits into
mainfrom
ci/automated-miner-releases
Open

Automate miner release versioning#523
n13 wants to merge 2 commits into
mainfrom
ci/automated-miner-releases

Conversation

@n13

@n13 n13 commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

Summary

Replaces the manual version-entry miner release with the same automated flow as quantus-cli / chain / quantus-miner:

  • create_miner_release_proposal.yml (new): dispatch with patch/minor/major/custom. Computes the next version from the latest miner-v* tag, bumps miner-app/pubspec.yaml, and opens a PR labeled miner-release-proposal (labels auto-created on first run).
  • publish_miner_release.yml (new): when that PR merges — verifies pubspec matches, tags the merge commit miner-vX.Y.Z, builds all platforms at the tag, and publishes the GitHub release (gh release create, with --notes-start-tag pinned to the previous miner tag so generated notes don't diff against wallet-v* tags).
  • create_miner_build.yml: now build-only and reusable (workflow_call with a ref input); manual dispatch still available for test builds. The old in-workflow release job (deprecated actions/create-release@v1) is removed.

Notes

  • The proposal uses the PAT_TOKEN secret for gh pr create so CI triggers on the release PR (same as ADMIN_PAT in the other repos). If it has expired, the proposal job fails at PR creation and the secret needs rotating.
  • main is already at pubspec 0.5.0 (manual bump) while the latest tag is miner-v0.4.5, so the proposal commit allows an empty diff; for the first run choose minor to release miner-v0.5.0.

Test plan

  • actionlint clean (one pre-existing info-level note in the untouched Linux tar step)
  • Version bump math, pubspec sed, and previous-tag lookup verified locally against the real tag list
  • After merge: run the proposal workflow with minor, merge the bump PR, confirm tag + 3-platform release

Release proposal workflow computes the next miner-v version
(patch/minor/major/custom) from tags and opens a version-bump PR;
merging it tags, builds all platforms, and publishes the release.

@dewabisma dewabisma left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Have you tested the CI workflows? It can be tricky to make CI works without actually run and see it works.

@n13

n13 commented Jun 14, 2026

Copy link
Copy Markdown
Collaborator Author

Yes thats why i did not merge yet ;)

@n13

n13 commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator Author

🔍 Review — Automate miner release versioning (CI)

Verdict: 🟡 Approve with non-blocking comments

The design is sound: the release-firing guard is correct (only a merged PR carrying the miner-release-proposal label can tag), the workflow uses pull_request rather than pull_request_target, and untrusted inputs are passed via env vars — so there is no injection or "any-merged-PR-tags-a-release" bug. The remaining items are robustness/idempotency concerns, and the whole post-merge tag→build→release path is explicitly untested in the PR's own test plan.

What it does

  • create_miner_build.yml: converted to build-only + reusable (workflow_call with a ref input), keeping workflow_dispatch for test builds; drops contents: write to contents: read; removes the deprecated actions/create-release@v1 / upload-release-asset@v1 release job.
  • create_miner_release_proposal.yml (new): workflow_dispatch (patch/minor/major/custom) → computes next version from the latest miner-v* tag → seds miner-app/pubspec.yaml → pushes a release/miner-vX.Y.Z branch → opens a PR (via PAT_TOKEN) labeled automated,miner-release-proposal (+draft-release if requested); labels auto-created with gh label create --force.
  • publish_miner_release.yml (new): on merged PR to main labeled miner-release-proposal → extract version from PR title, verify pubspec matches, tag merge_commit_sha, call the reusable build, then gh release create with assets and --notes-start-tag pinned to the previous miner tag.

Strengths

  • Release-firing guard is correct (publish_miner_release.yml:15): github.event.pull_request.merged == true && contains(labels.*.name, 'miner-release-proposal'). When create-tag is skipped, the build and create-release jobs (needs: it) are skipped too, so no unlabeled/unmerged PR can produce a tag or release.
  • Correctly uses pull_request not pull_request_target (publish_miner_release.yml:4). Proposal PRs come from same-repo branches so secrets/write-token are available; forks would get neither, failing safe.
  • No script injection: PR_TITLE (:32), LATEST_TAG/CUSTOM_VERSION/VERSION_TYPE (create_miner_release_proposal.yml:63-66), NEW_VERSION/TARGET_BRANCH (:117-120) are passed via env and quoted, not interpolated into the script body.
  • pubspec-vs-tag verification (publish_miner_release.yml:48-57) is a good safety net if the PR title is edited.
  • Mostly least-privilege: top-level contents: read on publish and build; contents: write/pull-requests: write scoped per job.
  • --notes-start-tag correctly pinned to the previous miner-v* tag with the first-tag edge handled (publish_miner_release.yml:118-121), so generated notes don't diff against wallet-v*.
  • PAT_TOKEN used for gh pr create so the proposal PR actually triggers CI (a GITHUB_TOKEN-created PR would not).

Findings

  1. [non-blocking — top verification gap] create-tag checks out the default PR ref but operates on merge_commit_sha, which may not be fetched. publish_miner_release.yml:24-27 checks out the default pull_request ref (refs/pull/N/merge) with fetch-depth: 0, then :53 runs git show "$MERGE_COMMIT:miner-app/pubspec.yaml" and :66 runs git tag -a "$TAG" "$MERGE_COMMIT". merge_commit_sha (the real merge on main) is a sibling of the test-merge commit and is not in that ref's history, so these can fail with "invalid object name". This is exactly the post-merge step left unchecked in the test plan. Safer: actions/checkout with ref: ${{ github.event.pull_request.merge_commit_sha }} (or explicitly git fetch origin main). Must be validated on a throwaway version before relying on it.

  2. [non-blocking] Version source-of-truth is the latest tag, not pubspec — "patch" can silently downgrade pubspec. create_miner_release_proposal.yml:61-100 derives the next version purely from the latest miner-v* tag, then :135 seds pubspec to that value unconditionally. If pubspec is ahead of the latest tag (the exact situation the PR describes), a patch bump writes a lower version than what's in pubspec. The grep check at :137 only confirms the sed applied, not that the version moved forward. Consider taking max(tag, pubspec) as the base, or refusing a bump that lowers pubspec.

  3. [non-blocking] Proposal's own duplicate-tag guard is likely ineffective — update-version doesn't fetch tags. create_miner_release_proposal.yml:112-113 sets fetch-depth: 0 but omits fetch-tags: true (which calculate-next-version deliberately sets at :41-42). actions/checkout@v4 does not fetch tags by default, so the guard git tag -l | grep -q "^${NEW_VERSION}$" at :126 runs against an empty/partial tag list and won't catch an already-released version. The downstream git push origin "$TAG" still fails safely, but the intended fail-fast is lost. Add fetch-tags: true.

  4. [non-blocking] No idempotency/concurrency handling in publish → no recovery from a partial failure. publish_miner_release.yml:59-67 pushes the tag before build/release. If the build or gh release create fails, re-running the workflow dies at git tag/git push because the tag now exists, with no way to resume. The removed workflow at least probed git ls-remote --tags first. Consider a concurrency: group and tolerating an existing tag (skip-if-present) so the release step can be retried.

  5. [nit] Dispatch inputs interpolated directly into run scripts instead of via env. create_miner_release_proposal.yml:156 (if [[ "${{ github.event.inputs.is_draft }}" == "true" ]]) and :171 (Bump type: ${{ github.event.inputs.version_type }}) break the env-var pattern used everywhere else. Low risk (choice/boolean, dispatch-only), but it's the actionlint expression-injection smell; move to env:.

  6. [nit] No top-level permissions: in create_miner_release_proposal.yml. calculate-next-version (:31) therefore inherits the repo/org default token scope (possibly write-all) though it only reads. Add a top-level permissions: contents: read and keep the per-job elevation.

  7. [nit] Operator instruction in the PR notes is already stale. The notes say "latest tag is miner-v0.4.5… choose minor to release miner-v0.5.0", but miner-v0.5.0 already exists on origin (lightweight tag, commit 11b1d1b9, 2026-06-12). The latest tag is now 0.5.0, so minor yields 0.6.0; following the written instruction would collide.

  8. [nit] sed at create_miner_release_proposal.yml:135 drops any +build suffix (^version:[[:space:]]*.+$version: X.Y.Z). Harmless for current miner-app (pubspec is a bare 0.5.0), but worth knowing if a build number is ever added. Also publish_miner_release.yml:118 matches $TAG with unescaped dots via grep -x (cosmetic; grep -Fx would be literal), and create_miner_build.yml:287 has no trailing newline (pre-existing).

Verification

actionlint clean and local version-math/prev-tag checks are claimed and plausible from the code. The gap is the entire post-merge path — tagging merge_commit_sha, the reusable build handing artifacts to create-release, and gh release create — which the test plan leaves as an unchecked box. Finding #1 lives precisely there. Recommend a full dry run against a disposable version (e.g., a high custom like 0.99.0) on a fork/test repo, confirming the tag lands on the real merge commit and all three assets attach, before trusting this in production. Cross-workflow artifact handoff (reusable build job → create-release's download-artifact) works within a single run, and the artifact names/paths (quantus_miner-{macos,linux,windows}*_macos.zip/*_linux.tar.gz/*_windows.zip) match between create_miner_build.yml:187/243/286 and publish_miner_release.yml:132-134.


🤖 AI-assisted review generated with Claude Code

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.

2 participants