Skip to content

release

release #22

Workflow file for this run

name: release
# Manually-triggered stable release. Pick bump (patch/minor/major);
# the next vX.Y.Z tag is computed from the latest stable tag.
#
# Beta auto-tagging was removed: the scorecard Signed-Releases check
# ignores prereleases, and most users download the latest stable tag.
# Tag pushes are not a trigger so the workflow cannot recurse on its
# own tag.
on:
workflow_dispatch:
inputs:
bump:
description: "Semver bump (patch/minor/major)"
required: true
type: choice
default: patch
options:
- patch
- minor
- major
permissions: read-all
concurrency:
group: release-manual
cancel-in-progress: false
jobs:
tag:
name: compute next tag
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
tag: ${{ steps.ver.outputs.tag }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
- name: Compute next stable tag
id: ver
env:
BUMP: ${{ inputs.bump }}
run: |
set -eu
latest=$(git tag -l 'v[0-9]*.[0-9]*.[0-9]*' \
| grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' \
| sort -V \
| tail -1)
if [ -z "$latest" ]; then
latest="v0.0.0"
fi
ver="${latest#v}"
major="${ver%%.*}"
rest="${ver#*.}"
minor="${rest%%.*}"
patch="${rest##*.}"
case "$BUMP" in
major) major=$((major+1)); minor=0; patch=0 ;;
minor) minor=$((minor+1)); patch=0 ;;
patch) patch=$((patch+1)) ;;
*) echo "unknown bump: $BUMP" >&2; exit 1 ;;
esac
next="v${major}.${minor}.${patch}"
echo "tag=$next" >> "$GITHUB_OUTPUT"
echo "Next tag: $next (from $latest, bump=$BUMP)"
ui:
name: build ui
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: '22'
cache: 'npm'
cache-dependency-path: ui/package-lock.json
- run: npm --prefix ui ci
- run: npm --prefix ui run build
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: ui-dist
path: ui/dist
retention-days: 1
if-no-files-found: error
build:
name: build ${{ matrix.suffix }}
needs: [tag, ui]
strategy:
fail-fast: false
matrix:
include:
- runner: ubuntu-latest
goos: linux
goarch: amd64
suffix: linux-amd64
- runner: macos-latest
goos: darwin
goarch: arm64
suffix: darwin-arm64
runs-on: ${{ matrix.runner }}
permissions:
contents: read
env:
CGO_ENABLED: "1"
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
with:
go-version-file: go.mod
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: ui-dist
path: ui/dist
- name: Build binary
id: build
run: |
set -eu
tag="${{ needs.tag.outputs.tag }}"
sha="${{ github.sha }}"
date="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
pkg="github.com/RandomCodeSpace/docsiq/cmd"
ldflags="-s -w -X ${pkg}.Version=${tag} -X ${pkg}.Commit=${sha} -X ${pkg}.Date=${date}"
out="docsiq-${tag}-${{ matrix.suffix }}"
go build -tags sqlite_fts5 -trimpath -ldflags="${ldflags}" -o "${out}" ./
echo "binary=${out}" >> "$GITHUB_OUTPUT"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: binary-${{ matrix.suffix }}
path: ${{ steps.build.outputs.binary }}
retention-days: 1
if-no-files-found: error
release:
name: sign + publish
needs: [tag, build]
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write # required for cosign keyless signing
attestations: write # required for actions/attest-build-provenance
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
pattern: binary-*
merge-multiple: true
path: dist/
- uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1
with:
# Pin v2.x — v3 changed sign-blob defaults to emit a Sigstore
# bundle alongside, and our --output-signature/--output-certificate
# flags aren't compatible with that new behaviour. Revisit v3 once
# we're ready to switch to bundle-based attestations.
cosign-release: 'v2.6.3'
- name: Sign binaries
run: |
set -eu
cd dist
for f in docsiq-*; do
case "$f" in *.sig|*.pem|SHA256SUMS*) continue ;; esac
cosign sign-blob --yes \
--output-signature "${f}.sig" \
--output-certificate "${f}.pem" \
"$f"
done
- name: Generate SHA256SUMS (+ signature)
run: |
set -eu
cd dist
sha256sum docsiq-* > SHA256SUMS
cosign sign-blob --yes \
--output-signature SHA256SUMS.sig \
--output-certificate SHA256SUMS.pem \
SHA256SUMS
# SLSA build provenance — scorecard's Signed-Releases check asks
# for in-toto attestations alongside the cosign sigs. The bundle
# covers both binaries and SHA256SUMS.
- name: Generate SLSA build provenance
id: attest
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
with:
subject-path: |
dist/docsiq-*-linux-amd64
dist/docsiq-*-darwin-arm64
dist/SHA256SUMS
- name: Copy attestation into dist
run: |
set -eu
cp "${{ steps.attest.outputs.bundle-path }}" \
dist/docsiq-${{ needs.tag.outputs.tag }}.intoto.jsonl
# softprops/action-gh-release is the pattern scorecard's Packaging
# check recognises, replacing our ad-hoc `gh release create`.
# Creates the tag on the triggering SHA when it doesn't yet exist.
- name: Create GitHub release
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0
with:
tag_name: ${{ needs.tag.outputs.tag }}
name: ${{ needs.tag.outputs.tag }}
target_commitish: ${{ github.sha }}
generate_release_notes: true
make_latest: 'true'
files: |
dist/*