Includes, Templates, CI/CD Components, Component Catalog, and Reusable Pipeline Architecture: Configuration, Design Choices, and Tradeoffs
Choose deliberately among anchors, extends, includes, and components; decide where reuse should live, how narrowly components should be scoped, and how consumers should pin and migrate versions.
Learning objectives
- Choose among YAML anchors, extends, includes, and components based on ownership.
- Compare local reuse with centralized platform components.
- Select SHA, full SemVer, partial SemVer, ~latest, or branch policies deliberately.
- Design narrow components with least-privilege runtime assumptions.
- Define compatibility, testing, release, and migration policy.
include, CI/CD inputs, CI/CD components, the CI/CD
Catalog, and CI Lint are available on Free/Premium/Ultimate across
GitLab.com, Self-Managed, and Dedicated. Component references must
resolve on the same GitLab instance as the consuming project. The
mandatory labs require only a disposable Free project and local
configuration. Catalog publication is optional because publishing
requires a catalog-enabled component project, appropriate project
roles, a semantic-version release, and repository metadata. The
current glab repo publish catalog command is experimental
and therefore is not a mandatory production path.
1. Reuse is an architecture decision, not a DRY contest
Every extraction introduces an interface and an owner. The right question is not “can this YAML be shared?” but “who should be able to change it, how many consumers depend on it, what compatibility promise exists, and how will we prove the exact version used?”
2. Four reuse layers with increasing organizational coupling
| Mechanism | Boundary | Strength | Cost |
|---|---|---|---|
| YAML anchor | One YAML document | Very local structural reuse | Hard to use across files; YAML-level behavior only. |
extends |
Merged GitLab configuration | Job inheritance with GitLab semantics | Can become hard to reason about with deep chains. |
include |
File/project/remote/template boundary | Composable configuration split or central library | Merge order/provenance must be inspected. |
| CI/CD component | Versioned component project | Typed public contract, releases, Catalog discovery | Requires lifecycle ownership, tests, versioning, migration policy. |
3. Local reuse versus centralized platform ownership
Local includes maximize change atomicity: application and CI fragment move in the same commit. Central components reduce duplicated maintenance but create cross-repository dependency management. Centralization is justified when the platform team can actually maintain compatibility, publish releases, communicate deprecations, and operate an incident/rollback path.
4. Prefer narrow composable components over “one component to run the company”
A broad component tends to accumulate unrelated inputs, conditionals, credentials, runner assumptions, and hidden defaults. Narrow components create more explicit composition points. Good boundaries often align with one responsibility such as “run unit tests,” “build an OCI image,” or “publish a static report,” not “do CI.”
5. Version policy is part of the component API
| Consumer policy | Change intake | Risk | Recommended context |
|---|---|---|---|
| Full SHA | Only explicit update | Lowest silent-drift risk | High-assurance or externally reviewed dependency. |
| Full SemVer tag | Only explicit version update | Low if release governance is strong | Normal production catalog component consumption. |
Partial 1.4 |
Automatic compatible patch updates | Moderate | Teams accepting patch-line automation with good tests. |
~latest |
Automatic latest release | High | Experimentation, not critical delivery. |
| Branch | Every branch change | Highest | Component development/testing, not stable consumer contract. |
SemVer communicates intent; it cannot enforce honesty. Breaking behavior shipped under a patch version is still breaking behavior.
6. Ordinary project include: full commit SHA is the strongest pin
For include:project, a full commit SHA identifies exact
Git content. A tag or branch is easier for humans but can have
weaker governance or mutability depending on project policy. Record
the source project, file, resolved commit, reviewer, and reason for
trust.
7. Remote includes add DNS, TLS, server, and content availability to your pipeline
Remote configuration is not “just YAML.” Pipeline creation now
depends on an external host and content owner. If remote consumption
is unavoidable, review the exact bytes, use
include:integrity, monitor availability, and have a
fallback/migration plan. A cached remote include may improve fetch
behavior, but caching does not transform an untrusted source into a
trusted one.
8. Component privilege comes from the consumer runtime
A component does not carry a magical sandbox. Its jobs execute with runners, variables, job tokens, networks, services, and permissions made available by the consuming project. Therefore a small YAML diff can become code execution with production reach. Review images, scripts, shell expansion, API calls, inherited variables, and runner tags before adoption.
9. Compatibility contract and migration policy
Define what counts as major, minor, and patch change for every public component. Breaking changes include not only renamed inputs, but changed defaults, new required privileges, different runner tags, changed image/tool versions, altered artifact names, or new network dependencies.
| Change | SemVer expectation | Consumer action |
|---|---|---|
| Fix internal implementation with same observable contract | Patch | Normal dependency update/tests. |
| Add optional input with safe default | Minor | Adopt when convenient; regression test. |
| Remove/rename input or change required privilege | Major | Plan migration; maintain old major during transition. |
| Security fix that changes behavior materially | Context-dependent | Document impact explicitly; prioritize remediation over cosmetic version rules. |
10. Test reusable configuration as a product
A component project should lint its templates, run representative consumer pipelines, verify job names and outputs, test failure behavior, and create releases only after tests pass. Where possible, test the component from the current commit SHA so the test actually exercises the code being released.
11. Worked scenario: platform-wide quality check
Requirement: 40 repositories need a quality check that takes a stage name and strictness flag, requires no secrets, and must not change unexpectedly.
| Decision dimension | Choice | Rationale |
|---|---|---|
| Mechanism | Narrow CI/CD component | Typed contract and centralized ownership are justified at this scale. |
| Inputs | stage, strict |
Explicit bounded configuration; no secret input. |
| Version | Full release 2.3.1; record resolved SHA |
Readable compatibility contract plus provenance evidence. |
| Runner | Ordinary unprivileged runner | Component does not need host/network privilege. |
| Dependencies | Pinned image/tool versions | Reduce transitive drift. |
| Migration | Support major v2 while v3 is introduced | Consumers can migrate intentionally without surprise breakage. |
12. Catalog automation is currently an optional experimental CLI surface
Current documentation marks
glab repo publish catalog as
experimental and dependent on a feature flag. Do
not make a production release process rely on it without verifying
your GitLab instance and CLI version. The established documented
publication path uses a semantic tag and a release job with the
release keyword.
Knowledge check
When should a YAML anchor be preferred over a component?
When reuse is purely local structural duplication in one YAML document and does not need a cross-project versioned contract.
Why can a component be dangerous even if its YAML contains no literal secrets?
Its jobs can receive runtime variables/tokens and execute on the consumer’s runners/network, so the component code can still access sensitive capabilities.
What is the main architectural cost of centralizing CI configuration?
Consumers gain a dependency on another team/project’s lifecycle, compatibility discipline, availability, and release governance.
Does semantic versioning make a mutable dependency immutable?
No. A full released version is intended as a stable contract, but governance must protect tags/releases; a full commit SHA is the strongest exact Git-content identity.
What kind of component change may be breaking even if no input name changes?
A changed default, required runner privilege, artifact contract, image/tool behavior, or external dependency can break consumers.
Summary
Use the least powerful reuse abstraction that fits the ownership boundary. Local mechanisms minimize dependency complexity; centralized components require real product stewardship. Narrow contracts, immutable identity, representative tests, least privilege, SemVer discipline, and migration plans are the foundation of reusable pipeline architecture.
Official references
Primary sources used for the current GitLab 19.3 behavior taught in this lesson:
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.