Skip to content

Pipeline Concepts

llms.txt lists every page for an agent

Every exported Job declaration becomes a job entry in the generated .gitlab-ci.yml. The serializer handles the translation automatically:

GitLab Pipeline with stages (build, test, deploy) each containing jobs, with stage dependencies flowing left to right
GitLab CI pipeline hierarchy
  • Property names use spec-native snake_case (expire_in, allow_failure)
  • Converts export names to kebab-case job keys (buildApp becomes build-app)
  • Collects stages from all jobs into a stages: list
  • Collapses single-property objects (new Image({ name: "node:20" }) becomes image: node:20)
job-basic.ts
import { Job, Image } from "@intentius/chant-lexicon-gitlab";
export const buildApp = new Job({
stage: "build",
image: new Image({ name: "node:20" }),
script: ["npm ci", "npm run build"],
});

Produces this YAML:

stages:
- build
build-app:
stage: build
image: node:20
script:
- npm ci
- npm run build

The lexicon provides 3 resource types and 16 property types:

TypeDescription
JobA CI/CD job — the fundamental unit of a pipeline
WorkflowTop-level workflow: configuration (pipeline-level rules, name, auto_cancel)
DefaultTop-level default: block (shared defaults inherited by all jobs)
TypeUsed inDescription
ImageJob, DefaultDocker image for the job runner
CacheJob, DefaultFiles cached between pipeline runs
ArtifactsJobFiles passed between stages or stored after completion
RuleJob, WorkflowConditional execution rules (rules: entries)
EnvironmentJobDeployment target environment
TriggerJobDownstream pipeline trigger
IncludeWorkflowExternal YAML file inclusion
AllowFailureJobFailure tolerance configuration
RetryJobAutomatic retry on failure
ParallelJobJob parallelization (matrix builds)
ReleaseJobGitLab Release creation
AutoCancelWorkflowPipeline auto-cancellation settings
NeedJobJob dependency for DAG-mode execution
InheritJobControls which global defaults a job inherits
ServiceJob, DefaultSidecar service container (e.g. Docker-in-Docker, databases)
WorkflowRuleWorkflowConditional rules for pipeline-level execution

Extract reusable objects into a shared config file and import them across your pipeline files:

pipeline-shared-config.ts
import { Job } from "@intentius/chant-lexicon-gitlab";
import { nodeImage, npmCache, buildArtifacts } from "./config";
export const build = new Job({
stage: "build",
image: nodeImage,
cache: npmCache,
script: ["npm ci", "npm run build"],
artifacts: buildArtifacts,
});

A Job is the fundamental unit. Every exported Job becomes a job entry in the YAML:

job-test.ts
import { Job, Image, Artifacts } from "@intentius/chant-lexicon-gitlab";
export const test = new Job({
stage: "test",
image: new Image({ name: "node:20-alpine" }),
script: ["npm ci", "npm test"],
artifacts: new Artifacts({
paths: ["coverage/"],
expire_in: "1 week",
reports: { junit: "coverage/junit.xml" },
}),
});

Key properties:

  • script — required (or trigger/run). Array of shell commands to execute.
  • stage — which pipeline stage this job belongs to. Defaults to test if omitted.
  • image — Docker image. Use new Image({ name: "..." }) or pass a string to the YAML.
  • needs — job dependencies for DAG-mode execution (run before stage ordering).

Stages define the execution order of a pipeline. The serializer collects unique stage values from all jobs:

stages.ts
import { Job } from "@intentius/chant-lexicon-gitlab";
export const lint = new Job({ stage: "test", script: ["npm run lint"] });
export const unitTest = new Job({ stage: "test", script: ["npm test"] });
export const stageBuild = new Job({ stage: "build", script: ["npm run build"] });
export const deploy = new Job({ stage: "deploy", script: ["npm run deploy"] });

Produces:

stages:
- test
- build
- deploy

Jobs in the same stage run in parallel. Stages run sequentially in declaration order.

Artifacts are files produced by a job and passed to later stages or stored for download. Caches persist files between pipeline runs to speed up builds. Both are shown in the shared config:

config.ts
export const npmCache = new Cache({
key: "$CI_COMMIT_REF_SLUG",
paths: ["node_modules/"],
policy: "pull-push",
});
export const buildArtifacts = new Artifacts({
paths: ["dist/"],
expire_in: "1 hour",
});
export const testArtifacts = new Artifacts({
paths: ["coverage/"],
expire_in: "1 week",
reports: { junit: "coverage/junit.xml" },
});
export const onMergeRequest = new Rule({

The key difference: artifacts are for passing files between stages in the same pipeline; caches are for speeding up repeated pipeline runs.

Rule objects control when a job runs. They map to rules: entries in the YAML:

rules-conditions.ts
import { Rule, Job, CI } from "@intentius/chant-lexicon-gitlab";
export const mrRule = new Rule({
if: CI.MergeRequestIid,
});
export const defaultBranchRule = new Rule({
if: `${CI.CommitBranch} == ${CI.DefaultBranch}`,
when: "manual",
});
export const deployJob = new Job({
stage: "deploy",
script: ["npm run deploy"],
rules: [defaultBranchRule],
});

Produces:

deploy:
stage: deploy
script:
- npm run deploy
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
when: manual

The if property maps directly to if: in the YAML. Use the CI pseudo-parameter object for type-safe variable references.

Environment defines a deployment target:

environment.ts
import { Environment, Job } from "@intentius/chant-lexicon-gitlab";
import { onDefaultBranch } from "./config";
export const prodEnv = new Environment({
name: "production",
url: "https://example.com",
});
export const deployProd = new Job({
stage: "deploy",
script: ["npm run deploy"],
environment: prodEnv,
rules: [onDefaultBranch],
});

GitLab tracks deployments to each environment and can roll them back from the UI.

Image specifies the Docker image for a job:

images.ts
import { Image } from "@intentius/chant-lexicon-gitlab";
export const alpineNodeImage = new Image({ name: "node:20-alpine" });
export const customImage = new Image({
name: "registry.example.com/my-image:latest",
entrypoint: ["/bin/sh", "-c"],
});

Workflow controls pipeline-level settings: when pipelines run, auto-cancellation, and global includes:

workflow.ts
import { Workflow, Rule, AutoCancel, CI } from "@intentius/chant-lexicon-gitlab";
export const workflow = new Workflow({
name: `CI Pipeline for ${CI.CommitRef}`,
rules: [
new Rule({ if: CI.MergeRequestIid }),
new Rule({ if: CI.CommitBranch }),
],
auto_cancel: new AutoCancel({
on_new_commit: "interruptible",
}),
});

Default sets shared configuration inherited by all jobs:

defaults.ts
import { Default, Image, Cache, Retry, CI } from "@intentius/chant-lexicon-gitlab";
export const defaults = new Default({
image: new Image({ name: "node:20-alpine" }),
cache: new Cache({ key: CI.CommitRef, paths: ["node_modules/"] }),
retry: new Retry({ max: 2, when: ["runner_system_failure"] }),
});

Jobs can override any default property individually.

Trigger creates downstream pipeline jobs:

trigger.ts
import { Job, Trigger } from "@intentius/chant-lexicon-gitlab";
export const deployInfra = new Job({
stage: "deploy",
trigger: new Trigger({
project: "my-group/infra-repo",
branch: "main",
strategy: "depend",
}),
});

chant graph --format ir|mermaid|dot draws the pipeline as one .gitlab-ci.yml box holding its jobs, and draws the dependencies the jobs write as strings as edges:

Edge kindFromToFrom the source
needsjobjobneeds: ["build"], needs: [new Need({ job: "build" })], or the Job itself; jobs are named by their kebab-cased export name
needs (via stage)jobjoba job with no needs: waits for every job in the nearest earlier stage, and further back past any job there that starts early with needs:
extendsjobjobextends: "base-job"
triggersjobpipelinetrigger:, a child pipeline file or another project
includespipelinefilethe Pipeline entity’s include:

Stage order follows the stages: list the build writes: the Pipeline entity’s list, then the stages jobs name in first use, or GitLab’s build, test, deploy when nothing names one. A job with no stage is in test.

trigger:, include: and a cross-project or parent-pipeline needs: { project, job, ref } name a pipeline or file this project does not generate, so they are listed under dangling in the IR with crossPipeline: true, drawn as dashed edges to a (not declared) stub. So is a job name nothing declares, such as a hidden template from an included file. See chant graph.

chant