I was working on a mono-repo based micro-services project recently and I realised that the CI/CD pipelines were taking too much time. Upon a closer look I noticed that on every run, the pipelines were rebuilding all micro-services!
So here’s a simple pipeline that runs only the changed micro-services,
Project specifics
- My project used Gradle as the over all build tool
- All micro-services are located within the
/servicesdirectory from the root
Bash script to find which service has changed
modified_services=$(
git diff --dirstat=files $1 \
| grep -E 'services/[^/]+/' \
| cut -d'/' -f2 \
| uniq \
| tr '\n' ',' \
| sed 's/,$//'
)
run_jobs=1
if [[ ! $modified_services ]]; then
run_jobs=0
fi
json_string=$(echo $modified_services | sed 's/,/"},{"name":"/g; 1s/^/[{"name":"/; $s/$/"}]/')
echo "$json_string $run_jobs"Workflow that uses the script
jobs:
identify-modified-services:
runs-on: [ 'ubuntu-latest' ]
outputs:
run_jobs: ${{ steps.identify_step.outputs.run_jobs }}
modified_services: ${{ steps.identify_step.outputs.modified_services }}
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
fetch-depth: 2
- name: Identify modified services
id: identify_step
run: |
read previous_sha <<< $(git rev-parse HEAD~1)
read services flag <<< $(.github/workflows/services_diff.sh $previous_sha )
echo "modified services: $services"
echo "run_jobs=$flag" >> $GITHUB_OUTPUT
echo "modified_services=$services" >> $GITHUB_OUTPUT Using the outputs
job2:
needs: [ identify-modified-services ]
if: needs.identify-modified-services.outputs.run_jobs == '1'
steps:
- env:
services: ${{ fromJSON(needs.identify-modified-services.outputs.modified_services) }}
run: echo "$services"%%
Using the outputs in a matrix strategy
pr:
needs: [ identify-modified-services ]
if: needs.identify-modified-services.outputs.run_jobs == '1'
strategy:
fail-fast: false
matrix:
services: ${{ fromJSON(needs.identify-modified-services.outputs.modified_services) }}
uses: ./.github/workflows/_build.yml
with:
service-name: ${{ matrix.services.name }}%%