DiffCI

GitLab CI/CD component

See which tests a merge request affects, inside your GitLab pipeline.

The DiffCI observer is a GitLab CI/CD Catalog component. It adds one job that analyses each merge request and default-branch push, and writes a report saying which tests the change affects and why. It runs no tests, skips nothing, and cannot change the pipeline result.

Add the observer job

Add this to .gitlab-ci.yml and keep every existing test, build, lint and security job unchanged:

include:
  - component: $CI_SERVER_FQDN/diffci/diffci/observe@0.3.38

The component version is the exact @diffci.com/diffci CLI version it runs, so one number means one thing. Pin it like any other dependency and update it on purpose.

On the next merge request the pipeline shows a diffci-observe job beside your own jobs. It needs a runner with Docker; gitlab.com shared runners work as they are.

What the job does

  • Runs only where a commit range exists: merge request pipelines and pushes to the default branch. It does not run for other branch pushes, schedules, or pipelines started from the web or API, and it runs once per change in projects that have both branch and merge request pipelines.
  • Never becomes the pipeline result. It starts at once (needs: []) and uses allow_failure: true, so a DiffCI problem shows as a warning, not a failed pipeline.
  • Clones full history (GIT_DEPTH: "0"). GitLab's default 20-commit clone can drop a merge request's base commit.
  • Leaves your checkout alone. It installs the pinned CLI outside the project with --ignore-scripts and writes the report outside it while observing, so your lockfile and node_modules are untouched.
  • Sends nothing. The report stays in your pipeline as an artifact.

In a merged-results pipeline the observer diffs the source branch head, not the merge commit GitLab creates.

Read the report

The job uploads .diffci/diffci-observation.json as an artifact, linked from the merge request as "DiffCI observation". It records the commit range, the changed files, the tests the change affects with the reason for each, and every reason DiffCI fell back to a full run.

Later jobs can read the outcome. Add needs: [diffci-observe] and use $DIFFCI_STATUS:

DIFFCI_STATUSMeaning
OBSERVEDThe change was analysed. The report says whether a selective plan was safe or a full run is needed.
REFUSEDDiffCI could not establish a trustworthy range or graph. Run your normal pipeline; nothing was decided.
ERRORThe observer itself failed. The pipeline result is unaffected.

Inputs

InputDefaultMeaning
stagetestStage of the observer job. It starts at once whatever the stage.
job-namediffci-observeJob name. Redefine the job under this name to override rules or allow_failure.
imagenode:22Needs Node 22+, Git and Bash. Debian-based Node images qualify; Alpine ones lack Bash. Pin by digest in production.
cli-versionthe component versionExact published @diffci.com/diffci version.
redact-pathsfalseReplace every file path in the report with a stable 12-character digest.
artifact-expire-in7 daysHow long GitLab keeps the report artifact.
include:
  - component: $CI_SERVER_FQDN/diffci/diffci/observe@0.3.38
    inputs:
      stage: verify
      redact-paths: true

When it reports a full run

A conservative selection is only useful if it knows when to stop. The observer reports a full run, with the reason named, when:

  • Pipeline configuration changed: .gitlab-ci.yml, a file it includes, or another CI definition.
  • The dependency graph is incomplete: an import or configuration DiffCI cannot resolve, a lockfile or root configuration change, or a file type it does not model.
  • The range is untrustworthy: a missing base commit or a pipeline with no comparable range.

Unknown is never treated as "no tests affected". Review the language and framework support matrix before interpreting an ecosystem-specific fallback.

A measured run on gitlab.com

We imported jshttp/cookie (MIT, Vitest, four spec files) into a private gitlab.com test project and added only the include: above, with no dependency install. Each observer job took 34–40 seconds on shared runners.

ChangePipelineReport
Edit .gitlab-ci.yml on the default branchPushOBSERVED, full run: pipeline configuration changed
Edit one spec fileMerge requestOBSERVED, 1 of 4 specs affected
Edit src/index.tsMerge requestOBSERVED, 4 of 4 specs affected (every spec imports it)
Merge the spec changeDefault-branch pushOBSERVED, 1 of 4 specs affected
Push to another branchNoneNo observer job, as intended

The first version we ran, 0.3.28, reported a full run for every change in this project: its bundled TypeScript rejected the repository's TypeScript 6 options. Version 0.3.29 accepts them. One small repository is a functional check, not a savings claim; the open benchmark covers measurement.

Troubleshooting

SymptomWhy it happensWhat to do
No diffci-observe jobThe pipeline is not a merge request or default-branch push, or the project's own workflow:rules exclude merge request pipelines.Open a merge request, or allow merge_request_event pipelines in workflow:rules.
Job fails with bash: not foundAn Alpine image was set through image.Use a Debian-based Node 22+ image.
REFUSED on a merge requestThe base commit is missing, usually because a project-level GIT_DEPTH overrides the component's.Keep GIT_DEPTH: "0" for the observer job.
Every report is a full runA configuration DiffCI cannot resolve marks the graph incomplete. The report's fallback reasons name it.Read the reasons, check the support matrix, and open an issue with the redacted report if it looks wrong.
Pipelines never start on a new gitlab.com accountGitLab asks new accounts to verify before using shared runners.Complete GitLab's account verification, then rerun.

What it does not do yet

  • It does not skip or reorder jobs. The observer reports; your pipeline runs as before. Enforcement on GitLab, through a generated child pipeline with a full-pipeline fallback, is planned for after design partners.
  • It does not plan GitLab jobs. It selects tests, but does not yet read .gitlab-ci.yml to say which of your jobs a change affects. Changes to pipeline files already make it report a full run.
  • It does not send reports to DiffCI Cloud. Cloud accepts GitHub repositories only today.
  • Self-managed GitLab: components work there, but the catalog is per instance. Copy the component project to your instance for now.