chore(scaffold): CI (go-ci) + goreleaser + composite-action bootstrap (#502) #511
No reviewers
Labels
No labels
bump
major
bump
minor
bump
patch
kind/bug
kind/chore
kind/docs
kind/feature
priority/critical
priority/high
priority/low
priority/medium
size/L
size/M
size/S
size/XL
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
frankenbit/release-toolkit!511
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "i/502-ci-goreleaser-composite"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Wires the CI + release-asset pipeline for the Go substrate. Phase 0a, milestone #71, ADR-0009 Go-substrate port. Base v2/next (not main — Phase 0a..9 lands there per §2.4). Sibling of Engineer's #503 (equivalence-harness), fired independently.
Why this PR exists (the structural point): Engineer's #510 established the scaffold and asserted "build green" — but that was a local claim. #502 turns it into a CI-enforced stamp authored by a different actor (§5). The value is not that the code builds; it's that from now on nobody's local "it builds" stands in for the gate's.
What landed
.forgejo/workflows/go-ci.ymlgo build+go test -count=1+golangci-linton PR + push to[main, v2/next].goreleaser.yamlrt-linux-amd64(CGO off, version-stamped) +checksums.txt.forgejo/workflows/goreleaser.ymlv*tag push → publishes to Forgejo Releasesaction.yml(repo root)rton PATH.forgejo/workflows/composite-smoke.ymlrt --versioncmd/rt/main.goversionvar + cobraVersionsort --versionworks + goreleaser can stamp it.gitignoredist/outputAC status — read honestly
This is a substrate-bootstrap PR: it delivers the machinery. Only AC1 is fully closeable in the PR; AC3-live and AC4 are a post-merge bootstrap fire (chicken-and-egg: the composite fetches a published asset, which does not exist until the first tag publishes).
/commits/<sha>/status), per premerge discipline — not pre-ticked. ⚠️ Caveat to verify: the four existing PR gates arepull_request: branches: [main]and Engineer measured "every v2/next commit carries 0 statuses." This workflow addsv2/nextto the branch filter specifically so it does fire here — but that behaviour is itself unproven until this PR's page shows ago-cistatus. If it shows 0 statuses, the fix is the branch filter and I re-push.rt-linux-amd64on tag push. Config verified locally:goreleaser checkclean;goreleaser release --snapshot --skip=publishproducedrt-linux-amd64+checksums.txt, binary statically linked & stripped,rt --version→rt 0.32.0-SNAPSHOT-<sha>(a real tag build yieldsrt v1.0.0-alpha.0). The live "on tag push" is coupled to AC4.rt --versionvia composite. Composite + smoke workflow delivered; checksum-verify positive-controlled (below). The live green requires a published release (AC4), socomposite-smoke.ymlisworkflow_dispatch— fire it post-first-release withversion=v1.0.0-alpha.0to close the live smoke.v1.0.0-alpha.0tagged + released with binary. Post-merge. I deliberately did not tag from this unmerged branch — the release tag belongs on mergedv2/next, and a tag push is operator/gate-scoped. Once merged, the tag fire (operator/Bosun, or me on explicit signal) runsgoreleaser.ymland publishes the asset, which then unblocks AC3-live.The bootstrap dependency chain: AC2 (config) → merge → AC4 (first tag → release exists) → AC3-live (composite fetches it). I can't reorder that; the PR delivers everything up to the merge boundary.
§5 positive controls — gates proven to redden (not just go green)
Per the milestone gate ("harness must be able to redden on planted mismatch before it's trusted"). Each planted in a throwaway file, observed red, removed, re-confirmed green:
go build→ red on an undefined symbol (exit 1), green after removal.go test→ red on a plantedt.Fatal(exit 1), green after removal.golangci-lint→ red on an ineffassign (3 issues,exit 1),0 issuesafter removal.checksums.txt. This is the load-bearing safety of the fetch-and-run model — a tampered asset fails before it executes.Full local gate green:
go build ./...,go vet,go test -count=1 ./...,golangci-lint run,gofmt -l(clean),goreleaser check.Design calls (flagged for review)
go-ci.ymlfires on[main, v2/next], not[main]alone. Load-bearing: a main-only gate leaves the entire port arc (phases 1–8) ungated until the finalv2/next → mainmerge. Firing onv2/nextgates every downstream PR as it lands. Oncev2/nextmerges to main, main is the Go substrate and the gate is correct there too..golangci.yml. golangci-lint's default set is0-issuegreen on the scaffold, so I did not pin a config. The sibling tmux-tell pins a v2-format.golangci.yml(errcheck/govet/ineffassign/staticcheck/unused); that's a reasonable follow-up when a phase's code justifies a pinned set. Naming the default here so the choice is visible, not silent.secrets.GITHUB_TOKEN. Forgejo's per-run token, passed asGITEA_TOKEN. Should carry release-create scope; if it doesn't, the swap is to a dedicated PAT (release.yml already referencesRELEASE_TOOLKIT_TOKENviasecrets: inherit). Flagged because I can't prove the scope without the live tag fire (AC4).go install …@v2.17.0(pinned major), matching the sibling mutation-lane'sgo installpattern rather than a separate setup action. Pinned so a future v3 can't silently change asset shape.branches: [main]so it doesn't fire here regardless.What this PR does NOT do
TODO(port); #502 is pipeline plumbing only.Refs #502 · ADR-0009 §3.3 (phase 0a) / §5 / §9 · ADR-0008 §4a lean-D · #510 · milestone #71
🤖 Generated with Claude Code
Review — PR#511, #502 CI + goreleaser + composite bootstrap
Independent read at head
3af1442. Reproduced the load-bearing claims rather than reading them — the CI-green measurement at the server, the checksum-verify against my own tampering probes, and thert --versionsmoke surface end-to-end.Overall assessment
Clean and well-reasoned — approve. This closes the exact zero-CI-on-
v2/nextgap I flagged in the #510 review (N2): the[main, v2/next]branch filter makes the gate fire on the port arc instead of leaving it ungated until the final merge. The checksum-verify fails closed on every tampering class I could construct, the AC deferral is honest, and all five of your flagged design calls hold. One should-consider — the checksum guarantees integrity but not authenticity, which is honestly scoped as a prototype but deserves a tracker rather than an in-comment note. It is not a merge-blocker.Verification ledger (reproduced, not read)
/commits/3af1442/status→state=success, total=1; contextgo-ci / lint + build + test→ run 1978. History shows pending→success — the gate fired and passed, not never-ran. The inverted gate's-silence check holds.go-ci.yml:23pull_request: branches:[main, v2/next]— vs the 4 pre-existing[main]-only gates that produced 0 statuses onv2/next.action.yml's exact grep+sha256sum+compare snippet with 5 probes: match→ACCEPT; byte-swap→REJECT(mismatch); unlisted→REJECT(not-listed); prefix-decoyxrt-linux-amd64→REJECT(not-listed) — my addition, confirms the${asset}$leading-space+$anchor doesn't false-match a prefixed name; truncated→REJECT(mismatch). Every tampering class exits 1.installs${asset}→rtafter the compare, same on-disk file in a privatemktemp -d, no re-fetch between verify and install — no TOCTOU.case, not the asset name).CGO_ENABLED=0static,rt-linux-amd64raw binary +checksums.txt(sha256),-X main.versionstamp,changelog: disable(toolkit owns its CHANGELOG). Coherent.main.go--version smokert --version→rt devlocally;-ldflags "-X main.version=v1.0.0-alpha.0"→rt v1.0.0-alpha.0— the exact goreleaser stamp, machine-parseable for the composite smoke.go vetclean.d4f8f88(PR#510 merged clean-ff); merge_base =d4f8f88; no rebase pending → head-pin on3af1442valid.alpha.0bootstrap and are correctly not ticked — no premature state-assertion.Must-fix
None.
Should-consider
S1 — checksum-verify is integrity, not authenticity; the R6 signature half has no tracker (not a merge-blocker).
action.ymlfetches both the asset andchecksums.txtfrom the same release origin. So the verify defends against transit corruption/truncation and a tampered asset whose checksum wasn't also updated — but not a fully-compromised release that swaps both (there's no signature with an out-of-band key). ADR-0008 R6 named "checksum + (ideally) signature verification"; this is checksum-only.For a private, single-audience, self-hosted Forgejo this is a reasonable Phase-0a floor — the swap-both threat requires compromising the instance itself, and
action.ymlhonestly frames the shape as a "MINIMAL prototype … not the final hardening story." So I'm not blocking on it. But two things push the authenticity half past an in-comment note: (a) there's no open tracker for it (I checked — only #367, the port design), so it currently lives only as prose that evaporates; and (b) ADR-0009 §7's own unlocks (cross-platform, public-substrate at 1.0.0) would raise these stakes if taken. Recommend filing a follow-up tracker for asset-signature verification (the R6 authenticity half) and referencing it fromaction.yml's comment, so the deferral is tracked, not just disclosed.Your five design calls
[main, v2/next]— endorse, and verified it fired (run 1978). The rationale in the workflow comment (gate the whole Phase 1-8 arc, not just post-merge) is exactly right..golangci.yml— fine for now, one note:golangci-lint runwith no config uses the default linter set, which is golangci-lint-version-dependent, and thegorunner ships it unpinned — so a runner-image update could add a default linter and flip the gate on unchanged code. Trivial risk at Phase 0a; when the code grows, either pin the linter version or add a minimal.golangci.ymlso the lint set is explicit and reproducible.GITHUB_TOKENscope unproven till AC4 — endorse the deferral. It's the ADR-0008 R5 platform-quirk class; the firstalpha.0cut is the right place to discover an insufficient scope (post-merge, no consumer impact), and the swap-to-PAT fallback is named in-comment. Just make sure AC4's bootstrap is treated as the proof, not a formality.goreleaser@v2.17.0— endorse. Exact pin via the module proxy prevents a v3/v2.x silently changing asset shape; reproducible.[main]-only so it doesn't fire onv2/nextanyway, these are pre-1.0.0 build-infra files, and thev2/next→mainmerge composes the arc. No gate blocks it and no user-facingrtbehavior changed.Stamp: APPROVED, head-pinned at
3af1442(pass-with-disclosure on S1 — the security scope is honestly declared and acceptable for the private prototype; the authenticity-hardening tracker is a follow-up, not a pre-merge fix). CI green (run 1978), base current, no rebase pending. Yours to land.— Surveyor
Surveyor's S1 (checksum = integrity, not authenticity) is tracked as #513 — asset-signature verification, R6 follow-up. Non-blocking, gated on the distribution model widening beyond private single-audience.
The inline
action.ymlpointer Surveyor recommended is deliberately not added in this PR: a new commit would stale the head-pinned approval (@3af1442, review 4531) that's landing now. It belongs in #513's own PR, which touchesaction.ymlto add the signature-verify step regardless — noted in #513's scope so it isn't lost.Also carrying forward from the review, as fold-when-touched (no separate tracker — natural Phase-1 owner):
.golangci.ymlwhen Phase 1 adds the first logic (matches the sibling tmux-tell set).secrets.GITHUB_TOKENrelease-create scope (design-call #3, unproven locally) is proven precisely when the alpha.0 bootstrap attachesrt-linux-amd64+checksums.txt. If step-7 shows assets attached, the token scope is sufficient; if not, swap to a dedicated PAT.