Rule:changes sees changes where there are none

Does the rules:changes work properly?

I have the following gitlab-ci pipeline:

stages:
  - choose

choose:
  stage: choose
  rules:
    - if: $CI_COMMIT_BRANCH
      changes:
        paths:
          - file1
      variables:
        variable: "name1"
    - if: $CI_COMMIT_BRANCH
      changes:
        paths:
          - file2
      variables:
        variable: "name2"
    - if: $CI_COMMIT_BRANCH
      changes:
        paths:
          - file3
      variables:
        variable: "name3"
  script:
    - echo "variable=$variable" >> build.env && cat build.env
    - echo "changed files = " && git diff-tree --no-commit-id --name-only -r $CI_COMMIT_SHA
  artifacts:
    reports:
      dotenv: build.env

Everything works correctly when I add something to one of the files. In this case $variable equals name of the file that has been changed.
But in case when I delete something in the file 2 or file 3 (without adding any info to the files!), then the $variable equals name1 but git diff-tree --no-commit-id --name-only -r $CI_COMMIT_SHA correctly shows that there were only changes in file 2 or file 3. I expected that $variable would be equal the name of the changed file (2 or 3).
So what’s the problem with rules:changes or I don’t get something?

  • GitLab Enterprise Edition 15.11.2-ee
  • Runner gitlab-runner 15.1.0

Hi @Butovoboy :wave: welcome to the GitLab Community forum! :tada:

The problem you’re encountering seems to be related to the order in which your rules are evaluated.

GitLab’s CI/CD pipeline evaluates rules in the order they are defined until it finds a match, and then it stops.

This means that if more than one rule could potentially match a commit, only the first matching rule will be applied.

In your case, if you delete something in file2 or file3, the first rule matches because file1 has not changed, and the $variable is set to name1.

To resolve this issue, you might consider revising your CI job or reordering your rules. For example, if you change the rules for file2 and file3 come before the rule for file1. That way, if file2 or file3 are changed, their rules will match first and set $variable accordingly.

You can find more detailed information about the rules:changes keyword in the GitLab documentation.

I hope this helps! Let us know if you have any other questions. Happy coding! :rocket:

1 Like

Hi @gitlab-greg!
Thanks a lot for your answer!

But I face with this issue even if I delete something only in file2, and keep file1 unchanged. I thought that in this case the first rule would not match, but the second does and the $variable will be equal name2.
On the other hand, when I add something only to file2 everything goes right and $variable equals name2.
So I can’t figure out where is the problem in my pipeline.

Thank you, Greg, for trying to help me :grin:

I found out where it’s all.
When you branch from master, gitlab-ci thinks that all files file1, file2 and file3 have been changed so the first if is true and $variable equals name1.
So I modified the pipeline to compare files to master branch adding compare_to: 'refs/heads/master'. But in this case stage choose does not work when you merge dev branch to master so in this case I added another three ifs and now the whole pipeline looks like this:

stages:
  - choose

choose:
  stage: choose
  rules:
    - if: $CI_COMMIT_BRANCH
      changes:
        compare_to: 'refs/heads/master'
        paths:
          - file1
      variables:
        variable: "name1"
    - if: $CI_COMMIT_BRANCH
      changes:
        compare_to: 'refs/heads/master'
        paths:
          - file2
      variables:
        variable: "name2"
    - if: $CI_COMMIT_BRANCH
      changes:
        compare_to: 'refs/heads/master'
        paths:
          - file3
      variables:
        variable: "name3"
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
      changes:
        paths:
          - file1
      variables:
        variable: "name1"
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
      changes:
        paths:
          - file2
      variables:
        variable: "name2"
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
      changes:
        paths:
          - file3
      variables:
        variable: "name3"
  script:
    - echo "variable=$variable" >> build.env && cat build.env
  artifacts:
    reports:
      dotenv: build.env