Detect-Changed-Files in Rust with a proper GitHub Actions

I wrote a small tool in Zig to detect changed files in a GitHub. I wrote about this last August.

It was using Zig 0.14 and I wanted to port it to Zig 0.15. It was not a straightforward as I expected and I was not feeling efficient at this task.

Since I wrote a lot of Rust code recently, I decided to rewrite the tool in Rust.

I changed the configuration file format from YAML to something simpler, similar to INI files. This means that the tool has no dependencies, making it very easy to generate a static binary.

Docker container

To make the tool easy to share, I decided to package the tool as a docker container hosted on GitHub Container Registry.

It is built in 2 stages:

Github Action

My ultimate goal was to create a proper GitHub Action around the tool.

I created a new repository borisfaure/detect-changed-files-action that uses the docker container.

The action take an input parameter config-file that is the path to the configuration file.

How It Works

  1. Determines the base git reference (PR base, push event, manual input, or origin/main fallback)
  2. Fetches the base reference from the remote repository to ensure it’s available locally
  3. Runs git diff --name-only to get the list of changed files
  4. Passes the file list to a Docker container running the detect-changed-files tool
  5. The tool matches files against patterns defined in the configuration file
  6. Returns a JSON object indicating which groups have changes

In term of security, I have added the option --network=none to docker to ensure that the rust binary has no network access.

Example

It is very simple to use.

Given a configuration file (.github/changed-files.conf) like this one:

[linter]
*.py

[tests]
src/**
tests/**

[docs]
docs/**
README.md

Then the action can be used like this in a workflow (.github/workflows/ci.yml):

jobs:
  detect-changes:
    runs-on: ubuntu-latest
    outputs:
      changes: ${{ steps.detect.outputs.changes }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Detect changed files
        id: detect
        uses: borisfaure/detect-changed-files-action@v0.1.3
        with:
          config-file: .github/changed-files.conf

  run-linter:
    needs: detect-changes
    if: fromJson(needs.detect-changes.outputs.changes).linter
    runs-on: ubuntu-latest
    steps:
      - run: echo "Running linter..."

  run-tests:
    needs: detect-changes
    if: fromJson(needs.detect-changes.outputs.changes).tests
    runs-on: ubuntu-latest
    steps:
      - run: echo "Running tests..."

This way, jobs can be conditionally executed based on the changed files.