Skip to content

Which pipelines each component runs on

Every component carries its own rules:. Nothing here is inherited from your pipeline, and including a component does not mean its job appears on every pipeline — most of them are deliberately silent on most pipeline types. This page is the lookup table for which.

Two things decide whether a job appears:

  1. The component's own rules:, fixed in the template.
  2. Your workflow: rules, which decide whether the pipeline exists at all. A component cannot run on a pipeline your workflow: never created.

The trigger matrix

Columns are GitLab pipeline sources. MR is merge_request_event; branch is a push to a non-default branch with no merge-request pipeline; default is a push to $CI_DEFAULT_BRANCH; tag is a tag push; schedule is a scheduled pipeline.

Component MR branch default tag schedule
go-lint
go-test
go-security
goreleaser
rust-lint
rust-test
rust-docs
rust-security
release-plz
svelte-build
svelte-lint
svelte-test
svelte-security
docs-verify
skill-security
tofu-lint
tofu-security
tofu-validate
tofu-plan
tofu-apply (plan_source: job)
tofu-apply (plan_source: ref)
tofu-stop ✅ (manual)
tofu-deploy-generate (mode: generate)
tofu-deploy-generate (mode: verify-fixtures)
tofu-module-publish
zensical-pages (zensical-build)
zensical-pages (pages)
hugo-pages (hugo-build)
hugo-pages (pages)
releaser-pleaser
release-stamp
discord-release
renovate-self
renovate-group
renovate-merge
release-train

✅ runs · — no rule matches, so no job is created · ❌ an explicit when: never suppresses it

Notes on individual rows:

  • hugo-build's MR and branch runs come from mr_gate, which defaults to true. That rule carries no pipeline-source condition, so the build gate fires on a merge request and on a plain branch push whenever a changes path was touched. Setting mr_gate: false (the loose direct-to-main mode) turns both columns into .
  • tofu-apply and tofu-deploy-generate are one job, not two. plan_source and mode select a hidden job that carries the rules:, so choosing the mode chooses the trigger. Two rows, one job.
  • renovate-group also runs off-schedule when $RENOVATE_TARGET_FILTER is set — the manual-run escape hatch for a single repo, on any pipeline its if input matches.
  • The default-branch column assumes your workflow: creates a default-branch pipeline. Many repositories only run one so that a release driver can maintain its Release MR.

Why almost no component runs on a scheduled pipeline

Every other component's first rule is if: $CI_PIPELINE_SOURCE == "schedule"when: never, unconditionally, before anything a consumer can influence.

A scheduled pipeline exists to do one job — run Renovate, or rebuild a time-sensitive site. Without the guard, every gate in the repository would also fire on that schedule: a full lint/test/security fan-out, nightly, on a commit nobody pushed and nobody is waiting on. On a fixed-concurrency self-hosted runner that is capacity taken from pipelines somebody is waiting on.

The guard is first and unconditional so that widening a component's if input cannot pull the job back onto the schedule. renovate-self, renovate-group, renovate-merge and release-train have no guard because the schedule is the only place they are meant to run; hugo-pages deliberately runs on a schedule so future-dated content publishes without a push.

release-train is the exception among the four: a scheduled run only ever plans, and it refuses to execute on a schedule even when explicitly told to, because cutting releases is a per-train decision and never a standing one.

Why change-detection never applies to a tag or a schedule

rules:changes compares a diff, and a tag or scheduled pipeline has no meaningful diff to compare — GitLab treats the filter as always-true there. Filtering a release job on something that is always true is not a filter, and filtering it on something unreliable would occasionally skip a release. So changes: is attached only to merge-request rules (and, for the static-site components, to the branch rule that feeds a deploy).

The reasoning is in Explanation: change-detection; the recipe is Add change-detection to a gate.

Which components let you change when they run

Only some. A component with an if input exposes its trigger condition; a component without one has its trigger welded to the job it does, because the trigger and the work are one decision.

Has an if input (overridable) No if input (fixed trigger)
go-lint, go-test, go-security all eight tofu-* components
rust-lint, rust-test, rust-docs, rust-security goreleaser
svelte-build, svelte-lint, svelte-test, svelte-security zensical-pages, hugo-pages
skill-security
release-plz, releaser-pleaser
renovate-self, renovate-group, renovate-merge
release-train, release-stamp, discord-release

The fixed ones are fixed for a reason worth knowing:

  • tofu-plan / tofu-apply authenticate by OIDC, and the AWS trust policy only accepts subjects from the refs it was written for. A job moved to a ref the trust policy does not name does not run a wrong plan — it fails at assume-role.
  • tofu-apply's trigger is coupled to plan_source: needs: and rules: both live in a per-mode hidden job the component extends, so choosing job or ref is choosing the trigger.
  • goreleaser and tofu-module-publish publish artefacts. Their tag match is the release gate; making it overridable would make it possible to publish from a branch.

Widening a trigger you can widen still cannot reach a scheduled pipeline — see above.

Why the Tofu gates run on branch pushes and the Go, Rust and Svelte gates do not

This is a real difference, not a documentation slip. tofu-lint, tofu-security, tofu-validate and the zensical-pages build job have no if: on their run rule at all — they exclude schedules and tags and then run on whatever is left, which includes a plain branch push. The Go, Rust and Svelte gates default their if input to $CI_PIPELINE_SOURCE == "merge_request_event" and so run on merge requests only.

On a project with the usual workflow: dedup rule this is invisible, because no branch pipeline is created once a merge request is open:

workflow:
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    - if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS'
      when: never
    - if: '$CI_COMMIT_TAG'
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
    - if: '$CI_PIPELINE_SOURCE == "schedule"'

Without that dedup rule, a push to a branch that already has an open merge request runs the Tofu gates twice — once on the branch pipeline, once on the merge-request pipeline. Keep the $CI_COMMIT_BRANCH guard on the never rule: keying it on $CI_PIPELINE_SOURCE == "push" also matches a tag push and suppresses the entire tag pipeline, which is how a release silently stops publishing.

What "no rule matched" looks like

A component whose rules do not match produces no job. There is no skipped job, no grey icon, no entry in the pipeline graph — the job simply is not there. The pipeline goes green having run nothing.

That is the failure mode to expect when a component appears to have been ignored, and it is the first thing to check: not "why did my job fail" but "is my job in the pipeline at all". See Diagnose a job that did not run.

See also