For my personal projects and at work, I use GitHub Actions.
I have been using tj-actions/changed-files It is used to run some jobs only specific files have changed. This is particularly useful in large repositories. For example, one might want to compile the documentation only when the documentation files are changed, or run a linter only when the source code is changed.
tj-actions/changed-files
has some limitations when a large number of files are changed. It also has a lot of dependencies and experienced a major security issue few months ago.
Since my needs were pretty simple, I decided to write my own tool.
To minimize security risks, I want the tool to run simply, without requiring network access.
The list of all changed files from a pull request is fetched directly using git, outside of the tool:
The tool takes a configuration file that defines groups of patterns. Those patterns are similar to ones in .gitignore
files.
Here is a simple example with three groups:
github-workflows
for all the files in .github/workflows/
c
for all the C files in the repositorydoc
for all the documentation files in README.md
and docs/
The configuration, written in YAML, is as follows:
github-workflows:
- .github/workflows/
c:
- '*.c'
- '*.h'
- 'meson.build'
- /meson_options.txt
doc:
- 'docs/**'
- 'README.md'
Given a list of changed files, the tool identifies which groups contain matching files. It then returns a JSON object with the groups as keys and a boolean value indicating if there are matching files or not.
I chose JSON to make the output easy to use in a GitHub Actions workflow.
I decided to write the tool in Zig. I had wanted to try Zig on a real-world use case for a while and this was a good opportunity to do so.
The implementation is very simple and straightforward.
Zig is a very nice language. It is fast to learn as the entire documentation is on a single webpage. With over 20 years of experience in C, I felt right at home with Zig. The compiler is very fast and its error messages are excellent.
I experimented a bit with comptime
. It’s a powerful and unique feature that allows you to execute code at compile time.
The standard library is small but well designed. The ecosystem is still young and reminds me of Rust before the 1.0 release. To write the tool, only one dependency was needed: kubkon/zig-yaml. JSON is supported natively in Zig.
I was pleasantly surprised to to find native io_uring
support in zig.
A few units tests are provided to test the tool. This is an area where I feel Zig’s youth shows. For example, using the default testing framework, I couldn’t run a singe, specific test. I also couldn’t find a way to list all available tests. zig build test --verbose --summary all
gives a summary like:
Build Summary: 8/8 steps succeeded; 28/28 tests passed
test success
├─ run test success 204us MaxRSS:1M
│ └─ zig test Debug native success 2s MaxRSS:235M
│ └─ options cached
├─ run test 15 passed 28ms MaxRSS:1M
│ └─ zig test Debug native cached 36ms MaxRSS:37M
└─ run test 13 passed 18ms MaxRSS:1M
└─ zig test Debug native cached 34ms MaxRSS:37M
Like Go, Zig can generate static binaries, which makes the tool very portable.
My tool is called detect-changed-files
and is released under the MIT license and hosted on GitHub.
Using it is as simple as:
|
Or within a GitHub Actions workflow, it can be used like this: you can use it like this:
jobs:
detect-changes:
outputs:
run: ${{ steps.changed-groups.outputs.run }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: fetch branch
id: changed-groups
run: |
git fetch --depth=1 origin ${{ github.base_ref }}
gh -R borisfaure/detect-changed-files release download -p detect_changed_files-ubuntu-latest -O detect_changed_files
chmod +x detect_changed_files
RUN=$(git diff --name-only ${BASE_SHA} | ./detect_changed_files .groups.yaml)
printf "run=%s" "$RUN" >> $GITHUB_OUTPUT
linter:
needs: detect-changes
if: fromJson(needs.detect-changes.outputs.run).linter
...
I plan on supporting detect-changed-files
and will soon port it to zig-0.15 when it’s released, to take a good look at the new I/O model.