CI and Releases see history edit this page

Talks about: , , and

Static analysis

golangci-lint is not used. The tools below run directly, both in CI and locally inside the dev shell (see Building and Testing ). Every tool is a separate, auditable binary with its own config file.

ToolScopeConfig
go vet (all analyzers)Go correctness
staticcheckBugs, simplifications, stylestaticcheck.conf (checks = ["all"])
gosecSecurity patternsinline #nosec justifications
govulncheckKnown vulnerabilities in the dependency graph
arch-goArchitecture rulesarch-go.yml
gofumptStrict formatting
REUSELicense / copyright metadata on every fileREUSE.toml
yamllintYAML.yamllint.yaml
actionlintGitHub Actions workflows
markdownlintMarkdown.markdownlint.yaml
typosSpelling.typos.toml
TrivyContainer image CVEs

Architecture rules

arch-go.yml pins two invariants enforced with 100% compliance:

The verify.yml PR gate

.github/workflows/verify.yml fans out into one job per concern. A failure points straight at the offending gate. CI installs each tool fresh via go run <tool>@latest; the dev shell pre-installs the same tools at the same versions, so local and CI runs agree.

JobWhat it runs
testgo build ./... then go test -v -cover ./...
lint-gogo vet ./..., staticcheck ./..., gosec ./..., gofumpt -l . (fails on any output)
vulnerabilitiesgovulncheck ./... — reachable-from-code advisories are a hard merge gate
architecturearch-go against arch-go.yml
reusefsfe/reuse-action — every file must carry SPDX headers
yamlyamllint against .yamllint.yaml
github-actionsactionlint
markdownmarkdownlint-cli2 against .markdownlint.yaml
typostypos against .typos.toml
proseVale against the shared metio/vale-config style; error-level findings (naming/branding) fail the gate
container-imagedocker buildx build (load, no push) followed by Trivy scan; hard-fails on any fixable CRITICAL/HIGH

All-green aggregate

The workflow ends with a single all-green job:

That one job is the only check marked required in branch protection. Adding a new job to the needs list of all-green covers it automatically; no new required check needs to be registered.

The govulncheck gate is a hard blocker. A reachable-from-code advisory that cannot be fixed by bumping a dependency blocks the PR until resolved. Resolution is usually a toolchain bump in go.mod (for stdlib advisories) or go get -u (for module advisories).

The release pipeline

Releases are calendar-based and automated. .github/workflows/release.yml runs on a Monday cron (47 7 * * MON) plus manual workflow_dispatch. The version is computed from the run date:

date +'%Y.%-m.%-d'

For a Monday run on 2026-06-22 that produces 2026.6.22.

goreleaser is not used. GPG is not used. The pipeline is hand-rolled across three jobs.

prepare

Counts commits since the last release touching the build-relevant paths (go.mod main.go internal api config Dockerfile). Every downstream job gates on that count being non-zero (or there being no prior release at all), so an empty week publishes nothing.

build

A cross-compile matrix over ten platform/arch combinations:

Each platform compiles with:

CGO_ENABLED=0 go build -trimpath \
  -ldflags="-s -w -X main.version=<ver> -X main.commit=<sha>" .

Archives are tar.gz on linux/darwin and zip on windows (with a .exe binary), each bundling LICENSE and README.md.

container

A single docker buildx multi-arch push to ghcr.io/metio/jaas:{latest,<version>} over the six linux arches. The Dockerfile builder is pinned to $BUILDPLATFORM and cross-compiles via Go’s GOARCH, so the multi-arch build needs no QEMU.

SBOM and provenance are attached. The image is signed with cosign keyless immediately after push:

cosign sign \
  --yes \
  --annotations "repo=metio/jaas" \
  --annotations "workflow=Automated Release" \
  ghcr.io/metio/jaas@<digest>

Identity is proven by the workflow’s OIDC certificate issued by Fulcio; there is no key to distribute.

github

Gates on both build and container succeeding. Downloads all platform archives, computes a single SHA256SUMS over them, signs it with cosign keyless (Sigstore bundle format), and publishes the GitHub Release with all archives, the checksum file, and the bundle attached.

To verify a release download:

cosign verify-blob jaas_<version>_SHA256SUMS \
  --bundle jaas_<version>_SHA256SUMS.bundle \
  --certificate-identity-regexp '^https://github.com/metio/jaas/\.github/workflows/release\.yml@refs/' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com
sha256sum -c jaas_<version>_SHA256SUMS

To verify the container image:

cosign verify ghcr.io/metio/jaas:<version> \
  --certificate-identity-regexp '^https://github.com/metio/jaas/\.github/workflows/release\.yml@refs/' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com