From 49fa4908a2bb17fd4de85397a47d6a0c303fee75 Mon Sep 17 00:00:00 2001 From: Tri Lam Date: Sun, 31 May 2026 20:05:24 -0700 Subject: [PATCH 1/2] fix(release): replace pro-only prebuilt builder with OSS shape 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 --- .github/workflows/release.yml | 229 ++++++++++++++++++++++------------ .goreleaser.yaml | 143 --------------------- 2 files changed, 151 insertions(+), 221 deletions(-) delete mode 100644 .goreleaser.yaml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 809d5101..18c8bb4b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,16 +1,25 @@ # Release pipeline — RFC-0013 PR-C rewrite (+ PR-D image build via ko). # # Replaces the bespoke release workflow that previously lived here -# (preserved in git history). Build + archive + checksum are delegated -# to goreleaser via .goreleaser.yaml at the repo root; SBOM / signing / -# SLSA provenance / GitHub attestation are layered on top using OpenSSF / +# (preserved in git history). Binary archive + checksum are built by +# inline shell (the `package` job below); SBOM / signing / SLSA +# provenance / GitHub attestation are layered on top using OpenSSF / # Sigstore-blessed actions. Container image build via ko per RFC-0013 # §migration PR-D (replaces the deleted root Dockerfile + # docker/build-push-action stanza from the prior workflow). # +# Historical note (2026-05-31): the package job previously delegated +# build+archive+checksum to goreleaser via .goreleaser.yaml at the +# repo root. That config used `builder: prebuilt` to consume OCB's +# per-platform output — but `prebuilt` is GoReleaser Pro-only, and +# the OSS goreleaser-action installed here cannot parse it. The +# config now lives in shell: simpler, fewer moving parts, and avoids +# either a paid licence or a fragile no-op-shim workaround. The SBOM +# / cosign / SLSA / attest chain is unchanged. +# # Stack (per RFC-0013 §Adoption Matrix + §References): -# - goreleaser-action → binary build, archive, checksums -# - anchore/sbom-action → CycloneDX SBOM (in-repo, post-build) +# - shell tar.gz + sha256sum → binary build, archive, checksums +# - anchore/sbom-action → CycloneDX SBOM (per-archive + repo-wide) # - sigstore/cosign-installer → keyless signing of release blobs # + image manifests # - ko-build/setup-ko → multi-arch image build/publish @@ -42,24 +51,34 @@ permissions: jobs: # --------------------------------------------------------------------------- - # goreleaser: build linux/{amd64,arm64} binaries, tar.gz archives, sha256 - # checksums, and per-archive CycloneDX SBOMs (via goreleaser's `sboms:` - # stanza which shells out to syft — same engine as anchore/sbom-action). - # Produces `dist/` artifacts that downstream jobs consume. + # package: build linux/{amd64,arm64} binaries via OCB (`make build`), + # tar.gz archive each, emit a single sha256 checksums.txt, generate a + # per-archive CycloneDX SBOM with syft, and publish the GitHub Release + # carrying all of the above. Produces `dist/` artifacts that downstream + # jobs consume. + # + # Determinism contract (matches the prior goreleaser-driven path): + # - SOURCE_DATE_EPOCH is the tag commit's UNIX timestamp; tar uses + # `--mtime=@${SOURCE_DATE_EPOCH}` + `--sort=name` + numeric-owner so + # the archive bytes are reproducible across rebuilds. + # - `make build` injects TRACECORE_VERSION=$TAG into dist.version so + # the in-binary version matches the tag verbatim. + # - OCB inherits -trimpath via builder-config.yaml defaults and CGO + # stays disabled (CGO_ENABLED=0 on the env line below). # --------------------------------------------------------------------------- - goreleaser: - name: goreleaser (build + archive + sbom) + package: + name: package (build + archive + sbom + release) runs-on: ubuntu-latest timeout-minutes: 20 permissions: - contents: write # goreleaser publishes to GitHub Releases on tag push + contents: write # gh release create/upload on tag push outputs: hashes: ${{ steps.hashes.outputs.value }} tag: ${{ steps.tag.outputs.value }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - fetch-depth: 0 # goreleaser needs full history for changelog + git describe + fetch-depth: 0 # full history for `git describe` + auto-generated notes - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: @@ -77,66 +96,119 @@ jobs: fi echo "value=$tag" >> "$GITHUB_OUTPUT" - - name: Install syft (for goreleaser sboms stanza) - # anchore/sbom-action ships syft, but the goreleaser sboms: stanza - # invokes the `syft` binary directly. Install it on PATH so the - # build pass produces SBOMs in dist/ alongside the archives. + - name: Install syft (for per-archive SBOMs) + # anchore/sbom-action ships syft; the `download-syft` sub-action + # installs the binary on PATH without invoking it, so we can call + # `syft ` ourselves per-archive below. uses: anchore/sbom-action/download-syft@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0 - - name: Pre-build OCB binaries for goreleaser prebuilt builder - # RFC-0013 PR-A2: .goreleaser.yaml uses `builder: prebuilt` - # against ./_build/{Os}-{Arch}/tracecore. We loop over the - # (linux, {amd64, arm64}) matrix, regenerating the OCB - # submodule per-platform (each `make build` writes a fresh - # ./_build/) and parking the resulting binary at the path - # goreleaser expects. SOURCE_DATE_EPOCH from the tag commit - # keeps mtimes reproducible across this loop. + - name: Build + archive OCB binaries + # OCB writes ./_build/tracecore per `make build` invocation. We + # loop over the (linux, {amd64, arm64}) matrix, regenerating the + # OCB submodule per-platform, archiving each binary alongside + # LICENSE + README.md, and accumulating a single checksums.txt + # in dist/. env: TAG: ${{ steps.tag.outputs.value }} run: | set -euo pipefail SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct "${TAG}") export SOURCE_DATE_EPOCH - # Pin the version that `make build` injects into the binary to - # the release tag, independent of git-describe state. Avoids a + # Pin the version `make build` injects into the binary to the + # release tag, independent of git-describe state. Avoids a # shallow-clone footgun and binds the artefact to the published # tag name verbatim (e.g. v0.2.0, not v0.1.0-m1-188-gabc1234). export TRACECORE_VERSION="${TAG}" + version="${TAG#v}" + + mkdir -p dist + : > dist/checksums.txt + for arch in amd64 arm64; do rm -rf ./_build - CGO_ENABLED=0 GOOS=linux GOARCH=$arch make build - mkdir -p "./_build/linux-${arch}" - mv ./_build/tracecore "./_build/linux-${arch}/tracecore" + CGO_ENABLED=0 GOOS=linux GOARCH="${arch}" make build + + stage="dist/stage/tracecore_${version}_linux_${arch}" + mkdir -p "${stage}" + cp ./_build/tracecore "${stage}/tracecore" + cp LICENSE "${stage}/LICENSE" + cp README.md "${stage}/README.md" + + archive="tracecore_${version}_linux_${arch}.tar.gz" + # --sort=name + numeric-owner + --mtime + gzip -n strip every + # nondeterministic byte (file order, owner names, mtimes, + # gzip header timestamp). Result is byte-reproducible against + # SOURCE_DATE_EPOCH. + tar \ + --sort=name \ + --owner=0 --group=0 --numeric-owner \ + --mtime="@${SOURCE_DATE_EPOCH}" \ + -C dist/stage \ + -cf - "tracecore_${version}_linux_${arch}" \ + | gzip -n > "dist/${archive}" + + ( cd dist && sha256sum "${archive}" >> checksums.txt ) done - - name: Run goreleaser - id: goreleaser - uses: goreleaser/goreleaser-action@5daf1e915a5f0af01ddbcd89a43b8061ff4f1a89 # v7.2.2 - with: - distribution: goreleaser - version: "~> v2" - args: release --clean - env: - # goreleaser uses this for the GitHub Release creation step. - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # Honour the tag commit's timestamp so `mod_timestamp: - # "{{ .CommitTimestamp }}"` in .goreleaser.yaml yields a - # deterministic build (matches Makefile lines 36-39 + - # the prior release.yml's SOURCE_DATE_EPOCH stanza). - GORELEASER_CURRENT_TAG: ${{ steps.tag.outputs.value }} + # Repo's checksum convention is ` ` lines, + # filenames bare (no ./ prefix) — sha256sum already produces + # that shape. Surface the result for log debugging. + cat dist/checksums.txt + + rm -rf dist/stage + + - name: Generate per-archive CycloneDX SBOMs (syft) + # One SBOM per tar.gz alongside the archive (`.sbom.cdx.json`) + # so verifiers can pair binary↔SBOM by filename. Matches the + # pattern the prior goreleaser sboms: stanza emitted. + run: | + set -euo pipefail + shopt -s nullglob + for f in dist/*.tar.gz; do + syft "${f}" -o "cyclonedx-json=${f}.sbom.cdx.json" + done - name: Compute base64-encoded sha256 hashes for SLSA provenance id: hashes # The SLSA generic generator expects a base64-encoded list of - # ` ` lines (the contents of the goreleaser - # checksums.txt file, encoded). This becomes the `subjects` input - # to the reusable workflow below. + # ` ` lines (the contents of checksums.txt, + # encoded). This becomes the `subjects` input to the reusable + # workflow below. run: | set -euo pipefail cd dist hashes=$(base64 -w0 < checksums.txt) echo "value=$hashes" >> "$GITHUB_OUTPUT" + - name: Create GitHub Release + # Single source of truth for the GitHub Release object. The + # downstream `sbom`, `sign`, `provenance`, `attest`, and + # `ko-publish` jobs upload their own assets to this release via + # `gh release upload` or the SLSA reusable workflow's + # `upload-tag-name` input. + env: + GH_TOKEN: ${{ github.token }} + TAG: ${{ steps.tag.outputs.value }} + run: | + set -euo pipefail + # Pre-release detection mirrors the prior goreleaser + # `prerelease: auto` behaviour: anything with a `-` in the + # SemVer pre-release field (e.g. v0.2.0-rc1) is flagged. + prerelease_flag="" + if [[ "${TAG}" == *-* ]]; then + prerelease_flag="--prerelease" + fi + # `--generate-notes` asks GitHub to auto-fill release notes + # from commits since the previous tag — replaces the + # goreleaser changelog: stanza without per-section grouping. + # Operators may hand-edit `gh release edit "$TAG" --notes-file + # ...` post-merge for marquee releases. + gh release create "${TAG}" \ + --title "${TAG}" \ + --generate-notes \ + ${prerelease_flag} \ + dist/*.tar.gz dist/*.sbom.cdx.json dist/checksums.txt + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: dist @@ -149,37 +221,37 @@ jobs: # --------------------------------------------------------------------------- # sbom: re-run anchore/sbom-action on the source tree to publish a - # canonical, repo-level SBOM alongside the per-archive SBOMs that - # goreleaser emits. Belt + suspenders: per-archive SBOMs are pinned to - # the binary contents; the source-tree SBOM is what `gh release view` + # canonical, repo-level SBOM alongside the per-archive SBOMs that the + # `package` job emits. Belt + suspenders: per-archive SBOMs are pinned + # to the binary contents; the source-tree SBOM is what `gh release view` # consumers expect as a single discoverable artifact. # --------------------------------------------------------------------------- sbom: name: sbom (anchore/syft, source-tree) runs-on: ubuntu-latest - needs: goreleaser + needs: package timeout-minutes: 10 permissions: contents: write # uploads SBOM to the existing GitHub Release steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - # Pin by commit SHA so a force-push to the tag between goreleaser - # and sbom cannot produce an SBOM for a different tree than the - # binaries that goreleaser already shipped. + # Pin by commit SHA so a force-push to the tag between the + # `package` job and sbom cannot produce an SBOM for a different + # tree than the binaries that already shipped. ref: ${{ github.sha }} - uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0 with: format: cyclonedx-json output-file: tracecore.source.sbom.cdx.json - # Upload to the GitHub Release created by goreleaser. + # Upload to the GitHub Release created by the `package` job. upload-release-assets: true upload-artifact: true upload-artifact-retention: 7 # --------------------------------------------------------------------------- - # sign: cosign-keyless sign-blob of each goreleaser archive + the + # sign: cosign-keyless sign-blob of each release archive + the # checksums file. Bundle (cert + signature + Rekor inclusion proof) # ships as a release asset so third-party verifiers can validate # offline. @@ -187,7 +259,7 @@ jobs: sign: name: sign (cosign keyless) runs-on: ubuntu-latest - needs: goreleaser + needs: package timeout-minutes: 10 permissions: id-token: write # OIDC token for keyless cosign @@ -218,7 +290,7 @@ jobs: - name: Verify blob signatures (smoke check) env: IDENTITY_REGEXP: "^https://github.com/${{ github.repository }}/\\.github/workflows/release\\.yml@refs/tags/" - TAG: ${{ needs.goreleaser.outputs.tag }} + TAG: ${{ needs.package.outputs.tag }} run: | set -euo pipefail shopt -s nullglob @@ -235,7 +307,7 @@ jobs: - name: Upload cosign bundles to the GitHub Release env: GH_TOKEN: ${{ github.token }} - TAG: ${{ needs.goreleaser.outputs.tag }} + TAG: ${{ needs.package.outputs.tag }} run: | set -euo pipefail shopt -s nullglob @@ -248,8 +320,9 @@ jobs: # --------------------------------------------------------------------------- # provenance: SLSA v1.0 generic provenance via the reusable workflow. - # Operates on the base64-encoded checksums.txt produced by goreleaser - # so a single provenance attestation covers every archive published. + # Operates on the base64-encoded checksums.txt produced by the + # `package` job so a single provenance attestation covers every + # archive published. # # NOTE: reusable workflow is referenced by TAG (not SHA) per SLSA's # security model — see header comment. zizmor's `unpinned-uses` audit @@ -257,7 +330,7 @@ jobs: # --------------------------------------------------------------------------- provenance: name: provenance (SLSA v1.0) - needs: goreleaser + needs: package permissions: id-token: write # OIDC for Fulcio contents: write # upload provenance to GitHub Release @@ -269,9 +342,9 @@ jobs: # §"Workflow Pinning". uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0 # zizmor: ignore[unpinned-uses] with: - base64-subjects: ${{ needs.goreleaser.outputs.hashes }} + base64-subjects: ${{ needs.package.outputs.hashes }} upload-assets: true - upload-tag-name: ${{ needs.goreleaser.outputs.tag }} + upload-tag-name: ${{ needs.package.outputs.tag }} # --------------------------------------------------------------------------- # attest: GitHub-native build-provenance attestation as a second @@ -282,7 +355,7 @@ jobs: attest: name: attest (github-native build-provenance) runs-on: ubuntu-latest - needs: goreleaser + needs: package timeout-minutes: 10 permissions: id-token: write @@ -332,7 +405,7 @@ jobs: ko-publish: name: ko-publish (image build + sign + attest) runs-on: ubuntu-latest - needs: goreleaser + needs: package timeout-minutes: 15 permissions: contents: read # checkout @@ -344,9 +417,9 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - # Pin by commit SHA so a force-push to the tag between - # goreleaser and ko-publish cannot push an image built from - # a different tree than the binaries goreleaser already + # Pin by commit SHA so a force-push to the tag between the + # `package` job and ko-publish cannot push an image built from + # a different tree than the binaries the `package` job already # signed. Mirrors the `sbom:` job's identical pin. ref: ${{ github.sha }} @@ -364,10 +437,10 @@ jobs: - name: Resolve image build environment id: env env: - TAG: ${{ needs.goreleaser.outputs.tag }} + TAG: ${{ needs.package.outputs.tag }} run: | set -euo pipefail - # Strip leading `v` so the image tag matches goreleaser's + # Strip leading `v` so the image tag matches the archive's # ${VERSION} substitution. ko publishes `:${TAG}` exactly as # passed; pre-pending `v` to the registry tag would diverge # from the existing operator-facing convention documented in @@ -376,12 +449,12 @@ jobs: version="${TAG#v}" # SOURCE_DATE_EPOCH from the tag commit timestamp so the # image layer timestamps are byte-deterministic across - # rebuilds — matches goreleaser's - # `mod_timestamp: "{{ .CommitTimestamp }}"`. + # rebuilds — matches the `package` job's + # `tar --mtime=@${SOURCE_DATE_EPOCH}` invariant. epoch=$(git log -1 --pretty=%ct "${TAG}") short_sha=$(git rev-parse --short "${TAG}") branch="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo main)" - # RFC3339 build date consistent with goreleaser's {{ .Date }}. + # RFC3339 build date for the in-image VERSION metadata. build_date=$(date -u -d "@${epoch}" +'%Y-%m-%dT%H:%M:%SZ') # Stable release => no `-` in SemVer pre-release field. tags="${version}" @@ -402,7 +475,7 @@ jobs: env: # Consumed by .ko.yaml's `builds[0].ldflags` template # substitution so the in-image binary's version vars match - # the goreleaser-published archive's version vars exactly. + # the published archive's version vars exactly. VERSION: ${{ steps.env.outputs.version }} REVISION: ${{ steps.env.outputs.short_sha }} BRANCH: ${{ steps.env.outputs.branch }} @@ -415,7 +488,7 @@ jobs: # (this job checks out by-sha, not by-tag-ref, so a bare git # describe here would resolve against the underlying commit # rather than the release tag). - TRACECORE_VERSION: ${{ needs.goreleaser.outputs.tag }} + TRACECORE_VERSION: ${{ needs.package.outputs.tag }} run: | set -euo pipefail # Regenerate the OCB submodule so ko has a `main` to compile. @@ -475,7 +548,7 @@ jobs: - name: cosign verify (smoke check) env: IDENTITY_REGEXP: "^https://github.com/${{ github.repository }}/\\.github/workflows/release\\.yml@refs/tags/" - TAG: ${{ needs.goreleaser.outputs.tag }} + TAG: ${{ needs.package.outputs.tag }} DIGEST_REF: ${{ steps.build.outputs.digest_ref }} run: | set -euo pipefail diff --git a/.goreleaser.yaml b/.goreleaser.yaml deleted file mode 100644 index 31eee54d..00000000 --- a/.goreleaser.yaml +++ /dev/null @@ -1,143 +0,0 @@ -# tracecore goreleaser config — RFC-0013 PR-C + PR-A2. -# -# Replaces the bespoke .github/workflows/release.yml (preserved in git -# history). The release pipeline now delegates binary build + archive + -# checksum to goreleaser, and wires SBOM / signing / provenance through -# goreleaser-adjacent steps in .github/workflows/release.yml. -# -# RFC-0013 PR-A2 (2026-05-30): the binary is now generated by the -# OpenTelemetry Collector Builder (`make build` → `./_build/tracecore`). -# goreleaser consumes the pre-built artefacts via builder: prebuilt, -# crossing OCB's per-GOOS/GOARCH outputs into one archive set. The -# `before:` hook runs `make build` per-platform (set via GOOS/GOARCH) -# so the OCB sub-module's go.mod hash is verified at the same go.sum -# pin the parent module uses. -# -# Determinism contract: -# - SOURCE_DATE_EPOCH honored via mod_timestamp. -# - OCB's `go build` flags inherit -trimpath via builder-config.yaml -# (default for OCB-generated builds). -# - CGO disabled by OCB default. -# - Version string is injected at build time by `make build` from -# `git describe --tags --always --match 'v*' --dirty=-dev`, falling -# back to `$TRACECORE_VERSION` (CI override) and finally to the -# hardcoded `dist.version` in `builder-config.yaml`. At a clean -# `vX.Y.Z` tag commit, all three resolve to the same string, so the -# release artefact carries the tag name verbatim. - -version: 2 - -project_name: tracecore - -dist: ./dist - -before: - hooks: - # Verify module hashes against go.sum before building. Mirrors the - # `go mod download && go mod verify` step in the archived release.yml. - # If go.sum is poisoned at this tag commit, this catches it before - # any binary leaves the runner. - - go mod download - - go mod verify - # The release.yml workflow runs `make build` per platform AHEAD - # of `goreleaser release` (see the "Pre-build OCB binaries" - # step), parking each binary at ./_build/{Os}-{Arch}/tracecore - # so the prebuilt: stanza below resolves. No `make build` hook - # here — running it would clobber the pre-built per-platform - # tree with a single native-arch build. - -builds: - - id: tracecore - # OCB writes the binary to ./_build/tracecore regardless of - # goreleaser's per-build target. The release.yml workflow sets - # GOOS/GOARCH on a per-job basis and re-runs `make build`, then - # promotes ./_build/tracecore to the path prebuilt: expects below. - # This is the simplest binding without authoring an OCB-aware - # custom builder. - builder: prebuilt - goos: - - linux - goarch: - - amd64 - - arm64 - prebuilt: - # release.yml's matrix step renames ./_build/tracecore to - # this path before invoking `goreleaser release`. Pattern - # mirrors goreleaser's standard prebuilt example. - path: ./_build/{{ .Os }}-{{ .Arch }}/tracecore - binary: tracecore - -archives: - - id: tracecore - formats: - - tar.gz - name_template: >- - {{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }} - files: - - LICENSE - - README.md - -checksum: - name_template: "checksums.txt" - algorithm: sha256 - -# SBOM (CycloneDX JSON) generated per-archive via Syft (anchore/sbom-action -# uses the same engine). Output filename pattern matches the archive so -# verifiers can pair binary↔SBOM by name. cosign integration lives in the -# release.yml workflow (sign step) since signing needs OIDC at job scope. -sboms: - - id: tracecore - artifacts: archive - documents: - - "${artifact}.sbom.cdx.json" - cmd: syft - args: - - "$artifact" - - "--output" - - "cyclonedx-json=$document" - -changelog: - use: git - sort: asc - # Conventional-commits filter — surfaces feat / fix / perf and drops - # noise (chore, ci, docs, test, style, refactor). RFC-0013 release - # notes will be hand-edited for v0.1.0 anyway; this gives a sane - # default for patch releases. - filters: - exclude: - - "^chore:" - - "^chore\\(" - - "^ci:" - - "^ci\\(" - - "^docs:" - - "^docs\\(" - - "^test:" - - "^test\\(" - - "^style:" - - "^style\\(" - - "^refactor:" - - "^refactor\\(" - groups: - - title: Features - regexp: "^.*feat[(\\w)]*:+.*$" - order: 0 - - title: Bug fixes - regexp: "^.*fix[(\\w)]*:+.*$" - order: 1 - - title: Performance - regexp: "^.*perf[(\\w)]*:+.*$" - order: 2 - - title: Other - order: 999 - -release: - # The release.yml workflow owns release-note composition (it appends - # the docs/reproducibility.md walkthrough + Rekor logIndex pointer); - # goreleaser just stages assets + checksums. Setting `draft: false` - # mirrors the archived workflow's behaviour. - draft: false - prerelease: auto - mode: append - -snapshot: - version_template: "{{ incpatch .Version }}-next" From 47e4f32d80ce250793f6ff89657d3b3b96bff867 Mon Sep 17 00:00:00 2001 From: Tri Lam Date: Sun, 31 May 2026 20:16:02 -0700 Subject: [PATCH 2/2] fix(release): unwrap tar archive + idempotent gh release create MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .github/workflows/release.yml | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 18c8bb4b..1de774f8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -143,8 +143,8 @@ jobs: --sort=name \ --owner=0 --group=0 --numeric-owner \ --mtime="@${SOURCE_DATE_EPOCH}" \ - -C dist/stage \ - -cf - "tracecore_${version}_linux_${arch}" \ + -C "${stage}" \ + -cf - . \ | gzip -n > "dist/${archive}" ( cd dist && sha256sum "${archive}" >> checksums.txt ) @@ -203,11 +203,18 @@ jobs: # goreleaser changelog: stanza without per-section grouping. # Operators may hand-edit `gh release edit "$TAG" --notes-file # ...` post-merge for marquee releases. - gh release create "${TAG}" \ - --title "${TAG}" \ - --generate-notes \ - ${prerelease_flag} \ - dist/*.tar.gz dist/*.sbom.cdx.json dist/checksums.txt + # Idempotent: re-runs of the workflow (e.g., manual rerun + # after a transient downstream failure) re-upload assets via + # --clobber rather than failing on "release already exists". + if ! gh release view "${TAG}" >/dev/null 2>&1; then + gh release create "${TAG}" \ + --title "${TAG}" \ + --generate-notes \ + ${prerelease_flag} + fi + gh release upload "${TAG}" \ + dist/*.tar.gz dist/*.sbom.cdx.json dist/checksums.txt \ + --clobber - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: