ci: add the tag-driven release pipeline - #63
Conversation
There was a problem hiding this comment.
Pull request overview
Adds initial public-release governance and a tag-driven GitHub Actions release pipeline needed to ship the first v0.0.1 release, including signing/tag verification, multi-platform builds, checksums, and publishing.
Changes:
- Introduces
.github/workflows/release.ymlto verify signed annotated tags, build installers across macOS/Windows/Linux, generate checksums, and publish GitHub Releases. - Adds release governance docs:
docs/releasing.mdrunbook andSECURITY.mdvulnerability reporting policy. - Adds MIT
LICENSEfor public distribution.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
.github/workflows/release.yml |
New tag-triggered release pipeline with signing gates, multi-platform build matrix, artifact staging/smoke tests, checksum generation, and publish via gh. |
docs/releasing.md |
Release runbook covering versioning, signed tags, required secrets, rollback, and (optional) auto-update enablement guidance. |
SECURITY.md |
Security policy for supported versions and private vulnerability reporting process. |
LICENSE |
Adds MIT license text for legal clarity on distribution. |
Suppressed comments (1)
.github/workflows/release.yml:578
- Release notes always state that auto-update clients consume
latest.json, but this workflow intentionally skips generatinglatest.jsonwhen updater artifacts aren't present. This line could be misleading for v0.0.1-style releases where auto-update is off.
Auto-update clients consume \`latest.json\`."
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
a2fb2fc to
753ef49
Compare
|
Heads up: this branch and I raced, and the later commit ( Two things to decide. 1. This PR and #64 now overlap and will conflictBoth add Note that #64's 2. Hardening present at
|
| Missing | Why it mattered |
|---|---|
workflow_dispatch with a dry_run input defaulting to true |
Today this pipeline cannot be exercised at all without pushing a real tag and publishing a real release. A dry run does tag verification, version checks, all four platform builds, every smoke test, and checksum generation, then stops. It is the only way to validate a change to this workflow, or newly wired signing secrets, before a release depends on them. |
bundle.active guard in verify |
main currently has bundle.active: false. With it false, tauri build produces an executable and no installers, the artifact-collection step fails deep into a 90-minute matrix, and the reason is not obvious from the log. Checking it in verify fails in seconds with a sentence that says what to fix. |
Re-running lint, typecheck, test:unit:normal, build against the tagged tree |
The PR run tested a merge commit that no longer exists in that shape. The tagged tree is what ships and nothing had verified it. |
| Distinct error for an annotated-but-unsigned tag | A tag with no signature block and a tag whose signature failed to verify are different mistakes with different fixes. Collapsing them into one message costs a CI round trip to tell apart. |
lipo -archs check on the macOS binary |
Confirms the executable is actually built for the architecture the artifact name advertises. Without it, a matrix or target-triple mistake ships a _aarch64.dmg containing an x86_64 binary, and the first person to find out is a user whose app will not launch. |
Tag-format regex (v<major>.<minor>.<patch>[-prerelease]) |
Rejects a malformed tag before it reaches a published asset name. |
0.x treated as prerelease |
0.0.1 should not appear as "Latest" on the releases page. Suffix detection alone does not catch it. |
.deb payload check |
A .deb whose payload is missing the binary installs cleanly and then does nothing. |
| 1 MiB artifact floor | Catches a bundler that failed without a non-zero exit status. |
environment: release-signing on publish |
It is on build here but not on publish, so the job that actually writes the release is ungated. |
gh release create --verify-tag and an explicit refusal when a release already exists for the tag |
Prevents silently overwriting a published release. |
Native macos-13 for x86_64-apple-darwin |
This PR cross-compiles x86_64 on an arm64 runner. It works, but the smoke tests cannot exercise the result, and a native runner removes that gap for one cheap matrix entry. |
Things this PR does better, which I folded into 753ef49 rather than replacing:
- Rejecting a lightweight tag outright, instead of falling back to the signature on the commit it names.
- The
TAG_ALLOWED_SIGNERSsecret making a realgit verify-tagpossible on a runner. I kept it as an independent second check layered on GitHub's API verification, so a release is not gated on a single source of truth and a disagreement between the two blocks the release. - The
.sig-gatedlatest.jsonstep, which correctly no-ops today.
3. Unrelated, but blocking everything
Every open PR is currently red for one shared reason: phase1-conformance-lock.test.ts and phase1-conformance.test.ts fail with Error: Command failed: git merge-base, because the lock pins revisions no longer reachable from main after #57, #58, and #60 were squash-merged. #65 addresses it. A docs-only PR (#64) reproduces it, which is how it was isolated as unrelated to any of these changes.
Separately, the conformance workflow's own next failure is filed as #66.
|
Correction to my earlier note. I said the red CI was pending on #65. That was wrong, and #65 has since been closed without merging. Here is what is actually true, with the evidence. The repin already landed in #60 ( and The branches were the problem, not Hence The fix is a rebase onto Confirmed rather than assumed: I rebased #64 onto I have rebased #59, #61, #62, and #64. This branch (#63) still needs the same rebase — I have deliberately not touched it, since it is being worked concurrently and I did not want to force-push into that. Issue #66 is unaffected: that is the |
a62dc84 to
d651041
Compare
Add the two governance documents the repository still lacks before a public release. CONTRIBUTING.md is written against this repository rather than from a template: the real toolchain (Node 22+, pnpm 10.34.0 via Corepack, rust-toolchain.toml), the real script names, the worktree-per-branch convention, the signed-commit requirement including the pre-flight user.signingkey and gpg.format checks, and an honest description of the phase 1 conformance lock, including which paths it pins and the two-commit repin process. Contributors are told not to repin speculatively. CODE_OF_CONDUCT.md adapts the Contributor Covenant 2.1 principles and enforcement ladder, with attribution, and points reporting at the GitHub report control plus a private channel. LICENSE and SECURITY.md are deliberately not included here. They are carried by the release-infrastructure branch (#63); adding them in both places would guarantee a conflict. README.md is left untouched for the same reason: #63 already appends to it, and the links to these two files should be added once that lands. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add the two governance documents the repository still lacks before a public release. CONTRIBUTING.md is written against this repository rather than from a template: the real toolchain (Node 22+, pnpm 10.34.0 via Corepack, rust-toolchain.toml), the real script names, the worktree-per-branch convention, the signed-commit requirement including the pre-flight user.signingkey and gpg.format checks, and an honest description of the phase 1 conformance lock, including which paths it pins and the two-commit repin process. Contributors are told not to repin speculatively. CODE_OF_CONDUCT.md adapts the Contributor Covenant 2.1 principles and enforcement ladder, with attribution, and points reporting at the GitHub report control plus a private channel. LICENSE and SECURITY.md are deliberately not included here. They are carried by the release-infrastructure branch (#63); adding them in both places would guarantee a conflict. README.md is left untouched for the same reason: #63 already appends to it, and the links to these two files should be added once that lands. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add .github/workflows/release.yml and docs/RELEASING.md. The repository could build the app but had no way to ship it: no tagged build, no signing path, no checksums, and no release publication. The workflow is a new file and touches neither ci.yml nor client-v1-conformance.yml, so it is outside the phase 1 conformance lock and needs no repin. verify (Ubuntu): - rejects any tag that is not v<major>.<minor>.<patch>[-prerelease] - rejects a lightweight tag outright, and rejects an annotated tag with no signature block in those words, because that is a different mistake with a different fix - requires GitHub to report the tag object signature as verified against the keys registered to the signer account, then independently re-verifies with git verify-tag when a TAG_ALLOWED_SIGNERS secret is configured, so a release is not gated on a single source of truth. verify-tag is not the primary check because an SSH-signed tag needs an allowed-signers file a fresh runner does not have, and it would reject a good tag. - fails if the tag disagrees with package.json, tauri.conf.json, or the [package] version in Cargo.toml, or if bundle.active is false - re-runs lint, typecheck, unit tests, and build against the tagged tree - marks any 0.x or suffixed version as a prerelease build (four native targets, fail-fast disabled): - linux-x86_64 deb, macos-aarch64 and macos-x86_64 app+dmg, windows-x86_64 msi+nsis - Apple signing and notarization, and Windows Authenticode, activate only when their secrets are present; an unsigned release still succeeds but emits a loud warning for every unsigned platform - the Windows thumbprint is injected through a --config overlay that is deleted afterwards, so the tracked tauri.conf.json is never modified - smoke tests read the artifact, not the exit status: Info.plist version and identifier, lipo architecture, codesign, .deb Version and payload, Authenticode status, and a 1 MiB floor that catches a bundler that failed silently publish (Ubuntu, the only job with contents: write): - recomputes SHA256SUMS.txt over the bytes actually being published - generates notes with the commit list since the previous tag - gh release create --verify-tag, refusing to overwrite an existing release Signing material lives in a release-signing environment rather than repository secrets. All ten action references are pinned to full commit SHAs. Every job sets timeout-minutes and least-privilege permissions. No updater artifacts are produced: the updater plugin is not configured, and createUpdaterArtifacts fails the Tauri build outright. docs/RELEASING.md records the exact steps required to enable it later. workflow_dispatch takes an existing tag and a dry_run flag defaulting to true, so the whole pipeline can be rehearsed without publishing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Restore the full release-infrastructure deliverable set after a concurrent worktree reset dropped LICENSE and SECURITY.md and replaced the release pipeline and runbook with shorter versions. This reinstates the complete, validated work while preserving the README.md added by the intervening commit. - LICENSE: MIT, Copyright (c) 2026 OpenCoven (canonical text). - SECURITY.md: supported versions + private GitHub advisory reporting. - docs/releasing.md: tag -> build -> checksum -> publish -> smoke-test -> rollback runbook, secrets table, and honest note that v0.0.1 ships without auto-update plus the exact enablement procedure. - .github/workflows/release.yml: tag-triggered (v*) pipeline with signed-tag verification (annotated + git verify-tag + version match across package.json, tauri.conf.json, Cargo.toml), matrix installer builds (macOS aarch64/x86_64, Windows x86_64, Linux x86_64) via `tauri build` with explicit per-platform --bundles, gated Apple/Windows signing, SHA256SUMS, per-platform smoke tests, opt-in updater/latest.json (skipped, never hard-fails, since createUpdaterArtifacts is false), and gh-CLI publishing. Third-party actions pinned to full commit SHAs; least-privilege per-job permissions; release-signing environment on signing jobs; artifact paths quoted for the space in the product name. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The README linked `docs/RELEASING.md` but the file is tracked as `docs/releasing.md`. macOS is case-insensitive so this looked fine locally; GitHub is case-sensitive and the link would 404. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add dry-run coverage, tag provenance checks, tagged-tree validation, artifact smoke tests, conditional updater handling, and guarded draft publishing. Keep security and release documentation aligned with the actual v0.0.1 packaging path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Describe the manual release rehearsal path and the current in-memory conversation boundary without discouraging valid security reports. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Skip release creation when dry_run is enabled and publish a summary of the verified artifacts instead. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
4928d0a to
4808e3e
Compare
|
Rebased onto current `main` now that #85 (the Windows supervisor fix, which is what this PR's only real failure was waiting on) has merged. Clean rebase, no conflicts. Verified locally: `cargo check/clippy/fmt` clean, `pnpm typecheck`/`lint`/`format:check` clean (7 pre-existing baseline CSS warnings), full unit suite 475/475 real tests (1 known unrelated environment flake in `phase1-schema-v2-evidence.test.ts`), and `src/release-workflow.test.ts` 3/3. Pushed. Should be fully green in CI now that both the Windows fix and billing are sorted. 🤖 Generated with Claude Code |
Add the two governance documents the repository still lacks before a public release. CONTRIBUTING.md is written against this repository rather than from a template: the real toolchain (Node 22+, pnpm 10.34.0 via Corepack, rust-toolchain.toml), the real script names, the worktree-per-branch convention, the signed-commit requirement including the pre-flight user.signingkey and gpg.format checks, and an honest description of the phase 1 conformance lock, including which paths it pins and the two-commit repin process. Contributors are told not to repin speculatively. CODE_OF_CONDUCT.md adapts the Contributor Covenant 2.1 principles and enforcement ladder, with attribution, and points reporting at the GitHub report control plus a private channel. LICENSE and SECURITY.md are deliberately not included here. They are carried by the release-infrastructure branch (#63); adding them in both places would guarantee a conflict. README.md is left untouched for the same reason: #63 already appends to it, and the links to these two files should be added once that lands. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* docs: add contributing and code of conduct Add the two governance documents the repository still lacks before a public release. CONTRIBUTING.md is written against this repository rather than from a template: the real toolchain (Node 22+, pnpm 10.34.0 via Corepack, rust-toolchain.toml), the real script names, the worktree-per-branch convention, the signed-commit requirement including the pre-flight user.signingkey and gpg.format checks, and an honest description of the phase 1 conformance lock, including which paths it pins and the two-commit repin process. Contributors are told not to repin speculatively. CODE_OF_CONDUCT.md adapts the Contributor Covenant 2.1 principles and enforcement ladder, with attribution, and points reporting at the GitHub report control plus a private channel. LICENSE and SECURITY.md are deliberately not included here. They are carried by the release-infrastructure branch (#63); adding them in both places would guarantee a conflict. README.md is left untouched for the same reason: #63 already appends to it, and the links to these two files should be added once that lands. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * docs: correct governance references Align the documented Node pin and Phase 1 authority scope with current main. Clarify conduct reporting channels and keep security reports on the SECURITY.md path.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: align governance guidance with current workflow Correct the pinned setup, desktop connection model, branching and release guidance, signing checks, documentation validation, conformance repin process, and conduct reporting path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Adds the release pipeline and the public-release governance files the repo needs before v0.0.1 can ship. Four new files plus an eleven-line README section, zero modifications to anything else — nothing pinned by
phase1-conformance.lock.jsonis touched, so no repin is required..github/workflows/release.ymlTag-triggered (
v*) pipeline in three jobs:verify-tag— rejects lightweight tags, requires a signature block, runsgit verify-tagagainstTAG_ALLOWED_SIGNERS, and fails on any version mismatch acrosspackage.json/tauri.conf.json/Cargo.toml. A release cannot be cut from an unsigned or inconsistent tag.build— 4-target matrix,fail-fast: falseso one platform failing still yields the others:aarch64-apple-darwinapp,dmgx86_64-apple-darwinapp,dmgx86_64-pc-windows-msvcmsi,nsisx86_64-unknown-linux-gnuappimage,debToolchain mirrors
ci.ymlexactly (Node 24.18.1, pnpm 10.34.0, Rust 1.95.0). Apple signing + notarization, Windows signing, and Tauri updater signing are each gated on secrets — when a secret is absent the build still runs and emits a notice rather than failing, so the pipeline is exercisable before signing identities exist. Each platform runs a native smoke test on its own artifacts (existence, non-empty, expected extension,.appstructure pluscodesign/spctlwhen signed), and hard-fails if a target produced no installer.publish— generatesSHA256SUMS, re-verifies withshasum -a 256 -c, and publishes viagh release createwith prerelease detection.Permissions are least-privilege (top-level
{};contents: readfor verify/build;contents: writeonly for publish). All 6 third-party actions are pinned to 40-hex commit SHAs.Auto-update is deliberately off for v0.0.1
The repo has no
plugins.updatersection andcreateUpdaterArtifactsisfalse, so no updater archives or.sigfiles are produced. The workflow detects this and skipslatest.jsonwith a notice instead of publishing a broken manifest. Turning it on later is:pnpm tauri signer generate→ addplugins.updaterwithpubkey+endpoints→ flipcreateUpdaterArtifacts→ setTAURI_SIGNING_PRIVATE_KEY.LICENSEMIT,
Copyright (c) 2026 OpenCoven. The repo previously had no license file at all, which makes a public release legally ambiguous. (PR #59 adds the matching"license": "MIT"topackage.json.)SECURITY.mdSupported-version table and private vulnerability reporting instructions.
docs/releasing.mdThe end-to-end runbook: preflight, version bump, signed tag, build, checksum verification, publish, post-release smoke test, and rollback. Section 7 documents the recommended
mainbranch protection, which is not applied by this PR — that is a repo-admin action for a human to take deliberately.Verification
release.ymlparses as valid YAML; jobs resolve toverify-tag,build,publishbash -nclean on run blocks;node --checkclean on the embedded manifest scriptpnpm lintandpnpm typecheckcleanStill needs a human
Branch protection on
mainis absent and is intentionally left for Val to apply.README.mdAn eleven-line Releasing section pointing at the workflow and the runbook, so the process is discoverable from the front page rather than only from
docs/.Note on this branch's history
This branch was worked concurrently and at one point a force-push landed a variant that dropped
LICENSEandSECURITY.mdand useddocs/RELEASING.md. The current head restores the complete set and keeps the README section from the competing commit — nothing was lost. The README link was also corrected fromdocs/RELEASING.mdtodocs/releasing.md: macOS is case-insensitive so it looked fine locally, but GitHub is case-sensitive and the link would have 404'd.Squashing on merge is reasonable if you want a single clean commit.