Trigger an action upon completion of another action

Is there a way to trigger the Action upon completion of another Action in the same repository?

2 Likes

@cilerler ,

Currently, I do not find any build-in option or event to trigger another workflow when a workflow is completed.

I tried the below event configurations in the second workflow:

on:
  check_suite:
    types: [completed]

on:
  check_run:
    types: [completed]

But if the first workflow has multiple jobs, as long as any one job is completed in the first workflow, the second workflow will be triggered. It will not wait until the completion of the whole first workflow.

As a workaround, you can try to add an additional job at the end of the first workflow, this job is use to trigger the second workflow via the repository_dispatch event. In this job, it will create a repository dispatch event.

A simple demo:

# In the first workflow
jobs:
  # some jobs for the first workflow
  job_01:
  job_02:
  job_03:
  . . .
# the job to create repository dispatch event
  job_create_repository_dispatch:
    needs: [job_01, job_02, job_03, ...]
    runs-on: windows-latest
    env:
      GITHUB_PAT: ${{ secrets.GITHUB_PAT }}
    steps:
      - name: Create a repository dispatch event to trigger workflow
        shell: pwsh
        run: |
          $pat = $env:GITHUB_PAT
          $uri = "https://api.github.com/repos/BrightRan/TestClock/dispatches"
          $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f "", $pat)))

          $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
          $headers.Add("Authorization", ("Basic {0}" -f $base64AuthInfo))
          $headers.Add("Accept", "application/vnd.github.everest-preview+json")
          $headers.Add("Content-Type", "application/json")

          $body = "{
            `"event_type`": `"test-repository-dispatch`",
            `"client_payload`": {
              `"unit`": false,
              `"integration`": true
            }
          }"

          $response = Invoke-RestMethod -Uri $uri -Headers $headers -Body $body -Method POST
          $response | ConvertTo-Json

# In the sexond workflow
on:
  repository_dispatch:

In this situation, on the first workflow, when all the previous jobs ( job_01 , job_02 , job_03 , ) complete successfully, the job  job_create_repository_dispatch  will start and execute the GitHub REST API to create a repository dispatch event, then the second workflow is triggered to run.

Note: About the GITHUB_PAT used in the workflow, you need to create a personal access token and set it as a secret in your repository, instead of directly using the GITHUB_TOKEN automatically created by GitHub.

1 Like

@brightran I appreciate it.  Your answer is very detailed and on the spot.  Thank you!

The only missing part in your example is how to read the payload.

Let’s consider that the payload will be 

$body = "{
            `"event_type`": `"test-repository-dispatch`",
            `"client_payload`": {
              `"completedTask`": "library1"
            }
          }"

Now, how will I be able to read it from the second workflow? e.g.

name: library2

on:
  repository_dispatch:

jobs:
build:
if: github.event_name == 'push' && contains(toJson(github.?.?.?.completedTask), 'library1')
runs-on: windows-latest
...

what it should be should instead of ?.?.?

Thanks in advance.

Yes, in the second workflow which is triggered via the repository_dispatch event you can get the  client_payload from the github context of the second workflow. You can use the property  github.event.client_payload to access the object  client_payload. And you also can use the similar syntaxes to access the children objects of client_payload , such as  github.event.client_payload.integration in my example.

The property  github.event.action is the  event_type you set when creating the repository dispatch event in the first workflow, such as  test-repository-dispatch in my example.

You can reference the example printing context information to the log file to print the github context in the second workflow.

For example:

on:
  repository_dispatch:

jobs:
  workflow_run_info:
    name: Information about the workflow run
    runs-on: ubuntu-latest
    env:
      GITHUB_CONTEXT: ${{ toJson(github) }}
    steps:
      - name: Show Information
        run: |
          echo "This workflow run is triggered by ${{ github.event_name }} ."
          echo " *****************************************************************************"
          echo "$GITHUB_CONTEXT"
          echo " *****************************************************************************"

Logs:

github_context.png

I tried to implement it, but it doesn’t fit what I would like to do.  The way you did it is merely signaling the next action. The issue is, I have no control knowledge of which action should be triggered.

Real-life scenario;

I have an action that creates a based library package that holds all the models.  Upon the package update, I would like to redeploy all the other packages (which are created by other actions) that use the package. 

It could be done in a single action if there was a way to filter the triggering path in the steps as we do on top of the action. However, right now, there is no solution for me to have a robust way of deploying all the packages and not updating the ones that don’t need any update.

@cilerler ,

According to your request, maybe you can try the below steps:

  1. Every time when the based library package updated, in the workflow for the based library package, when configuring the API to create the repository dispatch event, you can custom an object in the client_payload to specify which workflows will be run.
    For example, custom the object run_workflows :

    {
    “event_type”: “test-repository-dispatch”,
    “client_payload”: {
    “unit”: false,
    “integration”: true,
    “run_workflows”: [
    {“name”: “workflow1”},
    {“name”: “workflow2”},
    {“name”: “workflow3”},
    . . .
    {“name”: “workflowN”}
    ]
    }
    }

  2. In the workflows for other packages, set they are triggered on.repository_dispatch event. Add the below if conditional on all the deploy jobs of the workflows.

    if: contains(github.event.client_payload.run_workflows.*.name, github.workflow)

This if conditional will check if the currently running workflow is contained in run_workflows you set.
github.event.client_payload.run_workflows.*.name returns all names of the workflows as an array.
github.workflow returns the name of the currently running workflow.

When the contains() function retuns ’ true’, the deploy job in current workflow will be executed, if ’ false’, the deploy job will be skipped.

I hear you, and I appreciate all the details you provided.  Unfortunately, your logic still relies on me placing consumer workflows into the base workflow. Where it is not a good practice, no base library should know who will consume it.  Even further maintenance of nested triggers would be a nightmare. For that reason, I believe this should be a feature request. 

My 2 cents say this should be a part of the internal dynamics of the GitHub Actions, where workflows should be able to tap into other workflows and should be triggered upon successful completion.  The only major issue that I can see would be a circular-reference, where it could be a deal-breaker. 

e.g.

name: library2

on:
  completion:
    success:
    - library1
  push:
    branches:
    - master
    paths:
    - '.github/workflows/library2.yml'
    - 'src/library2/**'

Unfortunately, I don’t see any clear path to deploy library packages via GitHub Actions the way it lets us at this point. Here is a real-life scenario, each project should generate its own packages, and consider nested triggers can occur.

@cilerler ,

Sorry my suggestions did not help you solve the problem.

You request is quite similar to the trigger option " Build completion" on Azure Pipelines. However, currently GitHub Actions does not have a build-in option like this, and it seems that the only way we can use to trigger a workflow from another workflow is the repository_dispatch event.

azure_pipelines.png

If your projects really need this feature, I recommend you directly report a feature request here. That will allow you to directly interact with the appropriate engineering team, and make it more convenient for the engineering team to collect and categorize your suggestions.

1 Like

Hi @brightran 

Nice catch! That is precisely what I’m looking for to move from Azure Pipelines to GitHub Actions.

As I stated earlier, the repository_dispatch event is a push based concept, where I’m looking for pull.

I appreciate all the help you provided. Thank you!

1 Like

@cilerler, if you don’t want to put consumer workflows into your base workflow, can’t you do the reverse? Send repository_dispatch event when base workflow finishes, run your consumer workflows only on that repository_workflow type.
In your base workflow:

- name: Send repository_dispatch
  env:
    PAT: ${{ secrets.ACTIONS_PAT }}
  run: |
    curl \
      -X POST \
      -H "Authorization: token $PAT" \
      -H "Accept: application/vnd.github.v3+json" \
      https://api.github.com/repos/{owner}/{repo}/dispatches \
      -d '{"event_type":"event_type"}'

and in workflows that should run after base:

on:
  repository_dispatch:
    types: [event_type]

PAT should be with repo scope. event_type can be whatever you decide (docs).

See it working here.

P.S. After finishing my message I realized this is over 1 year old (2020->2021 lol), but it’s actually the first result for me when searching “trigger github action from another action”.

P.P.S. Or you can use new workflow_dispatch event. It will be less flexible in the way that it will run even if your base workflow fails, but you will be able to check if it failed inside consumer workflow.

on:
  workflow_run:
    workflows: ["Build"]
    types: [completed]

jobs:
  on-success:
    runs-on: ubuntu-latest
    if: ${{ github.event.workflow_run.conclusion == 'success' }}
    steps:
      ...
  on-failure:
    runs-on: ubuntu-latest
    if: ${{ github.event.workflow_run.conclusion == 'failure' }}
    steps:
      ...

Docs