Skip to content

fix(release): replace goreleaser pro-only prebuilt builder with OSS-compatible shape#258

Merged
trilamsr merged 2 commits into
mainfrom
fix/goreleaser-oss-compat
Jun 1, 2026
Merged

fix(release): replace goreleaser pro-only prebuilt builder with OSS-compatible shape#258
trilamsr merged 2 commits into
mainfrom
fix/goreleaser-oss-compat

Conversation

@trilamsr

@trilamsr trilamsr commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Summary

The v0.2.0 release pipeline failed at the Run goreleaser step with:

yaml: unmarshal errors:
  line 63: field prebuilt not found in type config.Build

Root cause: .goreleaser.yaml used builder: prebuilt to consume OCB's per-platform output, but prebuilt is a GoReleaser Pro-only feature ("This feature is exclusively available with GoReleaser Pro"). The OSS goreleaser-action installed by the workflow (distribution: goreleaser, version: ~> v2 → v2.16) cannot parse the field, so the YAML unmarshal fails before any build runs. RFC-0013 PR-A2 assumed OSS support; it does not have it.

Chosen fix (Approach C — drop goreleaser entirely):

  • Delete .goreleaser.yaml.
  • Rename the goreleaser job to package and inline the build → archive → checksum logic in shell. make build per-arch (linux/{amd64,arm64}) writes ./_build/tracecore; we stage it alongside LICENSE + README.md, tar with --sort=name --owner=0 --group=0 --numeric-owner --mtime=@$SOURCE_DATE_EPOCH | gzip -n, then append a sha256sum line to dist/checksums.txt.
  • Generate per-archive CycloneDX SBOMs by calling syft <archive> -o cyclonedx-json=<archive>.sbom.cdx.json directly (same engine the goreleaser sboms: stanza shelled out to).
  • Create the GitHub Release via gh release create "$TAG" --title "$TAG" --generate-notes [--prerelease] dist/*.tar.gz dist/*.sbom.cdx.json dist/checksums.txt.

Tradeoffs:

  • We lose goreleaser's grouped changelog (Features / Bug fixes / Performance / Other regexes). --generate-notes gives a flat commit list — good enough for routine releases, and operators can hand-edit via gh release edit "$TAG" --notes-file … for marquee releases.
  • We give up mod_timestamp: "{{ .CommitTimestamp }}" templating, but the same SOURCE_DATE_EPOCH is now passed to tar --mtime directly so byte-reproducibility against the tag commit is preserved.
  • Fewer moving parts: no goreleaser binary install, no goreleaser-action SHA pin to maintain, no ~> v2 floating-version risk, no Pro-vs-OSS field-compatibility footgun.

The downstream sbom, sign, provenance, attest, and ko-publish jobs are unchanged aside from the needs: job-name rename (goreleaserpackage) and the matching needs.<job>.outputs.{tag,hashes} references. SBOM / cosign-keyless / SLSA v1.0 / attest-build-provenance chain is preserved end-to-end.

Considered and rejected:

  • Option A (foreign-binary via archives.files): still requires goreleaser to know about each binary's existence; without prebuilt, there's no OSS-compatible way to tell goreleaser "package this file as an archive without compiling anything".
  • Option B (no-op Go shim + hooks.pre make build): fragile, ugly, leaves a fake main.go in the tree just to satisfy goreleaser's default builder.

Test plan

Ran locally:

  • actionlint .github/workflows/release.yml — clean.
  • make check — green (gofumpt, tidy-check, golangci-lint, go vet, go mod verify).
  • Grep swept for stale goreleaser/needs.goreleaser references — only intentional historical comments remain.

Will be exercised on the v0.2.1 tag push after merge:

  • package job builds linux/{amd64,arm64} OCB binaries, archives + checksums, creates the GitHub Release with --generate-notes.
  • sbom (source-tree CycloneDX) uploads to the release.
  • sign (cosign-keyless) signs + verifies tar.gz + checksums.txt; uploads bundles.
  • provenance (SLSA v1.0 reusable workflow) attaches provenance to the release with upload-tag-name: $TAG.
  • attest (GitHub actions/attest-build-provenance) attests each tar.gz.
  • ko-publish builds + pushes ghcr.io/tracecoreai/tracecore:0.2.1 + :latest, cosign-signs by digest, attests image.

Reproducible-archive sanity (post-merge, on the runner): two consecutive make build + tar invocations against the same SOURCE_DATE_EPOCH should produce byte-identical .tar.gz files (operator can spot-check via sha256sum in the workflow logs).

Followups

  • Operator must cut v0.2.1 after this merges. That is the first installable release. Suggested:
    git tag -s v0.2.1 -m 'v0.2.1: first installable release (goreleaser-pro fix)'
    git push origin v0.2.1
  • v0.2.0 tag handling: deletion is blocked by branch-protection rules, so the tag stays on origin as a "failed release" marker. No GitHub Release object was ever created against it, so gh release view v0.2.0 will continue to 404 — operators landing on the tag should be steered to v0.2.1. (Alternative: a maintainer with admin override can delete v0.2.0 if the marker is undesirable; not blocking.)
  • Re-running the failed v0.2.0 workflow is intentionally NOT a fix path — even with this PR merged, the v0.2.0 tag points at commit 0025969 which still carries the old .goreleaser.yaml. The fix only takes effect for tags pushed after this PR merges to main.
  • Doc rot: docs/notes/reproducibility.md and docs/reproducibility.md reference .github/workflows/goreleaser.yml (a path that has never existed in this repo — workflow is release.yml) and describe goreleaser as the build engine. Out of scope here; tracked separately.
fix(release): replace GoReleaser Pro-only `prebuilt` builder with an OSS-compatible shell-based packaging job. Drops `.goreleaser.yaml`; binary archive + checksum + per-archive SBOM are now produced by inline shell + syft, and the GitHub Release is created via `gh release create --generate-notes`. SBOM / cosign-keyless / SLSA v1.0 / GitHub attest-build-provenance chain is preserved end-to-end.

Reviewer A+ deltas applied (commit 47e4f32)

Per fresh-context review:

  • HIGH — fixed tar archive wrap-in-directory regression. Previous tar -C dist/stage -cf - "tracecore_${ver}_linux_${arch}" wrapped contents in a versioned subdir. goreleaser v2's wrap_in_directory defaults to false, and the deleted .goreleaser.yaml never set it, so the prior release shape emitted ./tracecore at archive root. Now: tar from inside the staging dir with . so contents land at archive root. Preserves curl … | tar xz && ./tracecore operator muscle memory.
  • MEDIUMgh release create made idempotent. On workflow retries (transient gh API failure, SLSA-reusable manual rerun-all-jobs), the previous shape exited non-zero. Now: gh release view as guard + gh release upload --clobber for assets, mirroring the sign job's bundle pattern.
  • LOW deltas declined (per [[no-bloat]] / scope discipline): SOURCE_DATE_EPOCH already wired via Makefile mod_timestamp; SLSA reusable v2.1→v2.2 bump deferred to separate PR; defense-in-depth nullglob count guard not load-bearing (upload-artifact if-no-files-found: error catches upstream).

The v0.2.0 release pipeline failed with:

  yaml: unmarshal errors:
    line 63: field prebuilt not found in type config.Build

Root cause: .goreleaser.yaml used `builder: prebuilt` to consume OCB's
per-platform output, but `prebuilt` is a GoReleaser Pro-only feature.
The OSS goreleaser-action installed by the workflow cannot parse it.

Fix: drop goreleaser entirely. The `package` job (renamed from
`goreleaser`) now builds + tar.gz-archives + sha256-checksums the OCB
binaries with inline shell, generates per-archive CycloneDX SBOMs via
syft directly, and creates the GitHub Release via `gh release create
--generate-notes`. Same determinism contract (SOURCE_DATE_EPOCH from
tag commit + numeric-owner + --sort=name + gzip -n + tag-pinned
TRACECORE_VERSION). Same dist/ artifact shape, so downstream sbom /
sign / provenance / attest / ko-publish jobs are unchanged aside from
the `needs:` job-name rename.

The SBOM (anchore/syft) / cosign-keyless / SLSA v1.0 / GitHub
attest-build-provenance chain is preserved end-to-end.

v0.2.0 git tag remains on origin as a "failed release" marker
(deletion blocked by branch-protection). Operator cuts v0.2.1
post-merge to land the first installable release.

Signed-off-by: Tri Lam <tri@maydow.com>
Per fresh-context review of #258:

HIGH — tar shape regression vs goreleaser default. Previous shape
`tar -C dist/stage -cf - tracecore_${ver}_${os}_${arch}` wrapped
contents in a versioned subdir. goreleaser v2's wrap_in_directory
defaults to false; deleted .goreleaser.yaml never set it, so the
prior path emitted ./tracecore at root. `curl … | tar xz &&
./tracecore` operator muscle memory now restored: tar from inside
the staging dir with `.` so contents land at archive root.

MEDIUM — gh release create is not idempotent. On any retry of the
workflow against a tag whose release already exists (transient gh
API hiccup, SLSA-reusable failure followed by manual rerun-all-jobs),
the previous shape exits non-zero and blocks the downstream chain.
goreleaser had `mode: append` semantics. Now: gh release view as a
guard, gh release upload --clobber for assets — matches the sign
job's bundle-upload pattern.

LOW deltas declined: SOURCE_DATE_EPOCH already wired via Makefile
mod_timestamp; SLSA reusable v2.1.0→v2.2.0 bump is separate PR;
defense-in-depth nullglob count guard not load-bearing (upload-artifact
if-no-files-found:error catches it upstream).

Signed-off-by: Tri Lam <tri@maydow.com>
@trilamsr trilamsr enabled auto-merge (squash) June 1, 2026 03:17
@trilamsr trilamsr merged commit 40e898b into main Jun 1, 2026
12 checks passed
@trilamsr trilamsr deleted the fix/goreleaser-oss-compat branch June 1, 2026 03:25
trilamsr added a commit that referenced this pull request Jun 1, 2026
…259) (#263)

## Summary

Three concern-isolated doc-rot reconciles, one commit per issue:

- **#256** — `components/receivers/pyspy/README.md` + `RUNBOOK.md`
reframe the stale 2026-05-22 "Scheduled for deletion at v0.3.0" banner
to "deferred to v0.4.0+ per
[#222](#222)" with the
OTel-Profiles-→-Beta + parca-agent-OTLP-export re-evaluation triggers
named verbatim.
- **#257** — `docs/rfcs/0013-distro-first-pivot.md` adds deferral
banners at §4 Migration timeline (v0.3.0 row) and §Migration/rollout
v0.3.0 sequencing; annotates the §7 Deletion list rows + PR-M sequencing
entry with explicit `(deferred to v0.4.0+ per #222)` markers. Historical
sequencing preserved verbatim (option 1 from the issue).
- **#259** — `docs/reproducibility.md` + `docs/notes/reproducibility.md`
reconciled against post-PR-#258 inline-shell `package` job: drop all
`.goreleaser.yaml` / `.goreleaser.yml` / `goreleaser.yml`
signer-workflow references; reframe build narrative around
`SOURCE_DATE_EPOCH` + `tar --mtime` + `--sort=name` + `gzip -n`
deterministic-byte controls; update download patterns + verification
commands to operate on tar.gz archives; add an explicit `sha256sum -c
checksums.txt` step so the operator integrity chain (sha256sum →
checksums.txt → cosign bundle) is named in the walkthrough. Cosign
bundle naming unchanged.

## Root causes

- **#256**: banner predates the PR-M deferral memo in #222; PR #254
reconciled `docs/migration/v0.2-to-v0.3.md` but explicitly left the
receiver READMEs out of scope.
- **#257**: PR #254's References section flagged the RFC body with a
"supersede with #222" note but the body itself was untouched.
- **#259**: PR #258 (goreleaser → inline shell) deletes
`.goreleaser.yaml`; the reproducibility docs additionally referenced a
never-existed `.github/workflows/goreleaser.yml` signer-workflow path
(the file has always been `release.yml`), so the docs were doubly-broken
pre-#258 and singly-broken post-#258.

## Sequencing note

PR #258 is the source of truth for the post-pivot inline-shell `package`
job referenced by the #259 reconcile. #258 is currently open + green; if
#258 merges first, the doc paths are immediately accurate. If this PR
merges first, the docs describe the target state and converge once #258
lands. Either order works.

## Test plan

- [x] `bash scripts/doc-check.sh` — clean (500 markdown links resolve,
47 non-.md intra-repo links resolve, banned-phrase lint clean, all
required sections present, 10 fenced bash/sh blocks in
`docs/reproducibility.md` shell-syntax-clean).
- [x] `make check` — clean (gofumpt, tidy-check, golangci-lint 0 issues,
go vet, go mod verify).
- [x] Manual cross-link spot check: confirmed the new `#222` issue links
+ `docs/migration/v0.2-to-v0.3.md` cross-references resolve; confirmed
RFC `(deferred to v0.4.0+ per #222)` markers attached at every flagged
line (matrix row, deletion-list rows, §Migration/rollout sequencing
entry); confirmed reproducibility docs no longer mention `goreleaser`
anywhere via `grep -i goreleaser`.
- [x] Verified no in-flight conflicts with PR #254 (already-merged
migration-guide reconcile) or PR #258 (still-open inline-shell pivot —
touches `.github/workflows/release.yml` + deletes `.goreleaser.yaml`, no
overlap with this PR's files).

## Out of scope / follow-ups

No new doc-rot noticed beyond the three issues addressed here.

```release-notes
docs: reconcile three stale references post-v0.2.0: pyspy README + RUNBOOK + RFC-0013 §Migration now correctly reflect that PR-M (parca-agent recipe + pyspy delete) is deferred to v0.4.0+ pending OTel Profiles → Beta + parca-agent OTLP export (#222); reproducibility docs (docs/reproducibility.md + docs/notes/reproducibility.md) updated for the post-#258 inline-shell `package` job — operator-facing integrity chain (sha256sum → checksums.txt → cosign bundle → SLSA attestation) preserved end-to-end.
```

Closes #256.
Closes #257.
Closes #259.

---------

Signed-off-by: Tri Lam <tri@maydow.com>
Co-authored-by: Tri Lam <tri@maydow.com>
trilamsr added a commit that referenced this pull request Jun 1, 2026
## Summary

Codify the release gates per tier (patch / minor / RC / GA) into a
single operator-readable doc. Each tier names what must be done BEFORE
the release-prep PR opens.

- Universal gates: `make check` / `make test` / chart-appversion
lockstep / adversarial review / no-AI-footer audit
- Patch + minor: doc-check + validator-recipe + migration-guide row +
bench gate
- RC: ≥12 NORTHSTAR patterns, schema v1.0 RC, soft-locked attrs,
deprecation policy, support matrix, compat CI, SLO declared, threat
model, ref architectures, production-preset Helm, upgrade guide, SDKs
- GA: third-party audit, real-operator integration, externally-verified
reproducibility, attrs hard-locked, SLOs binding, launch artifacts

Tag-cut steps map to the inline-shell release pipeline shipped via #258
(no goreleaser). Branch-protection caveats note the tag-deletion block.

## Test plan

- [x] `bash scripts/doc-check.sh` clean
- [x] `make check` clean
- [ ] CI gates (verify-test, verify-lint, validator-recipe, build,
install-bench)

```release-notes
docs(release): RELEASE-CHECKLIST.md formalizes patch / minor / RC / GA release gates
```

Signed-off-by: Tri Lam <tri@maydow.com>
Co-authored-by: Tri Lam <tri@maydow.com>
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