Skip to content

CI "rules:changes" does not work as expected for Merge Request Pipelines

I have the following simplified GitLab CI yaml:

image: node:14.16.1-alpine

include:
  - template: 'Workflows/MergeRequest-Pipelines.gitlab-ci.yml'

.modules_setup:
  cache:
    key: ${CI_COMMIT_REF_SLUG}
    paths:
      - node_modules/
    policy: pull
  before_script:
    - [...REMOVED...]

stages:
  - prepare
  - lint

build_deps:
  stage: prepare
  cache:
    key: ${CI_COMMIT_REF_SLUG}
    paths:
      - node_modules/
  before_script:
    - npm config set unsafe-perm true
    - apk update && apk upgrade
    - apk add --no-cache --virtual .build-deps alpine-sdk python libc6-compat gcompat
  script:
    - npm install -g node-gyp node-pre-gyp
    - npm ci
  rules:
    - changes:
        - package-lock.json
      when: always
    - when: never

lint_code:
  stage: lint
  extends: .modules_setup
  script:
    - npm run lint:code

The goal is that I have a build step for the dependencies which puts the resulting node_modules folder in the cache for later steps to use. This only needs to happen when new dependencies are added. In the context of a Merge Request I would expect the build_deps job to be added to the pipeline only if it's either a new merge request (so irrespective of changes to package-lock.json on the first run in a MR) or if a later commit in this MR changes the package-lock.json. For commits on this MR branch that are neither the first nor edit package-lock.json I would expect this job not to be added.

So it looks like this:

  • Create MR: Runs build_deps
  • Add new commit that does not change package-lock.json: Does NOT run build_deps
  • Add new commit that DOES change package-lock.json: DOES run build_deps
  • Add new commit that does not change package-lock.json: Does NOT run build_deps

This does not happen though. Every new commit to the MR branch will lead to the build_deps job to be added to the pipeline.

I do have a recovery script in the .modules_setup to regenerate the node_modules in the jobs if the cache is ever unavailable (as it's only best effort) but that's not the focus of this issue.

Previously I had a similar setup just without the include directive and instead of the rules on the build_deps job I had:

  only:
    changes:
      - package-lock.json

Configured like this, I get exactly the behaviour I outlined and want. But since enabling the Merge Request pipelines with the included template and switching over to rules:changes, it ALWAYS runs the build_deps job in a detached pipeline.

Sadly the meaning and implications of a detached pipeline is not clearly documented it seems.

Is there any way to get to my desired behaviour using Merge Request pipelines or will I have to revert?