Mastering GitHub Actions: Building Composite Actions
In the previous lessons of our Mastering GitHub Actions series, we explored how to create workflows and use marketplace actions. However, as your CI/CD pipelines grow, you will often find yourself repeating the same sequences of steps across different repositories or workflows. This violates the DRY (Don't Repeat Yourself) principle. This is where Composite Actions come into play.
What are Composite Actions?
A Composite Action allows you to bundle multiple workflow steps into a single action. Think of it as creating a custom function in programming. Instead of writing ten lines of code to set up a Java environment, configure caching, and run a build in every workflow file, you can wrap those steps into one reusable action.
Composite Actions are defined in a file named action.yml or action.yaml. Unlike standard workflows, they use a specific syntax that tells GitHub to execute multiple shell commands or other actions as a single unit.
Why Use Composite Actions?
- Code Reusability: Write once, use in multiple workflows.
- Maintainability: Update the logic in one place instead of hunting through dozens of YAML files.
- Readability: Keep your main workflow files clean and high-level.
- Abstraction: Hide complex shell scripts or setup logic from the end-user.
The Structure of a Composite Action
To build a composite action, you must create a directory (usually in .github/actions/your-action-name) and add an action.yml file. The core structure looks like this:
name: 'My Composite Action'
description: 'Greets the user and installs dependencies'
inputs:
user-name:
description: 'Who to greet'
required: true
default: 'Developer'
runs:
using: "composite"
steps:
- name: Greet User
run: echo "Hello ${{ inputs.user-name }}"
shell: bash
- name: Check Version
run: java -version
shell: bash
The Flow of a Composite Action
[ Main Workflow ]
|
v
[ Call Composite Action ] ----> [ Input Variables ]
| |
+-------------------------------+
|
v
[ Step 1: Run Shell Script ]
|
v
[ Step 2: Run Another Action ]
|
v
[ Step 3: Set Output ]
|
v
[ Return to Main Workflow ]
Step-by-Step Example: Java Build Composite Action
Let's create a practical example for a Java developer. This action will set up the JDK, cache Maven dependencies, and compile the project.
1. Create the Directory
In your repository, create the folder path: .github/actions/java-setup-build/.
2. Create the action.yml
name: 'Java Setup and Build'
description: 'Sets up JDK 17 and runs Maven compile'
inputs:
java-version:
description: 'Version of Java to use'
default: '17'
runs:
using: "composite"
steps:
- name: Set up JDK
uses: actions/setup-java@v3
with:
java-version: ${{ inputs.java-version }}
distribution: 'temurin'
cache: 'maven'
- name: Build with Maven
run: mvn clean compile
shell: bash
3. Use it in your Workflow
Now, in your main .github/workflows/main.yml, you can call it like this:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Run My Composite Action
uses: ./.github/actions/java-setup-build
with:
java-version: '21'
Real-World Use Cases
- Environment Setup: Bundling the installation of CLI tools (like AWS CLI, Terraform, or Kubernetes tools) and authenticating them.
- Monorepo Management: Creating specific build actions for different microservices within the same repository.
- Security Scanning: Wrapping multiple security tools (SAST, DAST, dependency check) into a single "Security Gate" action.
- Deployment Logic: Standardizing how applications are pushed to staging or production environments.
Common Mistakes to Avoid
- Missing the Shell Attribute: In composite actions, every
runstep must have ashellattribute (e.g.,shell: bash). If omitted, the action will fail. - Hardcoding Values: Avoid hardcoding versions or paths. Use
inputsto make the action flexible. - Relative Path Confusion: Remember that composite actions run relative to the workspace of the calling workflow. Always test your paths.
- Overcomplicating: If an action becomes too large, consider breaking it into smaller composite actions or a reusable workflow.
Interview Notes: Composite Actions vs. Reusable Workflows
This is a common interview question. Here is a quick breakdown:
- Composite Actions: Used for grouping steps. They are faster to call and feel like a single step in the UI. They cannot have their own "secrets" directly passed as environment variables easily without inputs.
- Reusable Workflows: Used for grouping jobs. They allow you to run jobs on different runners and provide better visualization in the GitHub Actions UI for complex pipelines.
- Key Tip: Use Composite Actions for small, repetitive tasks. Use Reusable Workflows for entire deployment or testing pipelines.
Summary
Building Composite Actions is a vital skill for any DevOps engineer or Java developer using GitHub Actions. It allows you to create clean, modular, and maintainable CI/CD pipelines. By defining an action.yml with using: "composite", you can encapsulate complex logic and share it across your organization.
In the next lesson, we will dive deeper into Action Metadata and Versioning to learn how to share your custom actions with the wider world via the GitHub Marketplace.