Skip to content
Casey Labs

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 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?

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.

This platform requires five stages for production repositories:

stages:
- build
- test
- security_scan
- package
- deploy

Application 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.

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.

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: deploy

The 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

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.yml

The 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.bundle

At 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.

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.

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.