Cross-Project Pipelines
glci supports both child pipelines (trigger: include:) and cross-project pipelines (trigger: project:) locally. Child pipelines share the parent’s Docker infrastructure. Cross-project triggers resolve the target project from a local directory or by cloning it from GitLab.
Child pipelines (trigger: include:)#
Child pipelines work the same as production GitLab CI parent-child pipelines. All include formats (string shorthand, local: map, forward:, inputs:) are supported.
glci-specific behavior#
- The child shares the parent’s mock server and runner containers.
- Child pipeline events are bridged to the parent’s event bus, so the TUI and
glci logdisplay them inline. - Nesting is capped at 2 levels deep (matching GitLab). Exceeding this produces:
child pipeline nesting depth 3 exceeds maximum 2. strategy: dependis recommended for local testing – makes the trigger job fail if the child fails.- The child receives
CI_PIPELINE_SOURCE=parent_pipelineandCI_PARENT_PIPELINE_ID. - Stopping the parent pipeline (
glci stop) automatically cancels in-flight child pipeline jobs via the Job-Status cancel protocol.
Variable forwarding#
By default, child pipelines inherit the parent’s base CI variables but not YAML-defined or CLI variables. Use forward: to control this:
| Forward setting | What it passes |
|---|---|
yaml_variables: true | Top-level variables: from the parent’s .gitlab-ci.yml |
pipeline_variables: true | Variables passed via --env on the CLI |
dotenv_variables: true | Variables loaded from --env-file |
Trigger inputs: are always passed regardless of forward settings.
Cross-project pipelines (trigger: project:)#
Cross-project triggers (trigger: project:) work the same as production GitLab CI, but glci needs to know where the target project’s source code lives locally.
Mapping projects to local directories#
Use --project-dir to map a project path to a local checkout:
glci run --project-dir group/other-project=../other-project
Or configure the mapping permanently in .glciconfig.toml:
[projects."group/other-project"]
dir = "../other-project"
branch = "main" # optional: override which branch to use
Relative paths in dir are resolved against the parent project’s working directory. The target directory must be a git repository.
Resolution order#
When glci encounters a trigger: project: job, it resolves the target in this order:
- CLI
--project-dirmapping – highest priority - Config
[projects]section in.glciconfig.toml - Git clone from GitLab – if a token is available, glci clones the target project as a bare repo (works for public repos without a token)
- Error with instructions – if none of the above succeed, glci prints a message showing how to configure the mapping
What requires a GitLab token#
| Feature | Token required | Works offline |
|---|---|---|
trigger: include: (child pipeline) | No | Yes |
trigger: project: with --project-dir | No | Yes |
trigger: project: with [projects] config | No | Yes |
trigger: project: (auto-clone from GitLab) | Yes (or public repo) | No |
include: project: in YAML | Yes | No |
include: component: in YAML | Yes | No |
include: local: in YAML | No | Yes |
include: remote: (HTTP URL) | No | Needs network |
include: template: | No | Needs network |
Cross-project variables#
Cross-project pipelines get fresh CI variables derived from the target project’s git state rather than inheriting the parent’s. This means CI_PROJECT_NAME, CI_COMMIT_SHA, CI_COMMIT_REF_NAME, and other git-derived variables reflect the target project.
The target pipeline receives CI_PIPELINE_SOURCE=pipeline (distinct from child pipelines which use parent_pipeline).
Dirty mode for cross-project targets#
When dirty mode is enabled (the default), glci overlays uncommitted and untracked files from local project directories onto the bare repo sent to the runner. This lets you test changes across multiple projects without committing first.
Multi-project setups#
For monorepos, standard child pipelines with different include paths work as expected. You can run a specific trigger job with glci run trigger-frontend.
For separate repositories that form a pipeline chain, map them all in .glciconfig.toml:
[projects."myorg/shared-lib"]
dir = "../shared-lib"
[projects."myorg/deploy-tools"]
dir = "../deploy-tools"
branch = "main"
include: project: in YAML#
include: project: works the same as production GitLab CI – it fetches YAML files from other projects via the GitLab Repository Files API at parse time. This always requires a GitLab token (see the token table above).
Nested includes within the fetched file that use local: are automatically rewritten to project: includes targeting the same remote project, matching GitLab’s behavior.
include: component: in YAML#
include: component: fetches CI/CD components from GitLab via the API at parse time. This always requires a GitLab token.
Version selectors#
Components are referenced with a version suffix after @. In addition to exact tag references, glci supports version selectors that resolve to the latest matching semver release tag:
| Selector | Example | Resolves to |
|---|---|---|
@~latest | mygroup/mycomp@~latest | Latest stable semver release tag |
@~N | mygroup/mycomp@~2 | Latest tag with major version 2 (e.g. 2.5.1) |
@~N.M | mygroup/mycomp@~2.3 | Latest tag with major.minor 2.3 (e.g. 2.3.7) |
Pre-release tags (e.g. 1.0.0-rc1) are excluded from version selector resolution. Only stable semver tags are considered.
Version selectors query the GitLab Tags API, so they always require a GitLab token and network access. Exact tag references (e.g. @1.2.3) also require a token but skip the tag listing step.
Troubleshooting#
If a trigger job fails, check the daemon logs for resolution and parsing errors:
glci log <pipeline-id> trigger-job-name
tail -f ~/.glci/daemon.log