Ability to Rerun Single Jobs in Github Actions

Written by Hassan Salem on

GitHub’s workflows are a handy tool to automate tasks in the software development lifecycle. This tool allows you to run a specific command after a particular event, like when someone creates a pull request or a new commit.

Note

This article is not about how to use or create workflows, but it is about solving the problem of not retrying the whole deployment when an action fails. So I am assuming that you have this problem, and you have some experience with GitHub actions.

Problem

The limitation that I am addressing here is the ability to re-run single job in GitHub workflows. For example, if you have a workflow with three jobs like build images, test app, and deploy the app. And for some reason, you have a flaky test that you need to retry multiple times to succeed. In GitHub, you only can retry the whole workflow, but not the failed job, which is not time or resources effective. And GitHub still didn’t solve this issue yet!

Proposed solution

I use GitHub actions to automate many tasks in my projects. I have a project with build, test, and deploy jobs. And sometimes, I need to redeploy a specific commit without rebuilding the whole application from scratch because I already have its image in the images repository. I only need to deploy that particular commit. I searched but couldn’t find any reasonable solution or workaround. I found some smart ones, but they were too complicated for me.

So I did the only thing we do when we are stuck, RTFM. I have read the manual and found that there are many ways to invoke workflows in GitHub. So I decided to utilize one of them, which is “workflow_run” this is an event that occurs when a workflow run is requested or completed. It is a convenient tool and has much to offer other than what I am demonstrating here workflow_run

That event gave the ability to separate the jobs into workflows and sequentially run them based on the event payload. So instead of having multiple jobs for the build, test, and deploy. Now I have them as workflows with the same names and steps.

This image shows the job-based separation:

And this image shows the workflow based separation:

Now I can retry tests without having to rebuild the images again. There are many ways to share data between these multiple workflows. Still, the trick I used here was the github.event.workflow_run context, which has the information about the workflow_run that invoked the event, as the run id. for many reasons, I use run id instead of the commit SHA to tag the images. But you can use the GITHUB_SHA environment variable to tag them because the SHA will be shared between all the workflows by default.

The following snippet is a basic example of two workflows configurations. When the first one is complete, it will invoke the second one.

First workflow

name: Workflow1

on:
  push:
    branches:
      - main
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Step one
        run: |
          echo $GITHUB_RUN_ID
          echo $GITHUB_SHA          

Second workflow

name: Workflow2

on:
  workflow_run:
    workflows: ["Workflow1"]
    branches: [main]
    types:
      - completed

jobs:
  build:
    if: ${{ github.event.workflow_run.conclusion == 'success' }}
    runs-on: ubuntu-latest
    steps:
      - name: Step 1
        run: |
          echo $GITHUB_RUN_ID
          echo ${{ github.event.workflow_run.id }}
          echo $GITHUB_SHA          

Final words

There might be better solutions to the problem I introduced, but this solution was so natural and straightforward compared to other solutions that need to maintain a global state of the steps and jobs. If you found this helpful or have a better solution, please let me know in the comments below.

Related articles

If you have any questions, suggestions, or you are seeking help, don't hesitate to get in touch with me on Twitter at @salem_hsn