1

I have a build step in Gitlab CI/CD that I would like to skip if the only changing file is the readme file. My understanding is each entry listed under rules:changes is additive.

Additionally, the repository is set up as a "monorepo", in that there are different sub repositories listed under the packages directory.

Is there a way to specify under the rules section that there must be at least one changed file within packages/foo/, besides a change to packages/foo/readme.md?

sytech
  • 29,298
  • 3
  • 45
  • 86
David
  • 2,080
  • 5
  • 29
  • 44

1 Answers1

1

rules:changes: takes glob patterns. And yes, your understanding is correct: by nature, glob patterns are inclusive only, so you cannot use add exclusionary parameters to a glob pattern or "negate" specific items that would otherwise match. Adding additional items to the changes: is also additive.

You can, however use negative-matching to include non-matching files (e.g., !(foo|bar|baz)). This should work for your use case.

So, a rule like this should work how you want:

rules:
  - changes:
    - "packages/foo/**/!(readme.md)"

Edit:

However, ruby's fnmatch doesn't support the ! metacharacter, so instead you can use the pattern:

packages/foo/**/{[^r]*,r,r[^e]*,re,re[^a]*,rea,rea[^d]*,read,read[^m]*,readm,readm[^e]*,readme,readme[^.]*,readme.,readme.[^m]*,readme.m,readme.m[^d]*,readme.md?*}

reference

Which should have the same effect.

rules:
  - changes:
    # same as "packages/foo/**/!(readme.md)" 
    # SEE: https://stackoverflow.com/a/69906355/5747944
    - 'packages/foo/**/{[^r]*,r,r[^e]*,re,re[^a]*,rea,rea[^d]*,read,read[^m]*,readm,readm[^e]*,readme,readme[^.]*,readme.,readme.[^m]*,readme.m,readme.m[^d]*,readme.md?*}'

Tested in irb:

irb(main):011:0> pattern = './packages/foo/**/{[^r]*,r,r[^e]*,re,re[^a]*,rea,rea[^d]*,read,read[^m]*,readm,readm[^e]*,readme,readme[^.]*,readme.,readme.[^m]*,readme.m,readme.m[^d]*,readme.md?*}'
irb(main):012:0> File.fnmatch(pattern, './packages/foo/readme.md', File::FNM_PATHNAME | File::FNM_DOTMATCH | File::FNM_EXTGLOB)
=> false
irb(main):013:0> File.fnmatch(pattern, './packages/foo/anything-else', File::FNM_PATHNAME | File::FNM_DOTMATCH | File::FNM_EXTGLOB)
=> true
sytech
  • 29,298
  • 3
  • 45
  • 86
  • Thanks for the reply. Gitlab is saying that they are using Ruby globs on the backend, and in irb this does not appear to work. https://docs.gitlab.com/ee/ci/yaml/#rulesexists e.g. `File.fnmatch('packages/foo/**/!(readme.md)', 'packages/foo/something.kt', File::FNM_PATHNAME | File::FNM_DOTMATCH | File::FNM_EXTGLOB)` returns false – David Aug 04 '22 at 16:26
  • 1
    Oh darn, you're right @David -- It seems that `!` is not a supported metacharacter in ruby's `fnmatch`. In which case, you might be able to use [this workaround](https://stackoverflow.com/a/69906355/5747944) to get the same effect -- (big yikes for readability, but it **should** work) edited my answer. Let me know if that works for you. Technically you could make it less verbose if you don't expect files named `r`,`re`,`rea` ... `readme.m` but this should be complete. – sytech Aug 04 '22 at 16:47
  • Thanks for taking another look at it and thanks for the edit. Yes, very hard to read, but it looks like we are fairly limited without the bang operator. Agreed, that it's probably useful by shortening it and not expecting source files like readm – David Aug 04 '22 at 16:59