Spec: phpboyscout/cicd v0.10 — Rust track¶
- Repository:
gitlab.com/phpboyscout/cicd - Released as:
v0.10.0(minor — five additive components; no change to existing components or the v0.8 preset). - Driver: every future project derived from
rust-tool-basewould otherwise copy ~290 lines of Cargo cache + binstall bootstrap + disk-pressure tuning + scanner version pins + release-plz remote setup. Extracting once means a new Rust project's.gitlab-ci.ymlbecomes ~30 lines ofinclude:blocks, and the painful lessons (CVSS 4.0 in cargo-deny ^0.18,RELEASE_PLZ_TOKEN≠CI_JOB_TOKENfor downstream tag pipelines,CARGO_INCREMENTAL=0+ DWARFline-tables-onlyfor medium-runner disk budget) stay in code, not scattered comments.
Decisions¶
D1 — Five components, mirroring the Go track¶
| Component | Stage | Jobs | Purpose |
|---|---|---|---|
rust-lint |
lint |
rustfmt, clippy |
Format + clippy. rustfmt skips target/ cache (source-only); clippy gets the Cargo.lock-keyed target/ cache. |
rust-test |
test |
test-linux (always), test-macos / test-windows (opt-in), test-integration (opt-in), coverage (opt-in) |
nextest-driven test matrix. Opt-ins gated by when: never (not just skipped) so disabled paths produce no pipeline-UI noise. |
rust-security |
security |
cargo-deny, cargo-audit, trivy, gitleaks |
Four scanners; cargo-* tools bootstrap via cargo-binstall + cargo install. |
rust-docs |
docs |
cargo-doc |
cargo doc --no-deps --all-features with RUSTDOCFLAGS=-D warnings. |
release-plz |
release |
release-plz |
release-plz release-pr + release-plz release on the default branch; handles the git-remote + token reauth. |
D2 — Disk-pressure tuning is baked in¶
Every Linux test/build job sets:
variables:
CARGO_INCREMENTAL: "0"
CARGO_PROFILE_DEV_DEBUG: "line-tables-only"
CARGO_PROFILE_TEST_DEBUG: "line-tables-only"
before_script:
- rm -rf "$CARGO_TARGET_DIR/debug/incremental" 2>/dev/null || true
The incremental cache never survives across CI runs but inflates
target/ enough to exhaust the SaaS medium runner. Stripping DWARF
to line-tables-only keeps backtraces meaningful (file + line) while
cutting per-object size enough to fit. Lessons paid for by a
rust-tool-base pipeline that died at link time on the medium
runner.
D3 — cargo-binstall bootstrap, not curl-pipe¶
cargo-binstall is bootstrapped via cargo install --locked
cargo-binstall (crates.io) rather than the upstream curl-pipe
installer from raw.githubusercontent.com. Shared runners
intermittently fail to resolve GitHub raw-content, and a curl-pipe
failure aborts the job before any real scanner has run. The 2–3
minute cargo install cost is still a net win over compiling
cargo-nextest / cargo-llvm-cov / cargo-deny / cargo-audit each from
source.
D4 — CVSS 4.0 version pins on cargo-deny + cargo-audit¶
RustSec began shipping CVSS 4.0 advisories in late 2025. cargo-deny
< 0.18 and cargo-audit < 0.22 trip on them with unsupported CVSS
version: 4.0. The component defaults pin cargo-deny@^0.18 and
cargo-audit@^0.22; consumers override only when they need a
specific version for some other reason.
D5 — Opt-in cross-OS + integration + coverage via when: never¶
rust-test exposes three booleans — enable_cross_os,
enable_integration, enable_coverage. When false (the default),
the corresponding jobs are not scheduled at all. The rule shape
mirrors go-test's enable_e2e:
Quoting the include-time interpolation as a string and comparing to
"true" (also a string) sidesteps GitLab's rule-expression engine
treating bare true as a variable reference. Without when: never
the disabled job sits in the pipeline UI as "skipped" — fine, but
noisy across 50+ MRs.
The cross-OS jobs further gate on $RUN_CROSS_OS_TESTS == "true"
even when enable_cross_os = true, so they stay dormant until
eligible SaaS macOS / Windows runners exist (otherwise they sit
stuck-pending and block the test stage).
D6 — release-plz token handling¶
The release-plz job has three subtleties the component encapsulates:
- Token ≠ CI_JOB_TOKEN. A tag pushed by CI_JOB_TOKEN doesn't
trigger downstream tag pipelines (GitLab loop-prevention). The
token input defaults to
$RELEASE_PLZ_TOKENand is wired through to a runtime variable (not interpolated into thescriptstring directly) so it doesn't leak into job-config logs. - Detached HEAD reattach. GitLab CI checks out at
$CI_COMMIT_SHAdetached; release-plz needs a branch with upstream to resolve the base branch.before_scriptre-attaches to$CI_COMMIT_BRANCHand sets its upstream. - Remote URL swap. The default
originuses CI_JOB_TOKEN; the component swaps it to agitlab-ci-token:<RELEASE_PLZ_TOKEN>@…URL so release-plz can push the Release MR branch and the version tag against a protected-rules repo.
--forge gitlab is required — release-plz defaults to the GitHub
forge and otherwise errors the repository is not hosted in GitHub.
Consumers (post-release)¶
| Project | Inline jobs replaced | Components consumed |
|---|---|---|
rust-tool-base |
rustfmt, clippy, cargo-deny, test:linux, test:macos, test:windows, integration-tests:linux, cargo-audit, trivy, gitleaks, cargo-doc, coverage, release-plz | all five (with enable_cross_os: true, enable_integration: true, enable_coverage: true) |
future projects derived from rust-tool-base |
(nothing inline yet) | inherit the full stack at fork time |
Self-test fixtures for the Rust track ship in v0.10.1 (tracked
separately) — same sequence as tofu-module-publish v0.7.0 → v0.7.1
and the Go track v0.9.0 → v0.9.1.
Follow-up¶
rust-tool-basewill need to keep its project-specificlibdbus-1-dev/pkg-config//etc/machine-idseeding in abefore_script:override on the test / clippy jobs, because that's driven by thecredentials-linux-persistentfeature and isn't generalisable to other Rust projects.- A future
rust-toolsimage (binstall + nextest + llvm-cov + deny + audit pre-installed) would letrust-test/rust-securityskip the bootstrap. Sibling toinfra-toolsfor Tofu; out of scope for v0.10.