Mastering GitHub Actions with Reusable Workflows: Simplifying Your YAML
How to DRYup your GA with Workflows!
While GitHub Actions (GA) provides powerful automation using YAML, there’s always room for improvement.
Many hoped for the YAML Anchor feature to keep our files DRY (Don’t Repeat Yourself). But two years on, GitHub Actions still doesn’t support this.
Instead, GitHub introduces Reusable Workflows and Composite Actions to ensure a neat workspace.
What Are YAML Anchors?
For the uninitiated, YAML Anchors allow for the definition of common sections, which can then be reused elsewhere within the same YAML file. For instance:
default: &default
adapter: mysql2
encoding: utf8mb4
development:
<<: *default
database: dummy_project_development
Such a feature aids in eliminating redundancy. In my prior article, [Speed Up Your CI With Docker (Github Actions)], the lack of Anchors led to repetitions that reduced readability and maintainability.
To demonstrate, consider this basic example:
name: Use Reusable Workflow
on: [push]
jobs:
prepare:
runs-on: ubuntu-latest
steps:
-
run: echo Prepare!
shell: bash
test-alpha:
needs: prepare
runs-on: ubuntu-latest
steps:
-
run: echo Test A!
shell: bash
test-beta:
needs: prepare
runs-on: ubuntu-latest
steps:
-
run: echo Test B!
shell: bash
Here, the ‘prepare’ section needs execution every time. The ‘test-*’ sections contain the repetitions we aim to DRY up.
The Power of Reusable Workflows
By harnessing Reusable Workflows, the repetitive ‘test-*’ sections can be refactored into another file:
name: The Reusable Workflow
on:
workflow_call:
inputs:
test-name:
required: true
type: string
jobs:
test:
name: "Test ${{ inputs.test-name }}"
runs-on: ubuntu-latest
steps:
-
run: echo Test ${{ inputs.test-name }}.
shell: bash
Even if there aren’t any inputs/outputs, it’s essential to declare on: [workflow_call]
.
Then, the primary section gets streamlined as:
name: Use Reusable Workflow
on: [push]
jobs:
prepare:
runs-on: ubuntu-latest
steps:
-
run: echo Prepare!
shell: bash
test-alpha:
needs: prepare
uses: <github-name>/<repositoy-name>/.github/workflows/reusable-workflow.yml@main
with:
test-name: "Alpha"
test-beta:
needs: prepare
uses: <github-name>/<repositoy-name>/.github/workflows/reusable-workflow.yml@main
with:
test-name: "Beta"
Just ensure that the ‘prepare’ job runs initially and passes the necessary inputs to the reusable job. Voila! Your workflow is now cleaner and more efficient.