Blogs

Quick Guide to Running Sub-Workflows With GitHub Actions

Quick Guide to Running Sub-Workflows With GitHub Actions

Adrian Ochmann

29 Nov 2023

Automation wizard and Cogworks developer Adrian Ochmann talks about two strategies that can help you run effective sub-workflows using GitHub actions.

Innerworks is coming soon...

This blog was originally published on our previous Cogworks blog page. The Cogworks Blog is in the process of evolving into Innerworks, our new community-driven tech blog. With Innerworks, we aim to provide a space for collaboration, knowledge-sharing, and connection within the wider tech community. Watch this space for Innerworks updates, but don't worry - you'll still be able to access content from the original Cogworks Blog if you want. 

Introducing GitHub workflow.

GitHub describes its workflow as a 'configurable automated process comprising one or more jobs'. With a plan behind configuration, your GitHub Actions workflow can handle some of the most complex build tasks, automating your projects' build, tests and deployment elements.

Sub Workflows with GitHub Actions.

One of the main issues found with GitHub Actions is how to trigger sub-workflows from the main workflow.  In an ideal world, developers should be able to manage their actions within a single workflow and add responsibilities to each job along with the conditions they depend on.

But, as you can imagine, this way of management could grow into a large, unreadable file as GitHub Actions doesn’t currently give the option to split workflow items into template files and include them in the main template file (like you can with Azure DevOps). Although you can find a similar workflow option available in Azure DevOps, maybe one day, teams will transfer this feature to Github, too.

GitHub Workflow tutorials:

1) The Personal Access Token strategy.

This strategy is great if you prefer working with built-in git functionalities, it entails creating workflows that depend on direct repository changes such as given tags or branches.

What are the benefits of a Personal Access Token (PAT) Strategy? 

- It’s simple to use as defining event trigger options, like tag, branch, or push is very easy to handle. 
- Workflows and actions are triggered by the events in the Git repository directly, so you only need to create a tag or branch.

And the cons?

- Misuse can cause looping or recursion and increase costs.
- There isn’t a graph view, which can be used for debugging, browsing, logging and validating the process. It’s not easy to cancel new workflows if there are multiple workflows triggered at the same time (there is no controlled workflow order for the same trigger).
- To achieve a logical and controlled sequence of events, we need to create temporary branches or tags, there is also a need to clean them after.

How to implement the PAT token strategy. 

Create an organisation repository token that allows repo scope permissions and add the token value as your secret repository settings.

 

In the code, all you have to do in the main workflow is create a dedicated trigger event, such as creating a new git tag or branch.

 

 

Assign the PAT token you have created to pass the tag or branch to the repository. To do this, add your PAT token. 

- uses: actions/checkout@v2  with:   token: ${{ secrets.PAT_TOKEN }}

Push your tag using the PAT token.

git push "https://$GITHUB_ACTOR:${{ secrets.PAT_TOKEN }}@github.com/$GITHUB_REPOSITORY.git" tag-pat/work/${{ env.tag }} -f

Examples of the complete set of workflows with the PAT strategy implemented.

Main workflow: tag-pat-main.yml

name: Tag PAT Mainon:  push:    tags:      - tag-pat/main/* jobs:  work:    name: Some Work    runs-on: ubuntu-latest     env:      tag: ${GITHUB_REF#refs/tags/tag-pat/main/}     steps:      - uses: actions/checkout@v2        with:          token: ${{ secrets.PAT_TOKEN }}      - run: echo "Some Main work"      - run: echo "Tag ${{ env.tag }}"      - run: |          git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"          git config --local user.name "github-actions[bot]"           git tag -a tag-pat/work/${{ env.tag }} -m "${{ env.tag }}"           git push "https://$GITHUB_ACTOR:${{ secrets.PAT_TOKEN }}@github.com/$GITHUB_REPOSITORY.git" tag-pat/work/${{ env.tag }} -f

Worker workflow: tag-pat-work.yml

 

name: Tag PAT Work
 
on:
  push:
    tags:
      - tag-pat/work/*
 
jobs:
  work:
    name: Some Work
    runs-on: ubuntu-latest
    env:
      tag: ${GITHUB_REF#refs/tags/tag-pat/work/}
    steps:
      - run: echo "Some PAT work"
      - run: echo "Tag ${{ env.tag }}"


Results after completed actions:

The result of pushing a new tag (for example tag-pat/main/1.0.0) will be that the main workflow (Tag PAT Main / tag-pat-main.yml) is executed first and after successfully running, then consumer/worker (Tag PAT Work/tag-pat-work.yml) was running.

2) Workflow Run Strategy.

This strategy is based on built-in GitHub Actions functionalities. The strategy allows you to create workflows that depend on a given main workflow by its name.

What are the benefits of the workflow-run strategy?


- There’s no need to use a private access token; you can just use the built-in functionality based on the specific workflow names.
- No need to create temporary triggers like tags or branches to execute and trigger another workflow
- It’s easy to define and control the order of the workflows.


Cons of a workflow-run strategy.

- Workflows only have two activity types: completed or requested. You need to create a job condition or expression to check previous workflow conclusions to determine if it was a  “success” or “failure” to allow or skip the specific workflow execution. For example,

github.event.workflow_run.conclusion == 'success'

- By default, the Workflow Run strategy works on the default branch (this is worth customising)
- No graph view - they don’t appear the same way as in a single file.  
- It can be hard to cancel all new workflows if multiple new workflows are triggered.
- A strict naming convention should be followed using this strategy. There’s little freedom to freely name your files. 

on:
  workflow_run:
    workflows: [ "Workflow Main" ]
    types:
      - completed

Example of the complete set of workflows with the workflow run strategy implemented.

Main workflow: workflow-main.yml

name: Workflow Main
 
on:
  push:
    tags:
      - workflow/*
 
jobs:
  work:
    name: Some Workflow Main Work
    runs-on: ubuntu-latest
    steps:
      - run: echo "Some Main work"

Example of the Worker workflow: workflow-work.yml

name: Workflow Work
 
on:
  workflow_run:
    workflows: [ "Workflow Main" ]
    types:
      - completed
  workflow_dispatch:
 
jobs:
  work:
    name: Some Workflow Work
    runs-on: ubuntu-latest
    steps:
      - run: echo "Some work"

Results after completed actions:


 

Take the approach that suits you.

There are different strategies to run sub-workflows, and each of them has pros and cons.

Sometimes, it is good to have a hybrid approach that can be used altogether: in a single file, tags approach, or workflow run.


We’re moving towards a complete token-based approach in Cogworks and divided small, single-responsibility workflows because it allows us to control and maintain the specific workflows separately and apply them accordingly in various projects.

Maybe in the future, we will get similar functionality as in Azure DevOps - templating. For now, it's not built-in.


It's great to hear from our readers. If you have any questions, thoughts, or even requests for more topics like this in the comments box below!

Adrian