I have a repository that contains many folders:
/folder_a
/folder_b
...
/folder_z
I want to create a GitHub Action workflow that is triggered whenever a PR is closed in which code within one of those folders has changed, in order to create a release that is tagged with the name of changed folder + a version number.
Example: if contents in folder_a
has been changed in a PR, then when it gets merged it should create a release named folder_a_1.0.0
with the folder name as prefix.
I could create a workflow for each of the folders, but since there are many folders this approach wouldn't be very maintainable (example below with a re-usable release.yml
workflow that contains the logic for creating the release and takes a prefix as input):
name: Release folder_a
on:
pull_request:
branches:
- master
paths:
- "folder_a/**"
permissions:
contents: write
jobs:
release:
uses: ./.github/workflows/release.yml
secrets: inherit
with:
prefix: folder_a
Is there a way to use a single workflow to do the same job?
Your workflow needs an extra job that would feed names of all folders that changed into the release job. Because your reusable workflow ./.github/workflows/release.yml
expects to be invoked with a single folder at a time, you need a matrix that would run your reusable workflow as many times as the number of changed folders:
name: Release folder_a
on:
pull_request:
branches:
- master
paths:
- "folder_*/**"
permissions:
contents: write
jobs:
collect:
runs-on: ubuntu-latest
outputs:
outcomes: ${{ steps.collect.outputs.folders }}
steps:
- uses: actions/checkout@v4
- id: collect
run: |
git diff --name-only ${{ github.base_ref }} ${{ github.head_ref }} | sed 's|/.*||' | uniq | jq -R -s -c 'split("\n")' > folders
echo folders=$(cat folders) >> $GITHUB_OUTPUT
release:
needs: collect
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
folder: ${{ fromJSON(needs.collect.outputs.folders) }}
uses: ./.github/workflows/release.yml
secrets: inherit
with:
prefix: ${{ matrix.folder }}
Here git diff
create a list of all files that changed on the PR relative to the base branch:
git diff --name-only ${{ github.base_ref }} ${{ github.head_ref }}
folder_a/file_1
folder_a/file_2
folder_a/file_3
folder_b/file_1
folder_b/file_2
then sed
removes everything after the "/" leaving just the folder name while uniq
removes duplicates:
sed 's|/.*||' | uniq
folder_a
folder_b
then jq
slurps (-s
) this output as raw text (-R
) into a JSON list and outputs this list as a compact, one line JSON (-c
):
jq -R -s -c 'split("\n")'
["folder_a","folder_b"]
This list is used as a folder matrix in the release job that invokes reusable workflow once per folder. Note, that the prefix
input will not have a trailing "/"