Github Actions is a “BYOBE” (Bring Your Own Bloody Everything) offering that provides basic CI with surprisingly convoluted configuration to Github.

The product as a whole is an exercise in frustration, one of the worst parts is the lack of reusability and the complexity required to achieve it. Github’s concept of reusable workflows on Github Actions is clearly a cobbled together afterthought.

There’s no one way to reuse workflows - you can’t simply include / inherit FROM another workflow and no support for standard YAML anchors/aliases1.

Composite Actions

Composite actions are a way to combine multiple steps into a single step. They are a way to reuse steps, but not workflows.

  • Booleans in composite actions are actually Strings - even if you set their type to Boolean2. 🤦
  • Composite Actions requires setting the shell in each step (shell: bash).
  • Composite Actions requires setting run.using as composite in each workflow (runs.using: "composite").
  • Composite Actions can be called as part of one of the steps in a job.
  • Composite Actions can include multiple files, so it’s possible to use files from the Action or from the user’s repository.
  • Composite Actions can include multiple steps, but not multiple jobs.
  • Composite Actions do not support ${{ secrets.mysecret }}, a workaround is to pass the secrets as input parameters from calling workflow. 🤦


Composite Action

name: Composite Action Example
description: "Composite Action Example"

    description: 'Input for the composite action'
    required: true
    default: 'Hello World'
    description: 'Secret input for the composite action'
    required: true

  using: "composite"
    - name: Hello
      run: echo world
      shell: bash
    - name: Do something secret
      shell: bash
      run: login -p ${{ }}

Calling Workflow

    runs-on: ubuntu-latest
      - name: hello world
        uses: actions/helloworld@v3

      - uses: myorg/my-reusable-repo/.github/actions/my-action@main
          node-auth-token: ${{ secrets.SUPER_SECRET_TOKEN }}

Reusable Workflows

A reusable workflow is similar to normal GitHub Actions workflow, however:

  • Reusable workflows cannot call other reusable workflows.
  • Reusable workflows cannot accept matrix input (strategy.matrix).
  • Reusable workflows are a single YAML file, with no additional files retrieved by default.
  • Reusable workflows are called as a job - but cannot contain steps in the calling workflow.
    • The reusable workflow itself can have multiple steps.
  • Reusable workflows can include multiple jobs, and multiple steps in each job.
  • Reusable workflows are defined through the workflow_call event type.
    • “Regular” (non-reusable) Workflow can be triggered through a workflow_dispatch event.
    • Both event types support input - however with dispatchable Workflows input object is ${{ github.event.inputs }} where as callable workflows receive ${{ inputs }} 🤦.
    • As a result, in order to make a reusable workflow dispatchable, a wrapper workflow may be required.
  • Unlike Composite Actions, Booleans in Reusable Workflows are Booleans.


Reusable Workflow

name: Reusable Workflow Example


    name: "Example job in a reusable workflow"
    runs-on: ubuntu-22.04
      - uses: "actions/checkout@v3"
          repo_token: ${{ secrets.GITHUB_TOKEN }}

Calling Workflow

    uses: myorg/my-reusable-repo/.github/workflows/my-action.yml@main
      secrets: inherit

(I Can’t Get No) YAML Anchors

As a side note - the YAML Spec defined a simple way to reuse values in YAML documents by means of Anchors and Aliases.

If you haven’t used them before (and don’t worry - you can’t on Github) - I have an example here

Unfortunately despite requests1 to3 do4 so5 - Github Actions does not support YAML anchors or aliases.

While obviously not a solution for reusable workflows - these would make large workflows a lot simpler and easier to maintain and as such reduce reliance on the band aid solutions that are Reusable Workflows and Composite Actions.