Andrew Mercer
on this page

Sometimes a skel-included pipeline needs a fresh run — e.g. after fixing something in skel itself — without any actual code change in the project that includes it. include: files are resolved once, when the pipeline is created; retrying a failed job/pipeline reuses that already-resolved config, so it won't pick up a fix pushed to skel after the fact. You need a new pipeline, not a retry.

Option 1: Run pipeline from the UI (preferred)

CI/CD → Pipelines → Run pipeline, on the branch you want (usually main). Creates a new pipeline against the current tip, re-resolving every include: fresh. No commit, no code change.

Option 2: Empty commit

If you need a push-triggered pipeline specifically (e.g. scripting this, or a rule that only fires on push events):

git commit --allow-empty -m "chore: trigger rebuild (no code changes)"
git push

--allow-empty commits with no working-tree changes — git just creates a new commit object pointing at the same tree as its parent. Pushing it triggers a normal push pipeline, resolving include: against the current state of whatever it references.

Option 3: Pipeline trigger token (for automation)

For scripted/automated reruns with no git operation at all, create a pipeline trigger token (Settings → CI/CD → Pipeline triggers) and:

curl -X POST \
  -F token=<trigger-token> \
  -F ref=main \
  https://gitlab.com/api/v4/projects/<project-id>/trigger/pipeline

Why "Run pipeline" works but "Retry" doesn't

Whenever a pipeline is created — by a push, a schedule, the API, or clicking "Run pipeline" — GitLab does two things once, at that exact moment, and then freezes them for the lifetime of that pipeline:

  1. Resolves the ref to a specific commit SHA. Telling it to run on main locks the pipeline to whatever commit main points to right now. Visible in the job log as Checking out <sha> as detached HEAD (ref is main).

  2. Resolves and merges every include:. For an include with no ref: pinned, GitLab fetches whatever's currently on the included project's default branch at that same moment, and bakes the result into this pipeline's final, merged configuration.

Once both are resolved, the pipeline is fully defined and nothing about it changes afterward — even if new commits land on main in either repo while it's queued, running, or sitting there finished.

Retry is the snapshot. Retrying a job or pipeline reruns the exact same job against the exact same already-resolved commit SHA and the exact same already-resolved include: content from whenever that pipeline was originally created. It has no mechanism to re-fetch anything — which is why retrying after fixing skel didn't help: the pipeline being retried had already frozen in the old, broken version of the included file before the fix existed.

"Run pipeline" (and any newly created pipeline) is the opposite — a request to do the whole resolution process over again, from scratch, right now. main gets re-resolved to its current tip, and every include: gets re-fetched fresh.

Caveat: this depends on includes not pinning a ref:

The "always fetches current" behavior only holds because an include like

include:
  - project: 'andrewmercer/skel'
    file: '/release/.gitlab-ci-auto-release.yml'

has no ref:, so it defaults to the included project's default branch. If an include is ever pinned instead —

include:
  - project: 'andrewmercer/skel'
    file: '/release/.gitlab-ci-auto-release.yml'
    ref: v1.2.0   # or a specific commit SHA

— then even a brand-new "Run pipeline" keeps resolving to that pinned version forever, until the pin itself is bumped in the including project. That's a deliberate tradeoff some teams make for stability (nothing upstream can silently break your pipeline), but it means "just click Run pipeline to pick up the fix" stops working — the pin has to be bumped explicitly instead.