When multiple WordPress repositories share the same CI pipeline — linting, PHPCS, PHPUnit, deployment — duplicating workflow YAML across repos creates a maintenance burden. GitHub Actions solves this with two complementary features: reusable workflows (call a workflow file from another repo like a function) and composite actions (bundle multiple steps into a reusable action that lives in its own repo).
Problem: A WordPress plugin repository has multiple workflows — lint, test, build, deploy — that share the same setup steps (checkout, PHP install, Composer install) duplicated across every workflow file, making updates to shared steps error-prone.
Solution: Extract shared steps into reusable workflows stored in .github/workflows/ with on: workflow_call, then call them from other workflows with uses: ./.github/workflows/setup.yml. For multi-step shell scripts, use composite actions stored in .github/actions/ — they are version-controlled alongside the code and composable without creating a separate repository.
The files below define a reusable WordPress CI workflow (stored in a central org/.github repo), a composite action that installs PHP + Composer in one step, and a caller workflow in an individual plugin repo.
# org/.github/.github/workflows/wordpress-ci.yml
# Reusable workflow — called by individual plugin repos
on:
workflow_call:
inputs:
php-version:
type: string
default: '8.2'
wordpress-version:
type: string
default: 'latest'
secrets:
DEPLOY_SSH_KEY:
required: false
jobs:
lint-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup PHP + Composer
uses: org/actions/setup-php-composer@v1 # composite action (see below)
with:
php-version: ${{ inputs.php-version }}
- name: PHPCS
run: vendor/bin/phpcs --standard=WordPress .
- name: PHPUnit
run: vendor/bin/phpunit --coverage-text
# org/actions/setup-php-composer/action.yml
# Composite action — bundles PHP setup + Composer install
name: 'Setup PHP and Composer'
description: 'Install PHP, enable extensions, and run composer install'
inputs:
php-version:
description: 'PHP version to install'
required: true
default: '8.2'
runs:
using: 'composite'
steps:
- name: Install PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ inputs.php-version }}
extensions: mbstring, xml, zip, mysqli
coverage: xdebug
env:
COMPOSER_TOKEN: ${{ github.token }}
- name: Cache Composer packages
uses: actions/cache@v4
with:
path: vendor
key: composer-${{ hashFiles('composer.lock') }}
- name: Composer install
shell: bash
run: composer install --no-interaction --prefer-dist --optimize-autoloader
# my-plugin/.github/workflows/ci.yml
# Caller workflow in an individual plugin repo
on: [push, pull_request]
jobs:
ci:
uses: org/.github/.github/workflows/wordpress-ci.yml@main
with:
php-version: '8.3'
wordpress-version: '6.8'
secrets:
DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
NOTE: Reusable workflows must be in a file under .github/workflows/ and triggered with on: workflow_call; composite actions live in their own directory with an action.yml file and are referenced by repo path — the two mechanisms are complementary, not interchangeable.