Release (artifacts + signing) #19
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release (artifacts + signing) | |
| # Builds release artifacts and signs them when a `v*` tag is pushed. | |
| # | |
| # The tag itself is cut by scripts/release.sh: it builds the React UI, makes a | |
| # DETACHED commit embedding internal/ui/dist, tags it, and pushes ONLY the tag | |
| # (main stays source-only). This workflow runs on that tag push and owns | |
| # release ARTIFACTS + SIGNING — GoReleaser builds the cross-platform binaries, | |
| # emits checksums + SBOMs, and cosign keyless-signs the checksums file so a | |
| # .sig + .pem pair attaches to the release. That satisfies Scorecard's | |
| # Packaging (a publishing workflow is detected) and Signed-Releases checks. | |
| # | |
| # release.sh embeds internal/ui/dist into the detached tagged commit, but this | |
| # workflow rebuilds the UI (npm ci && npm run build) BEFORE invoking goreleaser | |
| # anyway: `go build` only recompiles Go (never runs vite), so rebuilding here | |
| # guarantees the embedded dist is present and current regardless of how the tag | |
| # was produced. | |
| # | |
| # All third-party actions are SHA-pinned per Scorecard Pinned-Dependencies; | |
| # pins are reused verbatim from security.yml / scorecard.yml where they overlap. | |
| on: | |
| push: | |
| tags: | |
| - 'v*' | |
| # Manual re-release: run the CURRENT (default-branch) workflow against an | |
| # already-pushed tag. GoReleaser's `release: mode: append` adds artifacts to | |
| # the existing release WITHOUT mutating the immutable tag — used to recover a | |
| # release whose original tag-push run failed. | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: 'Existing tag to (re)build + sign (e.g. v0.3.1)' | |
| required: true | |
| # Workflow-level default: minimum needed for actions/checkout. The release job | |
| # escalates to contents: write (create/append the GitHub release + upload | |
| # assets) and id-token: write (cosign keyless OIDC). | |
| permissions: | |
| contents: read | |
| jobs: | |
| goreleaser: | |
| name: GoReleaser (build · sbom · sign) | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| id-token: write | |
| steps: | |
| - name: Harden runner egress | |
| # step-security/harden-runner v2.19.0 | |
| uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 | |
| with: | |
| egress-policy: audit | |
| - name: Checkout tag | |
| # actions/checkout v4.2.2 | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd | |
| with: | |
| # On workflow_dispatch, build the supplied existing tag; on a tag | |
| # push, the pushed ref. Full history so goreleaser version detection | |
| # works. | |
| ref: ${{ inputs.tag || github.ref }} | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Set up Go | |
| # actions/setup-go v6.2.0 | |
| uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Set up Node | |
| # actions/setup-node v6.4.0 | |
| uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e | |
| with: | |
| node-version: '24' | |
| - name: Build UI (embedded into the binary) | |
| # The Go binary embeds internal/ui/dist via //go:embed; build it before | |
| # goreleaser compiles. `npm run build` emits into internal/ui/dist. | |
| run: | | |
| cd ui | |
| npm ci | |
| npm run build | |
| test -f ../internal/ui/dist/index.html | |
| - name: Install cosign | |
| # sigstore/cosign-installer v3.9.2 — provides `cosign` for the keyless | |
| # signing step in .goreleaser.yaml (signs: cmd: cosign). | |
| uses: sigstore/cosign-installer@d58896d6a1865668819e1d91763c7751a165e159 | |
| - name: Install syft | |
| # anchore/sbom-action v0.24.0 (download-syft sub-action) — puts `syft` | |
| # on PATH for the goreleaser `sboms:` step, which shells out to syft per | |
| # archive. Without this goreleaser fails: `exec: "syft": not found`. | |
| # Pin reused from security.yml. | |
| uses: anchore/sbom-action/download-syft@e22c389904149dbc22b58101806040fa8d37a610 | |
| - name: Run GoReleaser | |
| # goreleaser/goreleaser-action v6.2.1 | |
| uses: goreleaser/goreleaser-action@90a3faa9d0182683851fbfa97ca1a2cb983bfca3 | |
| with: | |
| version: '~> v2' | |
| args: release --clean | |
| env: | |
| # Pure-Go SQLite driver → cgo off (matches .goreleaser.yaml builds.env). | |
| CGO_ENABLED: '0' | |
| # Default token: goreleaser uses it to create/append the release. | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # cosign keyless reads the OIDC token from these (set by id-token: write). | |
| COSIGN_EXPERIMENTAL: '1' |