Skip to content

fix: deliver the fixture and process overrides on InitRequest (#686) #1653

fix: deliver the fixture and process overrides on InitRequest (#686)

fix: deliver the fixture and process overrides on InitRequest (#686) #1653

Workflow file for this run

name: Go CLI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
quality:
name: ${{ matrix.gate }}
runs-on: ubuntu-latest
# The race and coverage gates take 4-5 minutes on a warm cache with ±40s of
# runner variance, so a 5-minute budget cancels borderline-healthy runs.
# 8 minutes keeps a hang from burning a full runner-hour while leaving the
# suite real headroom.
timeout-minutes: 8
# `conformance_rows` is the set of docs/supported-matrix.md rows this gate
# claims to prove. A claimed row's tests can no longer skip themselves when
# the backend is missing, and the always-run receipt check below fails if a
# claimed row never ran.
env:
CODEFLY_CONFORMANCE_REQUIRED: ${{ matrix.conformance_rows }}
CODEFLY_CONFORMANCE_RECEIPTS: ${{ github.workspace }}/conformance-receipts
strategy:
fail-fast: true
matrix:
include:
- gate: bootstrap-audit
- gate: control-integration
conformance_rows: linux-amd64-nix-run
- gate: coverage
conformance_rows: linux-amd64-source,linux-amd64-native-npm
- gate: race
conformance_rows: linux-amd64-source,linux-amd64-native-npm
steps:
- name: checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up Go
if: matrix.gate == 'coverage' || matrix.gate == 'race'
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version-file: go.mod
cache: false
# The production source-workspace regression runs the released Python
# plugin on a fresh Linux host. Provision its declared uv runtime in the
# ordinary test lanes so the proof exercises real tests instead of an
# environment-blocked agent response.
- name: Set up uv
if: matrix.gate == 'coverage' || matrix.gate == 'race'
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
# pkg/librarystore's npm backend shells out to the real npm CLI (npm pack,
# npm publish) against an httptest registry rather than mocking the store;
# those tests need node/npm on the runner.
- name: Set up Node
if: matrix.gate == 'coverage' || matrix.gate == 'race'
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
# Restore before the bootstrap action: that action compiles the CLI, so
# restoring afterward would make its cache permanently ineffective.
# Cache only compiled artifacts. Caching GOMODCACHE alongside a Go
# toolchain directive can race setup-go's toolchain install and produce
# tar restore conflicts.
- name: Restore Go build cache
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cache/go-build
key: cli-go-build-v3-${{ matrix.gate }}-${{ runner.os }}-${{ hashFiles('go.sum') }}
restore-keys: |
cli-go-build-v3-${{ matrix.gate }}-${{ runner.os }}-
# This is both the canonical bootstrap and the cross-repository action
# contract consumed by Codefly products. Dogfood the local action so a
# release cannot publish an installer that CI never exercised.
- name: install Codefly
if: matrix.gate == 'bootstrap-audit' || matrix.gate == 'control-integration'
uses: ./.github/actions/install
- name: Install Dependencies
run: go mod download
- name: Verify the composed-module command is packaged
if: matrix.gate == 'bootstrap-audit'
run: codefly sync module --help
# govulncheck gate via the CLI (replaces scripts/govulncheck.sh):
# suppressions + staleness from .govulncheck.yaml, fails only on
# actionable (upstream-fixed) findings.
- name: govulncheck audit
if: matrix.gate == 'bootstrap-audit'
run: |
go install golang.org/x/vuln/cmd/govulncheck@v1.8.0
codefly audit go --dir .
- name: Test with coverage
if: matrix.gate == 'coverage'
run: go test -failfast ./... -coverprofile=cover.out -covermode=atomic -coverpkg=./...
- name: Check test coverage
if: matrix.gate == 'coverage'
id: coverage
uses: vladopajic/go-test-coverage@f94bcf0d6b9fa5fb8b783830b22648f6c17475e2 # v2
with:
config: ./.testcoverage.yaml
# The CGO-free companion/self-cross build (`-tags codefly_nosemantic`,
# CGO_ENABLED=0) selects pkg/engine/source_nosemantic.go. `codefly
# companion publish` exercises it only in companions.yaml, which needs
# Docker; verify here, unconditionally, that the variant both links
# statically and runs its base source behavior. Without this a regression
# in the analyzer-free variant would surface only at publish time.
- name: Verify CGO-free (companion) build variant
if: matrix.gate == 'coverage'
env:
CGO_ENABLED: "0"
run: |
go build -tags codefly_nosemantic -ldflags '-s -w -extldflags "-static"' -o /dev/null .
go test -tags codefly_nosemantic ./pkg/engine/ -run '^TestNewSourceExecutesLanguageNeutralOperation$' -count=1
- name: Test with race detection
if: matrix.gate == 'race'
run: go test -failfast -race ./... -v
# The cross-process control-ownership proof drives the CLI built by the
# install action above as a real subprocess against real sockets. It runs
# here rather than in the default lanes because those would have to link
# a CLI binary of their own; it needs neither Nix nor an agent, so it runs
# before both and fails fast.
- name: Run the cross-process control-ownership proof
if: matrix.gate == 'control-integration'
run: go test -tags=integration -run '^TestRunRefusesAControlAddressAnotherProcessOwns$|^TestConcurrentDisposableRunsClaimDistinctIdentities$' ./cmd/run -v
# The pinned Redis agent resolves a Nix runtime during runtime-init, so the
# proof needs Nix on the runner or the agent fails with "nix is not
# installed" instead of exercising the real dependency. Mirrors core's CI.
- name: Install Nix
# Strict — nix is core to the platform; if it doesn't install,
# CI must fail loud rather than silently skipping nix tests.
# DeterminateSystems' installer is the de-facto standard for
# GH Actions: deterministic, no daemon issues on ephemeral runners.
#
# This used to follow @main. It is now pinned to a release, like every
# other action here, so the comment records the tag at the pinned commit
# (v23) rather than the branch it was reached through — Dependabot
# resolves the version from the tag at the SHA, so a `# main` comment
# would be silently rewritten to a version on the first bump.
if: matrix.gate == 'control-integration'
uses: DeterminateSystems/nix-installer-action@3138316df39ed29be04236d7ffc686fa525866aa # v23
- name: Allow unprivileged user namespaces (for bwrap)
# Ubuntu 24.04 ships with kernel.apparmor_restrict_unprivileged_userns=1
# which blocks bubblewrap's user-namespace setup ("bwrap: setting up uid
# map: Permission denied"). Disabling the restriction is the standard CI
# workaround — bwrap then works in unprivileged mode, which is what the
# nix runtime's sandbox expects.
if: matrix.gate == 'control-integration'
run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
- name: Install the pinned Redis agent
if: matrix.gate == 'control-integration'
run: codefly agent install codefly.dev/redis:0.0.74
- name: Run the in-process run-profile proof
if: matrix.gate == 'control-integration'
run: go test -tags=integration -run '^TestRunProfilesStartRealDependencyShapesInProcess$' ./pkg/control -v
# Pull separately from the proof below so a cold-cache image fetch is
# attributed to its own step instead of inflating the test's wall time —
# this gate shares one 8-minute job budget with the Nix install above.
# Keep the tag in step with teardownPostgresImage in
# pkg/orchestration/teardown_postgres_integration_test.go.
- name: Pull the ordered-teardown proof's database image
if: matrix.gate == 'control-integration'
run: docker pull postgres:16-alpine
# Ordered teardown (F09): the consumer holds its drain open and commits a
# final transaction to a disposable Postgres; the database must not begin
# stopping until that commit returned. Needs Docker, which the runner has.
# It claims no conformance row: the ordering contract is backend-neutral,
# so it proves nothing about a docs/supported-matrix.md row.
- name: Run the ordered-teardown acceptance proof
if: matrix.gate == 'control-integration'
run: go test -tags=integration -run '^TestFlowStopHoldsPostgresUntilItsConsumerCommits$' ./pkg/orchestration -v
- name: Verify CI image prerequisite ordering
if: matrix.gate == 'control-integration'
run: go test -tags=integration -run '^TestCIBuildOrdersRealImagePrerequisites$' ./cmd/ci -v
# Keep the tag in step with qualificationImage in
# cmd/generate/container_recovery_docker_test.go.
- name: Pull the interrupted-generate proof's image
if: matrix.gate == 'control-integration'
run: docker pull alpine:3.22
# `codefly generate` builds its containers in the CLI process rather than
# through an agent, so no fleet rebuild can make them recoverable and no
# unit test can prove they are: the labels and the sweep that reads them
# only exist against a real daemon. This is the only container-recovery
# dimension the CLI can qualify without a rebuilt, published agent —
# cli#658 carries the rest.
- name: Run the interrupted-generate container-recovery proof
if: matrix.gate == 'control-integration'
run: go test -tags=integration -run '^TestAnInterruptedGenerateLeavesARecoverableContainer$' ./cmd/generate -v
# The source-workspace pin is promoted by hand through `codefly agent
# promote-source`, which qualifies a candidate only through `codefly test
# source`. This drives the pin the binary actually embeds against the
# contracts that gate never reaches — Builder.SBOM, Builder.Package, and a
# go.work-governed checkout — so a promotion cannot land on a release that
# regressed one of them. It downloads the pinned agent, so it needs
# network but no Docker. It claims no conformance row: a roster pin is not
# a docs/supported-matrix.md row.
- name: Qualify the embedded source-workspace pin
if: matrix.gate == 'control-integration'
env:
CODEFLY_SOURCE_PIN_QUALIFY: "1"
run: go test -tags=integration -run '^TestPinnedGoAgentAnswersSourceContractsForAGoWorkCheckout$' ./pkg/sourceworkspace -v
# Always-run so a failed or filtered-out test still has to account for
# the rows its job claimed. Keep these last: they audit every step above.
- name: Verify conformance receipts
if: always() && matrix.conformance_rows != ''
run: ./scripts/verify-conformance-receipts.sh
- name: Upload conformance receipts
if: always() && matrix.conformance_rows != ''
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: conformance-receipts-${{ matrix.gate }}
path: conformance-receipts/
if-no-files-found: warn
# golangci-lint runs as its own job, not a quality gate, for two reasons:
# only-new-issues has a well-defined baseline only against a pull-request base
# branch (on push there is no diff to gate against), and a cold-cache
# whole-repository analysis needs more headroom than the 5-minute quality
# budget. Pinned to match the version documented in the Makefile (`make
# lint`). only-new-issues gates the PR on newly introduced findings so the
# pre-existing backlog does not block work while the standard is enforced.
lint:
name: lint
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
timeout-minutes: 10
steps:
- name: checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up Go
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version-file: go.mod
cache: false
- name: golangci-lint
uses: golangci/golangci-lint-action@ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a # v9.3.0
with:
# v2.13.1 bundles staticcheck (honnef.co/go/tools) v0.8.0, the first
# release that understands Go 1.27 stdlib source; v2.12.2's v0.7.0
# panics in its IR builder analyzing 1.27 packages. Keep in sync with
# GOLANGCI_LINT_VERSION in the Makefile.
version: v2.13.1
# Build the linter from source under the module's Go toolchain
# (1.27) instead of downloading the prebuilt binary, which is built
# with an older Go and refuses to lint a module targeting a newer
# one ("the Go language version used to build golangci-lint is lower
# than the targeted Go version"). Matches `make lint`.
install-mode: goinstall
only-new-issues: true
# pkg/web/go-grpc/out is a committed, generated artifact: web/dashboard builds
# into it and go:embed ships it inside the CLI binary (see the Makefile's
# `dashboard` target, which is the only other place it is produced).
#
# Nothing else in CI runs npm, so without this job a dependency bump under
# web/dashboard changes package.json and package-lock.json, leaves out/
# untouched, and passes every Go gate — TestHandlerServesDashboard included,
# because that test asserts against the committed bundle and so cannot notice
# that the bundle no longer corresponds to the declared dependencies. The
# binary would ship a dashboard built from the old dependency tree while the
# lockfile claimed the new one, and the drift would surface at the next manual
# `make dashboard`, several merges downstream of the bump that caused it.
#
# The build is byte-reproducible from the lockfile, so a clean tree is a fair
# gate rather than a flaky one.
dashboard:
name: dashboard
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up Node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
with:
# Pinned rather than left to the runner default: this job's whole
# value is that a dirty out/ means the dependencies moved, so the
# toolchain producing out/ must not drift underneath it.
node-version: '24'
cache: npm
cache-dependency-path: web/dashboard/package-lock.json
# npm ci, not npm install: it fails closed on a package.json that has
# drifted from the lockfile instead of silently resolving something else.
- name: Rebuild the embedded dashboard
run: |
set -euo pipefail
cd web/dashboard
npm ci
npm run build
# --intent-to-add so a newly emitted asset (an untracked file) is visible
# to `git diff`; without it the check would only catch modifications and
# deletions, and a build that added a chunk would pass while out/ was
# missing it.
- name: Fail if the committed bundle is stale
run: |
set -euo pipefail
git add --intent-to-add -- pkg/web/go-grpc/out
if ! git diff --exit-code -- pkg/web/go-grpc/out; then
echo "::error::pkg/web/go-grpc/out does not match a build of web/dashboard. Run 'make dashboard' and commit the result."
exit 1
fi