Skip to content

ci: add opt-in fork-friendly release ops (signing, publishing, fork sync) - #407

Open
arcaven wants to merge 10 commits into
Zious11:developfrom
ArcavenAE:feat/fork-friendly-release-ops
Open

ci: add opt-in fork-friendly release ops (signing, publishing, fork sync)#407
arcaven wants to merge 10 commits into
Zious11:developfrom
ArcavenAE:feat/fork-friendly-release-ops

Conversation

@arcaven

@arcaven arcaven commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Offers back the release-ops pipeline the ArcavenAE fork has been running in production since 2026-07-15. Everything is inert by default: with no repository variables set, every job either skips at scheduling time or never triggers, and CI is unaffected — no Apple Developer account, secrets, or tap repo are required to host these files. The design is ported from jira-cli's fork-friendly release-ops (docs/specs/fork-friendly-release-ops.md there), where the same pattern already lives upstream.

What this adds

File Purpose Inert unless
.github/workflows/sign-and-publish.yml Sign + notarize macOS binaries on five channels (develop push → wirerust-a, v*-dev.*wirerust-d, v*-beta.*wirerust-b, v*-rc.*wirerust-rc, stable v*wirerust), publish to a Homebrew tap vars.SIGNING_ENABLED == 'true' (+ vars.HOMEBREW_TAP_REPO for tap jobs)
.github/workflows/backfill-release.yml Build + release an existing tag that has no GitHub Release; optional sign/publish manual workflow_dispatch only; sign/tap jobs var-gated
.github/workflows/sync-upstream.yml Scheduled fork→upstream merge (main/develop/factory-artifacts) with protected-file auto-resolution vars.SYNC_UPSTREAM_REPO set (forks only)
.github/workflows/signing-guard.yml Actively runs on PRs/pushes touching workflows: YAML-structure-aware CWE-77 scanner for inline ${{ }} expansions in secret-bearing jobs (scripts/check-signing-workflow-injection.sh, includes --self-test negative fixture) n/a — read-only lint, no secrets, python3+PyYAML are runner-preinstalled
.github/local-workflows.txt Registry of files the fork sync auto-resolves "ours" on conflict read only by sync-upstream
Formula/wirerust{,-a,-b,-d,-rc}.rb Homebrew formula templates (placeholders sed'd at publish); parallel channel installs (wirerust + wirerust-a coexist) read only by the publish jobs
packaging/Info.plist, scripts/create-{app,dmg,pkg}.sh macOS .app/.dmg/.pkg packaging helpers invoked only by sign jobs

Notes for review

  • All uses: are 40-char SHA-pinned and pass the existing action-pin gate. dtolnay/rust-toolchain is pinned to a master SHA with an explicit toolchain: stable input rather than a versioned-branch snapshot — the versioned-branch SHAs hardcode their toolchain and silently ignore the toolchain: input (we hit this live: two failed runs before the fix; wirerust needs 1.91 and has no rust-toolchain.toml to mask it, since this repo intentionally tracks rolling stable).
  • Security posture: run-blocks in secret-bearing jobs env-bind all context expressions (CWE-77), alpha tags are reserved atomically via the GitHub API (no TOCTOU), verification steps use set -eo pipefail + mktemp/trap. The signing-guard workflow enforces the env-binding rule structurally and fails closed.
  • Bundle identifier is com.arcavenae.wirerust in packaging/Info.plist + create-pkg.sh — per the jira-cli convention this reflects the first signing fork; a different signing fork overrides it to match its own Apple team. Happy to change if you'd prefer a placeholder.
  • Formula descriptions stay ≤ 80 chars including channel suffixes (Homebrew's brew audit desc cap); the templates carry the audit rules as comments.
  • The CHANGELOG gate is untouched: this PR modifies only .github/, Formula/, packaging/, scripts/.

Verified in production (ArcavenAE/wirerust)

  • Alpha channel: signed + notarized + stapled binaries/pkg/dmg published, brew install arcavenae/tap/wirerust-a verified (Developer ID chain + hardened runtime).
  • Stable channel: v0.12.1 backfilled, signed, brew install arcavenae/tap/wirerust verified; parallel install with wirerust-a confirmed.
  • Sync: scheduled merges green across main/develop/factory-artifacts.

If merged as-is, the fork and upstream copies are byte-identical, so future scheduled syncs remain conflict-free and the local-workflows.txt entries become pure insurance. mise users also get mise use github:Zious11/wirerust@latest for free from the release assets.

arcaven added 7 commits July 15, 2026 18:31
…ream sync)

Port the ArcavenAE/jira-cli fork-friendly release-ops pipeline
(docs/specs/fork-friendly-release-ops.md there) to the wirerust fork:

- sign-and-publish.yml: five channels — develop push → alpha
  (wirerust-a, builds from source), v*-dev.* → wirerust-d,
  v*-beta.* → wirerust-b, v*-rc.* → wirerust-rc, v*.*.* → wirerust.
  Apple codesign + notarize + staple, .pkg/.dmg packaging, Homebrew
  tap formula publish. All jobs gated on SIGNING_ENABLED /
  HOMEBREW_TAP_REPO repository variables.
- sync-upstream.yml: scheduled merge from Zious11/wirerust
  (main/develop/factory-artifacts) with protected-file auto-resolution
  via .github/local-workflows.txt. Gated on SYNC_UPSTREAM_REPO.
- backfill-release.yml: manual-dispatch build+release+sign for an
  existing tag. No scheduled gap-fill automation is installed.
- signing-guard.yml: fork-local CI job running
  scripts/check-signing-workflow-injection.sh (CWE-77 YAML-aware
  scanner) — hosted as a separate workflow instead of a ci.yml job
  so the upstream-shared ci.yml stays conflict-free.
- Formula templates (wirerust, -a, -b, -d, -rc) for parallel channel
  installs; packaging/Info.plist + create-app/dmg/pkg scripts adapted
  to the wirerust binary and com.arcavenae.wirerust bundle id.

Differences from the jira-cli original: no embedded-OAuth build env or
smoke checks (wirerust has none), backfill matrix matches upstream
release.yml targets (no aarch64-linux cross build).
The SHA-pinned dtolnay/rust-toolchain ref cannot infer the toolchain
from the ref name and defaulted to rustc 1.85.0; wirerust requires
1.91. jira-cli masks this via rust-toolchain.toml (channel = stable),
which wirerust does not carry. Applies to sign-and-publish alpha-build
and backfill-release build.

Refs: run 29458958576
The previous SHA is a snapshot of the action's 1.85.0 versioned branch,
which hardcodes its toolchain and rejects the toolchain input
('Unexpected input(s) toolchain'). Pin master, which requires and
honors an explicit toolchain spec.

Refs: run 29459130005
Homebrew's desc cop caps the description at 80 characters. The
channel-suffix pattern makes long base descriptions a latent audit
failure (jr-a sits at 79/80 today). Drop the redundant 'tool written
in Rust' and use short channel suffixes so every variant stays under
55 characters.
These templates are the copy source for the next repo's release ops;
the comment travels with the copy so the 80-char desc cap (including
channel suffix) is visible at authoring time.
Zious11 added a commit that referenced this pull request Jul 18, 2026
…WITH-CHANGES — DEFERRED by human; triage preserved
Zious11 added a commit that referenced this pull request Jul 19, 2026
…velop=fcd57dc

- STATE.md D-476: PR #414 (ArcavenAE ci/scorecard-guard) ADOPTED; security-triaged
  SAFE-WITH-CHANGES; F1 CWE-494 RESOLVED-CLEAN (all 4 SHA↔tag MATCH, no advisories);
  CI 13/13 incl. action-pin-gate; human squash-merge fcd57dc 2026-07-19T01:54:40Z
- develop_head updated: 82ad2edfcd57dc
- PR-414-FORK-SCORECARD carry-forward CLOSED/ADOPTED; SCORECARD-ENABLEMENT-RUNBOOK
  carry-forward added (F2 CWE-200 + optional harden-runner bump v2.19.4→v2.20.0)
- PR-407-FORK-RELEASE-OPS remains OPEN (disjoint files, no overlap)
- Session Resume Checkpoint: D-476 steady-state ACTIVE; open: PR #407, STORY-166/175..179
- planning/pr-414-security-triage.md: security triage report + F1 SHA verification
  section appended (RESOLVED-CLEAN, dated 2026-07-19)
- planning/vsdd-factory-upstream-issues.md: upstream issues planning artifact
- sidecar-learning.md: session-end markers appended
Zious11 added a commit that referenced this pull request Jul 20, 2026
Record decision D-484: session resumed from D-483 pause (human-approved,
2026-07-20). Worktree health PASS (factory-artifacts 5f9218d, 0 ahead/behind);
develop=fa9be701 verified; no story worktrees; no in-flight work.

Human decisions at resume:
1. Resume point = STORY-176 v2.2 per-story delivery (wave-84 3/3), wave-84 gate after.
2. Dependabot github-actions PRs #422-425 DEFERRED to DEP-SOAK-FOLLOWUP-2026-07-27.
   PR #423 (harden-runner 2.20.0) satisfies SCORECARD-ENABLEMENT-RUNBOOK re-pin watch
   (window watch satisfied; no manual re-pin needed).
3. PR #407 governance remains pending/unchanged.

STATE.md changes:
- pipeline: PAUSED → ACTIVE
- frontmatter current_step + timestamp refreshed
- EXACT RESUME POINT updated to D-484 active state
- Last Updated / Project Metadata Mode rows updated
- Phase Progress Wave-84 row: PAUSED → RESUMED (D-484)
- Concurrent Cycles wave-084 row updated
- Current Phase Steps: D-480 housekeeping row rolled out (last-5 rule); D-484 added
- Decisions Log: D-484 appended
- SCORECARD-ENABLEMENT-RUNBOOK carry-forward updated (PR #423 arrived, deferred)
- DEP-SOAK-FOLLOWUP-2026-07-27 carry-forward updated (adds PRs #422-425)
- Session Resume Checkpoint replaced with D-484 ACTIVE checkpoint
- Historical Content: D-480 housekeeping + D-484 checkpoint archive rows added
- Banner wc-l: 292 lines (verified)

Cycle files:
- cycles/wave-084/burst-log.md: D-480 housekeeping row archived (last-5 overflow)
- cycles/wave-084/session-checkpoints.md: D-483 SESSION WRAP checkpoint archived
Zious11 added a commit that referenced this pull request Sep 6, 2026
…C-DRIFT fixed, dispositions recorded

Maintenance sweep maint-2026-09-05 (prior: maint-2026-07-21) run against develop
0b1ea80 (v0.13.3). 6 sweeps executed (1 Dependency Audit, 2 Documentation Drift,
3 Pattern Consistency, 4 Holdout Freshness, 5 Performance Baseline, 7 Spec
Coherence); Sweep 6 DTU SKIP (dtu_required:false) + Sweep 9 a11y SKIP (no UI).
Overall CLEAN: 0 CVEs, 0 test regressions, 0 performance regressions >10%,
0 blocking defects.

- maintenance/sweep-report-2026-09-05.md: consolidated top-level report.
- STATE.md: D-553 decision logged; SPEC-DRIFT SC-001 fixed (Drift Item rows
  PG-W84-LOCAL-BATCH/PG-W85-003/PG-W85-005 RESOLVED); maintenance_run_id/
  maintenance_prior_run frontmatter updated; Current Phase Steps + Session
  Resume Checkpoint rotated (D-548 evicted to Decisions Log, D-552 archived
  to cycles/wave-086/session-checkpoints.md); Active Carry-Forwards updated
  with this run's dispositions. Pipeline stays CLEAN RELEASED/PAUSED at
  v0.13.3 — no release/version/head change.
- cycles/maint-2026-09-05/lessons.md: two process observations
  (PG-MAINT-CLASSIFIER-MERGE-BLOCK, PG-MAINT-REVIEWONLY-HOOK-TRIP).

Dispositions: 5 Dependabot Rust-dep bumps (#459/#458/#444/#443/#442)
human-authorized to merge, execution handed to the human (gh pr merge
classifier-blocked this session); 5 Actions-bump PRs (#457/#456/#455/#449/#436)
HELD for supply-chain review; human PRs #451/#407 reviewed, not merged;
2-edit docs-only fix queued (not opened, gh pr create classifier-blocked);
5 holdout coverage gaps + 1 fixture-wiring opportunity routed to product-owner.

Single-commit burst per TD-VSDD-053.

Claude-Session: https://claude.ai/code/session_01Atd9SQNyxBnmfeBcVYVTHt
Zious11 added a commit that referenced this pull request Sep 6, 2026
Reconcile maint-2026-09-05 (D-553) from "5 Rust-dep merges authorized,
pending human execution" to executed-and-verified truth: all 5 merged
to develop (#458 clap/#443 serde_json/#442 anyhow/#444 serde/#459
owo-colors); develop tip 0b1ea80->adc9428d; CI fully green, zero
regression. DEP-SOAK-FOLLOWUP-2026-07-27 + ROUTE-BC-DEFER-2026-07-11
cleared. Scope note: human also merged 2 held Actions-bump PRs beyond
the authorized set (#457/#456); held-set narrowed to #455/#449/#436.
#451 now DIRTY/conflicting; #407 unchanged; doc-fix still queued.
No release (still v0.13.3; main unchanged 46ebd6e).

STATE.md: current_step/timestamp/develop_head frontmatter, EXACT
RESUME POINT, Project Metadata (Version/Develop HEAD/Last Updated),
Phase Progress maint-2026-09-05 row, Current Phase Steps (D-554 added,
D-549 evicted - preserved verbatim in Decisions Log), Decisions Log
D-554 added, Active Carry-Forwards (DEP-SOAK/ROUTE-BC-DEFER cleared,
MAINT-2026-09-05-DEP-RUST-MERGE-HANDOFF resolved, ACTIONS-BUMPS-HELD
narrowed, new PR-451-DTOLNAY-PIN-CONFLICT row), Session Resume
Checkpoint replaced (D-553 archived to
cycles/maint-2026-09-05/session-checkpoints.md), size-budget banner
reconciled to true wc -l (363).

maintenance/sweep-report-2026-09-05.md: frontmatter develop_head_close
+ prs_merged updated; "Post-run execution (D-554)" section appended.

Claude-Session: https://claude.ai/code/session_01Atd9SQNyxBnmfeBcVYVTHt

@Zious11 Zious11 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thank you for this contribution — the fork-friendly release-ops design is genuinely well-engineered. We confirmed it is opt-in and inert on the base repo today (0 repository variables set), the workflows follow least-privilege permissions, the injection guard's structural scope detection plus its negative-fixture self-test are a solid pattern, and the atomic tag reservation approach is sound. We'd like to get this merged, but we need the items below addressed first.

Blocking (please fix before merge)

1. Homebrew formulae have no OS guard
Formula/*.rb (around line 9) branches only on Hardware::CPU.arm?, with no OS.mac? check or depends_on :macos. On Linux, brew install falls through to the else branch and downloads the *-darwin-* Mach-O binary, which will silently fail to run. Please add depends_on :macos, or on_macos/on_linux blocks with an explicit "unsupported on Linux" message — these formulae don't currently ship a Linux artifact.

2. Unknown-channel tags produce misleading release notes
In sign-and-publish.yml, a tag whose suffix doesn't match -dev./-rc./-beta. is classified channel=unknown. The stable-homebrew job's if: correctly skips publishing in that case, but the "Update release title and notes" case statement's default branch (*) FORMULA="wirerust") still advertises brew install .../wirerust for a version that was never pushed to that formula. Please default the binary/install line to "download below" for the unknown-channel case.

3. Injection-guard scope gap (CWE-94/CWE-78)
sync-upstream.yml's "Report unresolved conflict" step inlines ${{ steps.merge.outputs.files }} directly into a run: body, inside a job that holds contents: write plus secrets.SYNC_UPSTREAM_SSH_KEY. That job meets the injection guard's own in-scope criteria, but check-signing-workflow-injection.sh only scans sign-and-publish.yml and backfill-release.yml, so it misses this case. Please: (a) env:-bind the value (e.g. CONFLICTED_FILES: ${{ steps.merge.outputs.files }}, then reference "$CONFLICTED_FILES"), and (b) extend the guard's scanned-file list — or better, have it self-discover workflows meeting its in-scope criteria — so CI catches this class going forward. (To be clear: this is only exploitable via a compromised operator-configured upstream, not a base-repo risk — but it does contradict the PR's stated "all context expressions env-bound" posture.)

4. Dead spec-doc references
There are 7 references across 6 files citing docs/specs/fork-friendly-release-ops.md, which doesn't exist in this repo — it only lives in ArcavenAE/jira-cli. Please either port the spec into wirerust or repoint the comments to a correct external permalink.

Strongly recommended before merge (security-sensitive duplication)

5. Roughly 450 lines of keychain-import → codesign → notarize → verify → checksum logic are triplicated across alpha-sign/stable-sign in sign-and-publish.yml and the backfill sign job. Please extract this into a composite action or a shared scripts/sign-and-notarize.sh — drift has already crept in (inconsistent set -euo pipefail). The parallel Homebrew tap-update block is likewise duplicated 3x with the same drift risk; please factor it into scripts/update-homebrew-formula.sh.

Fast-follow (not merge-blocking; fork-operator-only, inert on the base repo)

6. sync-upstream.yml merges upstream with no commit-signature verification and pushes directly (no PR/CI gate) to protected branches, silently "ours-wins" shadowing the signing/release files with no drift report (CWE-829/494). Consider opening a PR instead of a direct push, git verify-commit/signed-tag checks before merge, and a scheduled diff of the local-workflows set vs. upstream that warns on divergence.

7. Nothing validates that signing secrets are actually populated, nor that the release environment has protection rules configured (CWE-280/1188). Consider adding secret-presence preflight asserts and a verify-signing-setup dispatch job.

Minor

  • Release-notes NOTES= continuation lines carry literal 10-space indentation, which GitHub renders as a code block — left-align them or build the notes with printf.
  • sync-upstream.yml is missing a trailing newline.
  • egress-policy: audit (log-only) is set on jobs holding signing certs — consider block with an explicit allowlist.
  • dtolnay/rust-toolchain is pinned here to a different SHA than open PR #451 pins repo-wide — whichever PR merges second should reconcile onto the other's SHA.

Thanks again for the care put into this — the security-conscious design (opt-in, least-privilege, self-testing injection guard) is exactly the kind of contribution we want more of. Looking forward to a follow-up revision.

https://claude.ai/code/session_01EQAaPvh9fwaG31jmkPicKW

Zious11 added a commit that referenced this pull request Sep 6, 2026
…rged, #451 deferred, #407 request-changes posted

Session resumed from PAUSED (D-555). Human directive: clear the carry-over
PRs from maint-2026-09-05.

- 3 held Dependabot GitHub-Actions bumps supply-chain reviewed MERGE-SAFE
  and merged to develop: #455 codeql-action (1c7d740), #449
  scorecard-action (36bccab, also patches transitive CVE-2026-53488/
  47262/34986), #436 actions/checkout (9c49951). Held set now empty.
- Docs-only PR #465 squash-merged (97361cd), resolving the 2 LOW
  STALE-DOC findings from maint-2026-09-05 Sweep 2.
- PR #451 (dtolnay-pin) DEFERRED per explicit human decision — untouched.
- PR #407 (fork-friendly-release-ops) given a full review; CHANGES_REQUESTED
  posted to the contributor; remains open under PR-407-FORK-RELEASE-OPS.
- develop_head adc9428 -> 97361cd; backlog remains empty.
- Two process observations recorded in cycles/maint-2026-09-05/lessons.md
  (L-3 PG-MERGE-WRAPPER-BYPASS, L-4 PG-MAINT-CLASSIFIER-MERGE-BLOCK
  recurrence).

STATE.md is ~118KB / NEEDS-COMPACT. A /compact-state pass is advisable
before the next burst; not performed in this burst.

Single-commit burst (TD-VSDD-053).

Claude-Session: https://claude.ai/code/session_01EQAaPvh9fwaG31jmkPicKW
…cope, dead refs, dedup)

Blocking items from the 2026-09-06 review, plus the duplication item and the
four minors.

1. Homebrew formulae had no OS guard. All five now declare `depends_on :macos`.
   The release pipeline builds only darwin-arm64 and darwin-amd64, so on Linux
   the Hardware::CPU.arm? else-branch downloaded a Mach-O that could not run.

   Also removes the stable formula's `version` line. Its tag is v<version>, so
   brew scans the same value from the URL and `brew audit --strict` rejects the
   duplicate. The four prerelease templates keep theirs; their tags do not scan
   to the declared value. Verified by rendering all five with realistic tags in
   a scratch tap: `brew audit --strict` and `brew style` clean on each.

2. Unknown-channel tags no longer advertise a `brew install` line. The default
   case arm now leaves FORMULA empty, so the install line stays "download
   below". The release title falls back to the bare project name rather than to
   a formula that was never updated.

3. Injection-guard scope gap. `sync-upstream.yml`'s conflict report now binds
   `steps.merge.outputs.files` through `env:`, and every other context in that
   workflow's run bodies is bound too. The guard self-discovers: it scans every
   workflow in .github/workflows/ on its existing structural criteria, with
   sign-and-publish.yml and backfill-release.yml kept as required files whose
   zero-in-scope result is still the broken-detection sentinel. Discovery finds
   sync-upstream.yml, ci.yml and release.yml. Negative control: reintroducing
   the inline expression makes the guard fail and name the exact step.

4. Dead spec references. All seven now point at a commit-pinned permalink to
   the external design doc rather than a path that does not exist here.

5. Deduplication. The signing logic was byte-identical between stable-sign and
   the backfill sign job, and differed from alpha-sign only by the binary
   prefix; the tap update was near-identical across three jobs. Both are now
   scripts: scripts/sign-and-notarize.sh and scripts/update-homebrew-formula.sh,
   with `set -euo pipefail` in both, which is the drift the review named.

   The signing script takes subcommands rather than running end to end, so each
   phase stays its own workflow step and its secrets stay scoped to that step.
   A single entry point would put the notarization credentials in scope for
   signing and the signing identity in scope for notarization. Verified: the
   step sequence is unchanged in both workflows, and per-step env keys are
   identical before and after apart from one added COMMIT_SHA.

Minors: release notes are built with printf so continuation lines carry no
YAML indentation and stop rendering as a code block; sync-upstream.yml gains
its trailing newline; the rust-toolchain pin moves to the SHA open PR Zious11#451
uses, so whichever lands first the other needs no reconciliation.

Not done: flipping harden-runner to `egress-policy: block`. Block mode needs a
per-repo allowlist, and the base repo has run these jobs in audit mode with no
detections history to build one from, so turning it on blind is how the signing
jobs start failing on an unrelated day. Happy to do it as a follow-up once
there is a run history to derive the allowlist from.

Claude-Session: https://claude.ai/code/session_01YV58wMepvhR6qNYZhwPJkV
@arcaven

arcaven commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

Revision pushed as 3643b0a9. All four blocking items, the duplication item, and the four minors. Every claim below has the command that produced it, since a few of them are counts.

1. OS guard. All five formulae now declare depends_on :macos. While in there I also dropped the stable formula's version line: its tag is v<version>, so brew scans the same value from the URL and brew audit --strict rejects the duplicate. The four prerelease templates keep theirs, because their tags do not scan to the declared value. Verified by rendering all five with realistic tags into a scratch tap:

$ brew audit --strict arcaven-scratch/wr/wirerust      # and -a, -b, -d, -rc
(clean)
$ brew style arcaven-scratch/wr/wirerust               # and the other four
1 file inspected, no offenses detected

2. Unknown channel. The default case arm now leaves FORMULA empty, so BINARY_LINE stays download below. The release title falls back to the bare project name rather than to a formula that was never updated.

3. Guard scope. sync-upstream.yml's conflict report binds steps.merge.outputs.files through env:, and every other context in that workflow's run: bodies is bound too, so the stated posture now holds there. The guard self-discovers rather than reading a list: it scans every workflow in .github/workflows/ against its existing structural criteria, keeping sign-and-publish.yml and backfill-release.yml as required files whose zero-in-scope result is still the broken-detection sentinel. A discovered workflow scoring zero is the normal case, not a sentinel.

Summary: scanned 46 run-blocks across 2 required + 3 discovered in-scope file(s)
  In-scope: sync-upstream.yml sync(uses secrets.*), ci.yml semantic-pr, release.yml release
0 inline high-risk expansion(s) flagged

Negative control, because a guard that has never failed has not been tested. Reintroducing the inline expression:

FAILURE: inline high-risk context expansions found in run: script bodies:
  [sync-upstream.yml] job=sync, step='Report unresolved conflict': ${{ steps.merge.outputs.files }}

4. Dead references. All 7 now point at a commit-pinned permalink to the external design doc. Porting the spec would have imported 432 lines of another project's document, most of it jira-cli specific, so I took the permalink option you offered. Both cited section headings were checked to exist at that commit.

$ git grep -c 'docs/specs/fork-friendly-release-ops.md' HEAD -- .github scripts   # 7
$ git grep 'docs/specs/...' HEAD | grep -vc 'jira-cli/blob'                       # 0

5. Deduplication. You were right that drift had already started. The stable-sign block and the backfill sign job were byte-identical (diff returns nothing), and alpha-sign differed only by the binary prefix and one comment. Both are now scripts/sign-and-notarize.sh and scripts/update-homebrew-formula.sh, with set -euo pipefail in each. Workflows lose 437 lines and gain 107; the scripts add 266.

One deviation from your suggestion worth flagging. The signing script takes subcommands (import-certs, sign-binaries, package, notarize, verify, checksums) rather than running end to end, so each phase stays its own workflow step and its secrets stay scoped to that step. A single entry point or composite action would put the notarization credentials in scope for signing and the signing identity in scope for notarization, which trades away more than the duplication is worth. If you would rather have one composite action anyway, say so and I will change it.

Two checks on the refactor, since this is the part where a silent mistake is expensive: the step sequence is identical before and after in both workflows, and per-step env: keys are identical apart from one added COMMIT_SHA. So no secret gained or lost scope. shellcheck -S warning is clean on both new scripts.

Minors. Release notes are built with printf so continuation lines carry no YAML indentation. sync-upstream.yml has its trailing newline. The dtolnay/rust-toolchain pin moves onto the SHA #451 uses (2c7215f1, a real commit on that repo's master), so whichever of the two lands first, the other needs no reconciliation.

Not done: egress-policy: block. Block mode grades against a per-repo allowlist, and these jobs have only ever run in audit mode, so there is no detections history to derive one from. Turning it on blind means the signing jobs start failing on an unrelated day for an endpoint nobody wrote down. Happy to do it as a follow-up once a few audit-mode runs have produced the endpoint list, if you want it.

Fast-follows 6 and 7 I have left alone as you scoped them, but I am glad to take either in a separate PR rather than growing this one.

https://claude.ai/code/session_01YV58wMepvhR6qNYZhwPJkV

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