How Ankra decides whether a stage runs
The rule at every branch is the same: a file Ankra could not see is not a file that did not change. An unreadable diff, a change of more than 300 files, a created branch or a force push all mean “run it”, and the run’spaths_unverified records why.
Run only what a change touches
There are two kinds of stage path filter. The difference is what happens to the stages after a skipped one.paths:makes a stage affected-only. When no changed file matches, the stage is skipped aspath_filter, and the stages that need it run without it. Use it for a test stage whose absence should hold nothing back.when.pathsskips the chain. The stages behind a skipped stage are skipped too, unless they list it inskippable_needs. Use it for a component’s whole build, scan, gate and publish chain in a monorepo. It is what the generated monorepo pipeline uses.
path_sets names a list once, so every stage reuses it and the pipeline file is in every list. Sets can include other sets, and ! excludes a pattern (the last matching pattern decides).
Pull requests check what they touch, pushes check everything. Use an event-scoped when block, so the pull request filters on paths and the push does not:
Follow the imports: affected:
A path glob has one blind spot: a package your tests import from elsewhere in the repository. test-api lists api/**, a change to libs/auth matches nothing, and the tests that would have caught the break are skipped. affected: names packages instead of files, and Ankra follows their imports.
gonames packages asgo testdoes:./...,./internal/api, or an import path. Everygo.modin the repository is read, so a localreplacecounts. A change togo.mod,go.sumorvendor/reaches every package of that module.pnpmnames workspace packages by name (@acme/web) or directory (./apps/web). Workspace dependencies are followed. A change to the root lock file, rootpackage.json,pnpm-workspace.yamlor a patch reaches every pnpm stage.paths:besideaffected:adds what no import graph sees, such as migrations or fixtures a test opens by path. Either one matching runs the stage.- The checkout reads the graph of the exact commit it checked out, in seconds. No extra step, nothing cached. A stage is skipped only when the checkout positively answered that no change reaches it. A pattern that names nothing, a failed analysis, or one that took over two minutes runs the stage.
- A skip never waits for capacity. A not-affected stage is concluded as soon as the checkout answers, without taking a CI slot.
Newest commit wins
Push three commits to a branch in a minute, and only the last one’s result matters.cancel_in_progress: truecancels the in-flight run when a newer one arrives in the same group. Every pipeline Ankra generates sets it.cancel_queued: trueis the gentler choice: it keeps one running and one waiting run per group, cancelling older runs that have not started, and never the running one.- Superseded is not failed. The older run concludes cancelled, shows as Superseded in grey, leaves a neutral status on GitHub, GitLab or Bitbucket, and raises no failure notification.
ankra pipeline validate gives advice (pipeline_concurrency_queues_behind:) when a per-branch group would queue a branch behind itself.
Skip a push with [skip ci]
A pushed commit whose message carries [skip ci], [ci skip], [no ci], [skip ankra] or [ankra skip], or ends with a skip-checks: true trailer, records a skipped run instead of queueing one. The check is still posted.
Markers are honoured where they are safe and nowhere else:
Full runs: set the filters aside
Sometimes you want every stage, whatever the paths say: before a release, after a toolchain bump, or to prove a flaky skip.Build once, promote on merge
A merge tomain normally builds and tests the merged commit again, even though the pull request run already did all of that for the same files. promote_from re-tags the tested image instead.
See what you saved
Every run records the CI time it did not spend because of a path filter, the dependency graph or a promotion, estimated from the median of that stage’s recent successful runs.
The figure is runner time, the seconds the skipped steps would have run added up. A stage with no usable history counts as unknown, never as zero and never as a guess.