Skip to content

ci: source-only release workflow on v* tag - #80

Merged
hellno merged 1 commit into
mainfrom
hellno/find-app-icon
Jun 18, 2026
Merged

ci: source-only release workflow on v* tag#80
hellno merged 1 commit into
mainfrom
hellno/find-app-icon

Conversation

@hellno

@hellno hellno commented Jun 18, 2026

Copy link
Copy Markdown
Owner

Summary

Adds a source-only release pipeline: a v* tag now runs .github/workflows/release.yml, which reuses ci.yml as the gate via workflow_call (the full Definition of Done) and then publishes a GitHub Release whose body is the tag's CHANGELOG.md section plus a static testnet/source-only warning (GitHub attaches the source tarballs; no binaries are built). A new scripts/release-check.sh — wrapped by just release-check <tag> — is the single source of truth that CI and the local pre-flight share: it validates the tag shape, asserts every crate version matches the tag, and extracts the CHANGELOG section, failing loudly otherwise. The v* trigger moves out of ci.yml so one workflow owns the tag (no double build), and docs/RELEASING.md is rewritten to the automated flow. Binaries stay deferred on purpose: for a wallet, an unsigned/un-notarized download trains users to bypass Gatekeeper and a same-release checksum proves download-integrity rather than authenticity, so signed + notarized binaries (Apple cert) are the real later unlock.

Linked issue

No issue. Motivation: tagging v* previously only ran CI and produced nothing publishable; this gives each tag a gated, verifiable GitHub Release without the trust hole of shipping unsigned wallet binaries. The approach was chosen via a dual-model (/autoplan) review.

Definition of Done

This PR changes only CI workflows, a shell script, the Justfile, and docs — no Rust, no Cargo.toml/Cargo.lock changes (git diff origin/main --name-only has zero .rs/Cargo.* entries). So the Rust gates are unaffected by this change set, and the PR's own CI (ci.yml on pull_request, which this PR also wires as the release gate) runs them in full.

  • cargo fmt --all --check is clean — verified locally (output below)
  • just check — not run locally (no Rust delta); runs in this PR's CI
  • cargo test --workspace — not run locally (no Rust delta); runs in this PR's CI
  • No new or changed dependencies in Cargo.toml / Cargo.lock
  • No visual/UI change — DESIGN.md N/A (CI/tooling/docs only)
  • Secrets stay in Zeroizing / never logged — N/A; no secret-handling code touched (the script reads CHANGELOG.md + cargo metadata only)
Evidence (paste command output here)
$ cargo fmt --all --check
# clean (no output, exit 0)

$ git diff origin/main --name-only | grep -E '\.rs$|Cargo\.(toml|lock)$'
# (no matches — no Rust/Cargo changes)

$ shellcheck scripts/release-check.sh
# clean

$ actionlint .github/workflows/release.yml .github/workflows/ci.yml
# clean

$ just release-check v0.0.1-alpha
release-check: v0.0.1-alpha OK — all crates at 0.0.1-alpha; CHANGELOG section found.
# (prints the rendered CHANGELOG body; bad tag / version mismatch / missing section all hard-fail)

Notes for reviewers

  • Gate reuse, not duplication: release.yml's gate is uses: ./.github/workflows/ci.yml (workflow_call), so the release gate can never drift from CI. ci.yml lost its tags: ["v*"] push trigger and gained workflow_call.
  • Third-party actions are SHA-pinned in release.yml (actions/checkout, softprops/action-gh-release) since the release workflow is a high-value supply-chain target; cargo-deny does not cover Actions.
  • Hard pre-flight: the release body is built from the closed ## [X.Y.Z] CHANGELOG section, never [Unreleased]RELEASING.md now documents promoting [Unreleased][X.Y.Z] before tagging, and just release-check enforces it.
  • Safe to test without publishing: gh workflow run release.yml --ref <branch> -f tag=v0.0.1-alpha runs the full gate and prints the rendered body; the publish step is gated on github.event_name == 'push'.
  • Deferred (out of scope): binary artifacts via Apple codesigning + notarization (Option C), revisited on an observed tester request. The manual, unsigned .app escape hatch stays documented in RELEASING.md.

Add .github/workflows/release.yml: on a v* tag it reuses ci.yml as the gate
(workflow_call, the full Definition of Done), then publishes a source-only GitHub
Release whose body is the tag's CHANGELOG section plus a static testnet/source-only
warning. GitHub attaches the source tarballs; no binaries are built.

scripts/release-check.sh (wrapped by `just release-check`) is the single source of
truth that CI and the local pre-flight share: it validates the tag shape, asserts
every crate version matches the tag, and extracts the CHANGELOG section, failing
loudly with the fix otherwise.

Move the v* tag trigger out of ci.yml so one workflow owns the tag (no double build),
and rewrite docs/RELEASING.md to the automated flow.

Binaries stay deferred on purpose: for a wallet, an unsigned, un-notarized download
trains users to bypass Gatekeeper, and a checksum in the same Release proves
download-integrity, not authenticity. Signed and notarized binaries (Apple cert) are
the real later unlock, tracked separately.
@hellno
hellno merged commit 2af4042 into main Jun 18, 2026
5 checks passed
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