Github Actions are the go-to continuous integration tool for plenty of reasons - chief among them: first-party support with Github, pretty decent performance, and relatively cheap runner instances.

However, one place Github Actions has fallen short relative to other YAML-based players in the space (namely CircleCI) has been the inability to DRY up the YAML config files.

However, with a cool new 1 feature called Composite Run Steps, your repeated shell script invocations can now happily live in a separate YAML file!

Don’t read the docs

The documentation for Composite Run Steps (linked at the bottom) are great, but they make the terrible assumption that you want to create a whole different repository for your composite action.

If that’s your use-case, great! Otherwise, I’m guessing the far more common use-case is to warehouse code, workflow files, and composite action files in the same repository!

Fortunately, this is possible, the solution just is buried in a Github Community link instead of being plainly available in big bold letters at the top of the Github’s documentation.

3 Steps to DRYer YAML

  1. Go through the official docs for creating a Composite Run Step Action, except ignore everything about creating a new repository β€” just name your action metadata file as /.github/actions/my-action-name/action.yml

    As you’ll see in a moment, the my-action-name is important, since that’s how you’ll reference your action. It can be anything that’s legal as a file-path.

  2. In your actual Workflow yaml file, replace your repeated shell scripts with the following line; for example:

    - - run: sudo apt-get install androidsdk
    - - run: alias sdkmanager="androidsdk"  
    - - name: Update Android Deps
    -   run: sdkmanager "build-tools;31.0.0-rc5" "platform-tools" "platforms;android-30" "cmdline-tools;latest" "extras;android;m2repository" "extras;google;m2repository" --verbose   
    -   env:  
    -     ANDROID_SDK_ROOT: /opt/android/sdk
    + - uses: ./.github/actions/my-action-name
  3. Revel in your victory; your workflow files are just a wee bit smaller! πŸŽ‰

If you’re a visual person, your file heirarchy should look like this:

β”‚   β”œβ”€β”€actions/
β”‚   β”‚   └──my-android-action/
β”‚   β”‚       └──action.yml
β”‚   └──workflows/
β”‚       └──workflow.yml

In my case, across 2 workflow files, I converted 40 lines of YAML to 14 lines, with the benefit of better maintainability moving forward.


As of this writing (06/2021) Composite Run Step Actions don’t support importing other actions – meaning you’ll have to do anything involving a uses statement the old-fashioned way, for now2:

- name: Build App
  uses: eskatos/gradle-command-action@v1
  id: gradle
    wrapper-cache-enabled: true
    dependencies-cache-enabled: true
    configuration-cache-enabled: true
    arguments: :app:assembleDebug --scan

Composite Run Actions also don’t support conditionals, timeouts, referencing secrets, and a few other things. The Metadata Syntax doc has the full enumeration of allowable items; everything else is unsupported.


What did I miss? Is there some super-secret way to reuse YAML I don’t know about?

Send me a note at @JvmName!


  1. Github’s Composite Run Steps docs

  2. Metadata Syntax Doc:

  3. GitHub Composite Actions - STOP wasting your time and create reusable actions

  4. Github Community

  1. “New” meaning “I just discovered it yesterday”; I have no idea how long it’s been out. ↩︎

  2. It looks like Github has an Architecture Decision Record that’s been accepted…so now it’s just a matter of time? ↩︎