I have a NodeJS monorepo which I’m trying to process with GitLab CI.
I have a job that I want to trigger if the package.json in the root of the repository was changed. If a package.json in any workspace subfolder (/packages/**) of the repository was changed, I do not want to trigger the job.
Initially, I assumed this would be easy and I tried approaches like this:
test:
script:
- echo GitLab CI is such a pain sometimes
rules:
- if: '$CI_PIPELINE_SOURCE == "push"'
changes:
- ${CI_PROJECT_DIR}/package.json
- ./package.json
- /package.json
when: on_success
- when: never
None of these seemed to work.
I also tried to negate a ruleset with another:
test:
script:
- echo GitLab CI is such a pain sometimes
rules:
- if: '$CI_PIPELINE_SOURCE == "push"'
changes:
- package.json
when: on_success
- if: '$CI_PIPELINE_SOURCE == "push"'
changes:
- packages/**/package.json
when: never
- when: never
This also didn’t seem to have the desired effect, as the job is triggered on any changed package.json. This makes sense to me, as the documentation suggests that only the first successful rule is relevant.
I was certain that I had tried this as well with no success. So I tried to repro that just now in a minimal repo and you are right. This works exactly as expected. I must have done something else in my larger repo that causes me to get unexpected results.
I feel like my earlier mistakes were a result of not understanding the changes in relation to the specific push event properly. I finally found the article Redirecting... which touches on my earlier problem.
For example, changes is always true in certain pipeline types, including scheduled pipelines and pipelines for tags.
Is the key information I was missing earlier. I kept pushing commits that changed only package.json but also accompanying tags, which ultimately resulted in my changes being ineffective, as tag pushes don’t evaluate those.
Right, I think this trips a lot of people up. There’s a few little details like this that are tricky, like after_script runs in a separate shell, but before_script and script are concatenated together!