GitLab CI
Generate a .gitlab-ci.yml pipeline with reusable job templates defined as functions and rule-based conditional execution.
Source
examples/gitlab-ci/main.rank
/// Generates a GitLab CI pipeline configuration.
/// Shows reusable job templates defined as functions, stage ordering,
/// and rule-based conditional job execution.
use std::Emit
use std::Path
Rule = Object {
`if`: string,
}
Job = Object {
stage: string,
image: string,
script: [string],
rules?: [Rule],
needs?: [string],
artifacts?: Object {
paths: [string],
expire_in: string,
},
}
Pipeline = Object {
stages: [string],
variables: Object { ...: string },
...: Job,
}
stages: [string] = [`build`, `test`, `deploy`]
mr_only: Rule = { `if`: `$CI_PIPELINE_SOURCE == "merge_request_event"` }
main_only: Rule = { `if`: `$CI_COMMIT_BRANCH == "main"` }
always: Rule = { `if`: `$CI_PIPELINE_SOURCE != ""` }
node_job = |stage: string, script: [string]| -> Job {
return {
stage: stage,
image: `node:20-alpine`,
script: script,
}
}
build: Job = node_job(`build`, [`npm ci`, `npm run build`]) with {
artifacts: {
paths: [`dist/`],
expire_in: `1 hour`,
},
rules: [always],
}
test: Job = node_job(`test`, [`npm ci`, `npm test`]) with {
rules: [always],
needs: [`build`],
}
deploy_staging: Job = node_job(`deploy`, [`./scripts/deploy.sh staging`]) with {
rules: [mr_only],
needs: [`test`],
}
deploy_prod: Job = node_job(`deploy`, [`./scripts/deploy.sh production`]) with {
rules: [main_only],
needs: [`test`],
}
pipeline: Pipeline = {
stages: stages,
variables: {
DOCKER_DRIVER: `overlay2`,
NODE_ENV: `ci`,
},
build: build,
test: test,
`deploy:staging`: deploy_staging,
`deploy:prod`: deploy_prod,
}
pub main = || Emit::manifest({
entries: [
{
path: Path::join([`.gitlab-ci.yml`]),
format: `yaml`,
value: pipeline,
},
]
})
Output
# .gitlab-ci.yml
stages:
- build
- test
- deploy
variables:
DOCKER_DRIVER: overlay2
NODE_ENV: ci
build:
stage: build
image: node:20-alpine
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/
expire_in: 1 hour
rules:
- if: '$CI_PIPELINE_SOURCE != ""'
test:
stage: test
image: node:20-alpine
script:
- npm ci
- npm test
needs:
- build
rules:
- if: '$CI_PIPELINE_SOURCE != ""'
deploy:staging:
stage: deploy
image: node:20-alpine
script:
- ./scripts/deploy.sh staging
needs:
- test
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
deploy:prod:
stage: deploy
image: node:20-alpine
script:
- ./scripts/deploy.sh production
needs:
- test
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
Key concepts
- Function-as-template —
node_jobreturns a baseJobthat callers customise withwith, avoiding repetition while keeping each job explicit. - Open object type —
Pipeline = Object { stages: ..., ...: Job }allows arbitrary additional fields (the job entries) beyond the declared ones. withfor optional fields — addingartifactsorrulesviawithkeeps the base template minimal and each job self-describing.
Run it
rank examples/gitlab-ci