Building GitLab CI Components
Overview
GitLab CI components are reusable pipeline configuration units that can be versioned, shared, and discovered through the CI/CD Catalog. This skill guides you through creating complete, correct components following GitLab's official structure.
Core principle: Follow the GitLab documentation systematically. Components have specific requirements for directory structure, files, inputs, and CI/CD configuration that must be met for valid components.
When to Use
Use this skill when:
- •Creating a new reusable CI/CD component
- •Packaging existing pipeline configuration for sharing
- •Publishing components to the GitLab CI/CD Catalog
- •Setting up a component project structure
Critical Requirements Checklist
These are commonly missed - verify each one:
- • Directory structure:
templates/directory with components as.ymlfiles or subdirectories withtemplate.yml - • Required files: README.md, LICENSE.md, .gitlab-ci.yml present
- • Testing configuration: .gitlab-ci.yml includes jobs to test component behavior
- • Publishing configuration: .gitlab-ci.yml includes release job for catalog publishing
- • Input specification:
spec:inputssyntax correct with proper types and validation - • Input usage: Inputs referenced as
$[[ inputs.field-name ]]in template - • YAML separator:
---separator present between spec and job definitions - • No hardcoded values: Use
$CI_SERVER_FQDNand inputs instead of hardcoded domains/values
Directory Structure
Single Component Project
my-component/ ├── templates/ │ └── my-component.yml # Component definition ├── README.md # Documentation with usage examples ├── LICENSE.md # Required license file └── .gitlab-ci.yml # Testing and publishing
Multi-Component Project
my-components/ ├── templates/ │ ├── component-one.yml # Simple single-file component │ ├── component-two/ # Multi-file component │ │ ├── template.yml # Main template │ │ └── supporting-script.sh # Supporting files │ └── component-three.yml ├── README.md # Covers all components ├── LICENSE.md └── .gitlab-ci.yml
Limits:
- •Maximum 100 components per project (GitLab 18.5+)
- •Earlier versions: 30 components maximum
Component Template Structure
Basic Template with Inputs
spec:
inputs:
stage:
type: string
default: test
description: "Pipeline stage for the job"
dockerfile_path:
type: string
default: Dockerfile
description: "Path to Dockerfile"
image_name:
type: string
description: "Docker image name (required)"
image_tag:
type: string
default: latest
description: "Docker image tag"
---
build-docker-image:
stage: $[[ inputs.stage ]]
image: docker:latest
script:
- docker build -f $[[ inputs.dockerfile_path ]] -t $[[ inputs.image_name ]]:$[[ inputs.image_tag ]] .
Key syntax:
- •
spec:inputs:defines configurable parameters - •
---separator required between spec and jobs - •
$[[ inputs.field-name ]]for referencing inputs - •Inputs without
defaultare required
Input Specification
Input attributes:
- •
type: Data type (string, number, boolean, array) - •
default: Default value (makes input optional) - •
description: Documents the input purpose
Validation options:
- •
type: Enforces data type - •
regex: Pattern validation (e.g.,^v\d+\.\d+(\.\d+)?$) - •
options: Restricts to allowed values (e.g.,['dev', 'staging', 'prod'])
Empty spec handling:
# If no inputs needed, use empty spec (not blank)
spec: {}
---
Complete input specification reference: https://docs.gitlab.com/ci/inputs/
.gitlab-ci.yml for Testing
# Test the component works correctly
test-component:
stage: test
trigger:
include:
- component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/my-component@$CI_COMMIT_SHA
inputs:
image_name: test-image
image_tag: test-tag
.gitlab-ci.yml for Publishing
# Publish to catalog when a tag is created
release:
stage: deploy
image: registry.gitlab.com/gitlab-org/release-cli:latest
rules:
- if: $CI_COMMIT_TAG =~ /^\d+\.\d+\.\d+$/ # Semantic version tags only
script:
- echo "Releasing version $CI_COMMIT_TAG"
release:
tag_name: $CI_COMMIT_TAG
description: "Release $CI_COMMIT_TAG"
Versioning requirements:
- •Use semantic versioning:
1.0.0,2.3.4, etc. - •Tag precedence: commit SHA > tag > branch
- •Partial versions supported:
1.2matches latest1.2.* - •Use
~latestfor absolute latest version (not recommended for production)
README.md Requirements
# Component Name
Brief description of what the component does.
## Components
### component-name
Description of component functionality.
#### Inputs
| Input | Type | Default | Required | Description |
|-------|------|---------|----------|-------------|
| stage | string | test | No | Pipeline stage |
| image_name | string | - | Yes | Docker image name |
#### Usage
\`\`\`yaml
include:
- component: $CI_SERVER_FQDN/my-org/my-components/component-name@1.0.0
inputs:
image_name: myapp
image_tag: v1.2.3
\`\`\`
## Contributing
Guidelines for contributing to this component.
Required sections:
- •Component summary and capabilities
- •Input documentation (use table format)
- •Usage examples with
$CI_SERVER_FQDN(never hardcode domain) - •For multi-component projects: table of contents and sections per component
Best Practices
Avoid Hardcoding
# ❌ BAD: Hardcoded values script: - curl https://gitlab.example.com/api/v4/projects # ✅ GOOD: Use built-in variables script: - curl $CI_API_V4_URL/projects
Use predefined variables instead of hardcoded values:
- •
$CI_SERVER_FQDNfor domain names - •
$CI_API_V4_URLfor API references - •Inputs for user-configurable values
All predefined variables: https://docs.gitlab.com/ee/ci/variables/predefined_variables.html
Avoid Global Keywords
# ❌ BAD: Global default affects all jobs default: image: alpine:latest # ✅ GOOD: Define reusable config with extends .base-config: image: alpine:latest my-job: extends: .base-config
Global keywords like default: affect the entire pipeline, not just your component.
Component Usage Format
include:
- component: <FQDN>/<project-path>/<component-name>@<version>
inputs:
field: value
Example:
include:
- component: $CI_SERVER_FQDN/my-org/security/secret-detection@1.0.0
inputs:
stage: security-scan
fail_on_detection: true
Common Mistakes
| Mistake | Consequence | Fix |
|---|---|---|
Missing templates/ directory | Component not recognized | Create templates/ at project root |
Blank spec:inputs | JSON schema validation error | Use spec: {} if no inputs needed |
Missing --- separator | YAML parsing error | Add --- between spec and jobs |
| Wrong input reference syntax | Variable not interpolated | Use $[[ inputs.name ]] not ${inputs.name} |
| No .gitlab-ci.yml testing | Component breaks without detection | Add test jobs that use the component |
| No .gitlab-ci.yml release job | Manual publishing required | Add automated release on version tags |
| Hardcoded domains | Component not portable | Use $CI_SERVER_FQDN and variables |
| Missing required inputs | Pipeline error for users | Either add default or document as required |
Quick Start Workflow
- •
Create directory structure
bashmkdir -p my-component/templates touch my-component/README.md touch my-component/LICENSE.md touch my-component/.gitlab-ci.yml
- •
Create component template
- •Create
templates/my-component.yml - •Add
spec:inputswith validation - •Add
---separator - •Define jobs using
$[[ inputs.field ]]syntax
- •Create
- •
Document in README
- •Usage examples with
$CI_SERVER_FQDN - •Input table with types and descriptions
- •Contribution guidelines
- •Usage examples with
- •
Configure testing
- •Add test job to
.gitlab-ci.yml - •Test component with various input combinations
- •Add test job to
- •
Configure publishing
- •Add release job triggered by semantic version tags
- •Test with a pre-release tag first
- •
Verify checklist
- •Run through Critical Requirements Checklist above
- •Ensure no hardcoded values
- •Confirm all inputs documented
Reference Documentation
For complete details, see: