1

I have a CI build stage that runs whenever someone pushes to the repo, and it takes a long time to run. So I want to configure a .gitlab-ci.yml rule that says if the user is only updating the documentation in README.md, it doesn't need to execute the build stage.

Following the tips in How to exclude gitlab-ci.yml changes from triggering a job, it seems like the way to do this would be to add something like:

stages:
 - A

Stage A:
  stage: A
  rules:
    - changes:
      - "README.md"
      when: never

However, based on the documentation in https://docs.gitlab.com/ee/ci/yaml/#ruleschanges, I think this will also suppress the stage if the push contains multiple files, if one of them is README.md.

In that situation, I want the stage to run, not be suppressed. Is there a way to handle this distinction?

LWixson
  • 319
  • 3
  • 13
  • You might be able to get a list of changed files for the commit and only push if the list is not simply README.md. (see the accepted answer here: https://stackoverflow.com/questions/424071/how-do-i-list-all-the-files-in-a-commit) – j_b Aug 10 '22 at 22:41
  • That's a good idea! I'll give it a try. – LWixson Aug 11 '22 at 17:06

2 Answers2

2

I use this syntax to not trigger pipelines when modifying *.md files

workflow:
  rules:
    - if: '$CI_COMMIT_BRANCH && $CI_COMMIT_BEFORE_SHA !~ /0{40}/'
      changes:
        - "{*[^.]md*,*.[^m]*,*.m,*.m[^d]*,*.md?*,*[^d]}"
    - if: '$CI_PIPELINE_SOURCE == "web"'
    - if: '$CI_PIPELINE_SOURCE == "schedule"'
    - if: '$CI_PIPELINE_SOURCE == "pipeline"'
    - if: '$CI_COMMIT_TAG'
Bigouden
  • 21
  • 3
0

Following the idea from @j_b, I was able to solve this (with the caveat that it exits with pipeline having been marked as failed, as described in the code below). Here is the relevant code I added to my .gitlab-ci.yml.

stages:
  - Check Only Docs Changed
  - mybuild

# Stage that checks whether only documentation changed.  If so, then we won't build a new release.
check-only-docs-changed:
  stage: Check Only Docs Changed
  script:
    - BRANCH=origin/$CI_COMMIT_BRANCH
    - echo "Checking commit to see if only docs have changed.  "
    # The line below is from
    # https://stackoverflow.com/questions/424071/how-do-i-list-all-the-files-in-a-commit
    - GET_CMD="git diff-tree --no-commit-id --name-only -r $CI_COMMIT_SHA"
    - FILES_CHANGED=`$GET_CMD`
    - echo "Files in this commit:" $FILES_CHANGED
    - NUM_FILES_CHANGED=`$GET_CMD | wc -l`
    # We consider any file that ends in .md to be a doc file
    # The "|| true" trick on the line below is to deal with the fact that grep will exit with non-zero if it doesn't find any matches.
    # See https://stackoverflow.com/questions/42251386/the-return-code-from-grep-is-not-as-expected-on-linux
    - NUM_DOC_FILES_CHANGED=`$GET_CMD | grep -c ".*\.md$" || true`
    - echo $NUM_FILES_CHANGED "files changed," $NUM_DOC_FILES_CHANGED "of which were documentation."
    - |
      # We have to test whether NUM_FILES_CHANGED is > 0 because when one branch gets merged into another
      # it will be 0, as will NUM_DOC_FILES_CHANGED.
      if [[ $NUM_FILES_CHANGED -gt 0 && $NUM_FILES_CHANGED -eq $NUM_DOC_FILES_CHANGED ]]
      then
        DID_ONLY_DOCS_CHANGE="1"
        # Write out the env file before we exit.  Otherwise, gitlab will complain that the doccheck.env artifact
        # didn't get generated.
        echo "DID_ONLY_DOCS_CHANGE=$DID_ONLY_DOCS_CHANGE" >> doccheck.env
        echo "Only documentation files have changed.  Exiting in order to skip further stages of the pipeline."
        # Ideally it would be great to not have to exit with a non-zero code, because this will make the gitlab pipeline
        # look like it failed.  However, there is currently no easy way to do this, as discussed in
        # https://stackoverflow.com/questions/67269109/how-do-i-exit-a-gitlab-pipeline-early-without-failure
        # The only way would be to use child pipelines, which is more effort than it's worth for this.
        # See https://stackoverflow.com/questions/67169660/dynamically-including-excluding-jobs-in-gitlab-pipeline and
        # https://stackoverflow.com/questions/71017961/add-gitlab-ci-job-to-pipeline-based-on-script-command-result
        exit 1
      else
        DID_ONLY_DOCS_CHANGE="0"
        echo "DID_ONLY_DOCS_CHANGE=$DID_ONLY_DOCS_CHANGE" >> doccheck.env
      fi
  # The section below makes the environment variable available to other jobs, but those jobs
  # unfortunately cannot access this environment variable in their "rules:" section to control
  # whether they execute or not.
  artifacts:
    reports:
      dotenv: doccheck.env
LWixson
  • 319
  • 3
  • 13