4. Paved-Road CI
CI/CD is where platform engineering becomes visible in day-to-day work. A developer opens a merge request, the pipeline starts, tests run, scanners report findings, artifacts are created, and deployment rules decide what can happen next.
A paved-road pipeline is the default path teams should want to use. It should be easier than writing custom CI from scratch, and it should already satisfy the platform’s minimum security contract.
CI/CD in plain language
Section titled “CI/CD in plain language”CI means continuous integration. It is the practice of integrating changes frequently and using automated checks to find problems early. A CI pipeline usually builds the code, runs tests, and produces feedback on a merge request.
CD usually means continuous delivery or continuous deployment. Delivery means the software is kept in a releasable state. Deployment means the pipeline can push changes into an environment automatically when rules allow it.
For a secure delivery platform, CI/CD is more than automation. It is the place where the platform can require evidence:
- Was the code tested?
- Were dependencies scanned?
- Was a container image built from a pinned base?
- Was an SBOM produced?
- Was the artifact signed?
- Did a protected ref use a trusted runner?
- Were deployment credentials kept out of untrusted jobs?
Golden paths, not golden cages
Section titled “Golden paths, not golden cages”Platform teams often describe paved roads or golden paths as recommended ways to build, test, deploy, and operate software. The useful version is opinionated but not suffocating. It gives teams a working path with security controls, artifact handling, observability hooks, and operational expectations already wired in.
That matters because CI/CD is where developer experience and security policy collide. If the secure path is slow, unclear, or incompatible with common application patterns, teams will create local shortcuts. If the platform path is simple and extensible, teams get speed and the organization gets consistency.
The paved road in this series has two layers. Reusable GitLab CI/CD components provide the normal implementation surface. GitLab security policies and policy-as-code checks make sure the minimum delivery contract is still present when teams customize their pipelines.
Required stages
Section titled “Required stages”This platform requires five stages for production repositories:
stages: - build - test - security_scan - package - deployApplication teams can add jobs, but they cannot remove the mandatory control surface. That is the difference between a template and an enforcement model. A template is easy to ignore. A policy-injected baseline is much harder to bypass.
Merge request workflow
Section titled “Merge request workflow”Production repositories should use a mandatory merge request workflow:
- Protect default branches and release tags.
- Disallow direct commits to protected branches.
- Require merge request review before merge.
- Require successful pipelines before merge.
- Require CODEOWNERS approval for sensitive paths.
- Keep deployment jobs tied to protected refs and protected environments.
This gives teams a normal path for change while keeping high-risk actions out of unreviewed branches.
Reusable components
Section titled “Reusable components”GitLab CI/CD components provide the reusable implementation path. The reference component exposes inputs for stage names and artifact naming while preserving the same delivery shape:
spec: inputs: build_stage: default: build test_stage: default: test security_stage: default: security_scan package_stage: default: package deploy_stage: default: deployThe jobs are deliberately simple in the baseline. For example, the build job proves that the pipeline has a commit SHA, creates an artifact directory, and records build output. Real product teams would replace the body with language-specific build logic, but the stage contract stays the same.
paved-road:build: stage: $[[ inputs.build_stage ]] script: - test -n "${CI_COMMIT_SHA}" - mkdir -p dist - printf '%s\n' "${CI_COMMIT_SHA}" > "dist/$[[ inputs.artifact_name ]].build"Shared components should be versioned and pinned by immutable references where practical. A team can still upgrade, but the upgrade appears as a reviewable change instead of silently shifting under every repository.
The platform should publish reusable components for common workflow families:
- build and test
- release and artifact publishing
- container image creation
- infrastructure plan and apply
- SBOM generation
- signing and provenance
- deployment gates
Mandatory security controls
Section titled “Mandatory security controls”The injected policy pipeline includes GitLab security templates for SAST, dependency scanning, secret detection, container scanning, and IaC scanning:
include: - template: Jobs/SAST.gitlab-ci.yml - template: Jobs/Dependency-Scanning.gitlab-ci.yml - template: Jobs/Secret-Detection.gitlab-ci.yml - template: Jobs/Container-Scanning.gitlab-ci.yml - template: Jobs/SAST-IaC.gitlab-ci.ymlThe policy also requires provenance inputs on protected refs:
platform:provenance-required: stage: package rules: - if: '$CI_COMMIT_REF_PROTECTED == "true"' script: - test -f sbom.cdx.json - test -f provenance.intoto.jsonl - test -f signature.bundleAt this point, paved-road CI becomes supply chain infrastructure. The pipeline builds and tests the code, and it also produces evidence that later deployment, audit, and incident workflows can use.
Policy tests
Section titled “Policy tests”OPA gives the platform team another place to assert minimum CI behavior. The reference Rego policy rejects missing stages, skip CI, mutable latest image tags, and images that are not pinned by digest:
required_stages := {"build", "test", "security_scan", "package", "deploy"}
deny contains msg if { missing := required_stages - {stage | stage := input.ci.stages[_]} count(missing) > 0 msg := sprintf("missing required stage(s): %v", [sort(missing)])}
deny contains "skip CI cannot be allowed by platform policy" if { object.get(input.ci, "skip_ci_allowed", false)}This policy does not replace GitLab enforcement. It complements it by making the platform contract testable outside any single repository.
The CI contract
Section titled “The CI contract”Teams own their application build details. The platform owns the minimum delivery contract.
That balance matters. If the platform owns too much, teams work around it. If it owns too little, security becomes optional. Paved-road CI should provide a small, versioned, enforceable center that teams can extend without weakening.
Infrastructure changes need the same treatment: a clear path, a reviewable plan, and policy checks before apply.