GitHub Actions
Custom Actions
Using a Composite Action in Workflow
Integrating a composite action into your GitHub Actions workflow helps you DRY up repetitive steps—such as caching dependencies and installing packages—so your CI pipeline stays clean and maintainable.
Original Workflow
The workflow below sets up Node.js, caches dependencies, installs packages, runs tests, and uploads test results:
name: CI
on:
push:
branches: [main]
jobs:
test:
strategy:
matrix:
operating_system: [ubuntu-latest]
nodejs_version: [14, 16, 18]
exclude:
- nodejs_version: 18
operating_system: macos-latest
runs-on: ${{ matrix.operating_system }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Setup Node.js - ${{ matrix.nodejs_version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.nodejs_version }}
- name: Cache NPM dependencies
uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}-node-modules-${{ hashFiles('package-lock.json') }}
- name: Install Dependencies
run: npm install
- name: Unit Testing
id: nodejs-unit-testing
run: npm test
- name: Archive Test Result
if: always()
uses: actions/upload-artifact@v3
with:
name: Mocha-Test-Result
path: test-results.xml
Refactoring with a Composite Action
Instead of repeating cache and install steps, reference a custom composite action stored under .github/custom-actions/npm-action
:
jobs:
test:
runs-on: ${{ matrix.operating_system }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Setup Node.js - ${{ matrix.nodejs_version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.nodejs_version }}
- name: Composite Action – Cache & Install NPM Packages
uses: ./.github/custom-actions/npm-action
with:
path-of-folder: node_modules
- name: Unit Testing
id: nodejs-unit-testing
run: npm test
- name: Archive Test Result
if: always()
uses: actions/upload-artifact@v3
with:
name: Mocha-Test-Result
path: test-results.xml
Note
You can omit the action.yml
filename and simply point to the directory containing your composite action.
Comparison of Workflows
Phase | Original Workflow | Refactored Workflow |
---|---|---|
Checkout | actions/checkout@v4 | actions/checkout@v4 |
Setup Node.js | actions/setup-node@v3 | actions/setup-node@v3 |
Cache & Install | actions/cache@v3 + npm install | Composite Action (npm-action ) |
Test | npm test | npm test |
Archive Results | actions/upload-artifact@v3 | actions/upload-artifact@v3 |
Defining the Composite Action
Below is the action.yml
for our reusable NPM composite action. It accepts a required path-of-folder
input and runs cache & install steps:
name: 'NPM Custom Action'
description: 'Install and cache NPM packages'
inputs:
path-of-folder:
description: 'The path to cache'
required: true
runs:
using: composite
steps:
- name: Cache NPM dependencies
uses: actions/cache@v3
with:
path: ${{ inputs.path-of-folder }}
key: ${{ runner.os }}-node-modules-${{ hashFiles('package-lock.json') }}
- name: Install Dependencies
run: npm install
shell: bash
Warning
Every run
step in a composite action must specify a shell
(e.g., bash
, pwsh
, sh
, cmd
) depending on the runner OS.
Running the Refactored Workflow
After committing your composite action and workflow changes, view the GitHub Actions dashboard. You’ll see the unified “Cache & Install NPM Packages” step replace the individual cache and install tasks across all jobs:
By consolidating common operations into a composite action, you keep workflows consistent and scalable. To share your action with the community, consider publishing it on the GitHub Marketplace.
References
Watch Video
Watch video content