Skip to content

Spec: phpboyscout/cicd v0.9 — Go track

  • Repository: gitlab.com/phpboyscout/cicd
  • Released as: v0.9.0 (minor — four additive components; no change to existing components or the v0.8 preset).
  • Driver: every project that will be derived from go-tool-base carries ~120 lines of identical lint / test / security / release jobs. Extracting once means a new Go project's .gitlab-ci.yml becomes ~30 lines of include: blocks. Sibling to the Tofu split (tofu-lint / tofu-security / tofu-validate / tofu-module-publish) — same shape, same conventions, same Renovate-tracked component pins.

Decisions

D1 — Four components, mirroring the Tofu split

Rather than one monolithic "go-ci" component, the Go track is split by concern so consumers can opt out per pipeline stage:

Component Stage Job(s) Purpose
go-lint lint golangci-lint golangci-lint over the project's packages
go-test test go-test (+ optional go-test-e2e) go test -race -coverprofile with the standard coverage-badge regex; opt-in e2e job under an env-var build flag
go-security security govulncheck, trivy, gitleaks, osv-scanner, analyze (semgrep) Five MR-time vulnerability / secret / static-analysis scanners
goreleaser release goreleaser Tag-gated goreleaser release --clean

Each component is the minimal unit a consumer can include in isolation — a docs-only Go repo can take go-lint + go-test and skip go-security + goreleaser entirely.

D2 — MR-only by default, overridable via inputs.if

All four gate components (go-lint, go-test, go-security) default to running only on merge-request pipelines:

if: '$CI_PIPELINE_SOURCE == "merge_request_event"'

This matches go-tool-base's current .mr-only anchor. Consumers override the input to widen (e.g. also run on tags) or narrow (e.g. only on changed paths). Component interpolation passes runtime $VAR references through unchanged.

goreleaser instead takes a tag_pattern input (RE2, default ^v[0-9]+\.[0-9]+\.[0-9]+$) — same shape as tofu-apply's tag_pattern (v0.3.1) and tofu-module-publish's (v0.7.0).

D3 — enable_e2e as opt-in boolean with when: never on the disabled path

Most projects that derive from go-tool-base won't have a separate e2e suite. Rather than force every consumer to add a rules: [{when: never}] override, go-test exposes enable_e2e (default false):

rules:
  - if: '"$[[ inputs.enable_e2e ]]" != "true"'
    when: never
  - if: '$[[ inputs.if ]]'

The first rule (enable_e2e != "true") interpolates at include time to either "true" != "true" (matches → when: never) or "false" != "true" (no match → falls through to the MR-only rule). Compared to a single && rule, this keeps the disabled path from generating a skipped-job entry in the pipeline UI.

D4 — goreleaser defaults GOTOOLCHAIN=local to dodge sum.golang.org

The goreleaser image ships a Go toolchain that already satisfies common go.mod toolchain directives. Setting GOTOOLCHAIN=local stops the runner from attempting an inline toolchain download from sum.golang.org mid-release — a transient IPv6 routing issue on shared runners broke a prior go-tool-base v0.5.0 release at exactly this point. Override to auto only if your project's go.mod explicitly needs a newer toolchain.

GIT_DEPTH: "0" is hard-coded: goreleaser walks the full commit history to derive changelogs and snapshot metadata.

D5 — Tool-version pins per scanner, not a unified image

go-security exposes a separate *_image input per scanner (govulncheck via golang:X-bookworm, trivy, gitleaks, osv-scanner, semgrep). The Renovate preset's custom manager auto-bumps each pin independently, mirroring how tofu-security already handles its tool suite. A future "phpboyscout-go-tools" multi-tool image could collapse this to one input — out of scope for v0.9.

Consumers (post-release)

Project Inline jobs replaced Components consumed
go-tool-base lint, tests, e2e-bdd-tests, govulncheck, trivy, gitleaks, osv-scanner, analyze, goreleaser all four (with enable_e2e: true)
future projects derived from go-tool-base (nothing inline yet) inherit the full stack at fork time

Renovate (v0.8) already tracks phpboyscout/cicd component pins, so a new v0.9.0 surfaces as MRs in every consumer that extends the preset.

Follow-up

  • Adopt across consumers in one migration MR per repo (task #49 already covers Renovate; add Go-track adoption to its scope or a sibling task once v0.10 (Rust track) is also out — adopting all at once reduces churn).
  • Future component: a go-tools image (single image bundling govulncheck + trivy + gitleaks + osv-scanner + semgrep + golangci-lint
  • go) so go-lint and go-security jobs avoid pulling 5 distinct images per pipeline. Sibling to infra-tools for the Tofu side.