Triggering GitHub Actions across different repositories
Introduction
In this post, I’ll show you how one GitHub Actions workflow from a repository can invoke a workflow from a different repository. The first part describes the different events that can trigger a workflow, focusing on the repository_dispatch
event. The second part is a practical example showing how two repositories trigger their workflows mutually.
Events that trigger workflows
As you may already know, there are several events that can trigger a GitHub Actions workflow execution. The most common events that can trigger this execution are push
, pull_request
and schedule
. These events cover most of the use cases, as regularly you want your workflows to trigger when certain events happen on the repository. Sometimes this is not enough, and you require a workflow trigger to respond to an external event (chatbot, external CI, manual deploy to prod after QA, etc.).
This is where the repository_dispatch
event can be used (in this post it will be utilized to trigger workflows across different repositories).
In order to trigger this event, you need to send an HTTP POST request to this endpoint https://api.github.com/repos/:owner/:repo/dispatches
.
This endpoint expects 2 input parameters:
event_type
: This will be received in the workflow event payload as theaction
field.client_payload
: JSON object with any custom information that you want to propagate to the workflow, this will be available in theclient_payload
field of the event payload.
In order to be able to use the endpoint, you will need to be authenticated and authorized to access the repository. For that purpose, you can create a personal access token.
The endpoint can be invoked by using cURL:
1curl -X POST https://api.github.com/repos/:owner/:repo/dispatches \
2-H 'Accept: application/vnd.github.everest-preview+json' \
3-u $your_user:$your_personal_access_token \
4--data '{"event_type": "$your_event", "client_payload": { "customField": "customValue" }}'
Notice the Accept
header has application/vnd.github.everest-preview+json
custom media type value. As of now, this endpoint is still in the preview period and the API can change without previous notice.
Triggering events across repository workflows
In order to show how we can use repository_dispatch
event to trigger workflow executions from one repository to another, I’ve designed two repository pipelines named A and B:
There are two repositories, A and B. Repository A
will trigger its workflow execution when push
or repository_dispatch
event is received. Respository B
will only trigger its workflow execution when a repository_dispatch
event is received.
The sequence starts with a regular push to repository A
(there’s also a schedule so that the sequence gets executed at least once a day). This will trigger an initial workflow execution in repository A
, this workflow will perform a request to the dispatches endpoint in repository B
with a ping
action type and end its execution.
Repository B
will start a new workflow execution. For the received ping
event, the workflow is configured to send a request to repository A
with a pong
action type and end its execution.
Repository A
will start a new workflow execution. For the received pong
event, the workflow will send a request to repository B
with an ack
(acknowledge) action type and end its execution. This will be the last workflow execution in the sequence for this repository.
Finally, repository B
will start a final workflow execution. For the ack
event, the workflow is configured to simply print an informative message with the name of the repository that invoked the action. The workflow execution ends and the sequence concludes.
Access token
One of the main requirements to be able to perform requests to the repository dispatch endpoint is to use authentication.
First, we must create a personal access token following GitHub’s guide.
Next, we need to set these credentials in a secret in each of the repositories (A & B). For each of the repositories, we’ll access the settings->secrets page and add a new secret named ACCESS_TOKEN
. The value for the token will be our username a colon and the personal access token obtained in the previous step (username:token
).
Repository A workflow (ping)
1name: Remote Dispatch Action Initiator
2
3on:
4 push:
5 schedule:
6 - cron: '0 12 * * *'
7 repository_dispatch:
8
9jobs:
10 ping-pong:
11 runs-on: ubuntu-latest
12 steps:
13 - name: PING - Dispatch initiating repository event
14 if: github.event.action != 'pong'
15 run: |
16 curl -X POST https://api.github.com/repos/marcnuri-demo/actions-remote-dispatch-b/dispatches \
17 -H 'Accept: application/vnd.github.everest-preview+json' \
18 -u ${{ secrets.ACCESS_TOKEN }} \
19 --data '{"event_type": "ping", "client_payload": { "repository": "'"$GITHUB_REPOSITORY"'" }}'
20 - name: ACK - Acknowledge pong from remote repository
21 if: github.event.action == 'pong'
22 run: |
23 echo "PONG received from '${{ github.event.client_payload.repository }}'" && \
24 curl -X POST https://api.github.com/repos/marcnuri-demo/actions-remote-dispatch-b/dispatches \
25 -H 'Accept: application/vnd.github.everest-preview+json' \
26 -u ${{ secrets.ACCESS_TOKEN }} \
27 --data '{"event_type": "ack", "client_payload": { "repository": "'"$GITHUB_REPOSITORY"'" }}'
This repository contains the “initiating” workflow. It will be triggered on repository_dispatch
but also on push
and schedule
. Push and schedule events will be used to trigger the initiating ping action. On the other hand, dispatch events will be used to trigger the acknowledge action. Both these executions can be seen in the jobs
section of the workflow.
There is only a single job for this workflow with two steps. The first step will trigger only when github.event.action != 'pong'
. For our current example, this means that it will trigger for any push
or schedule
event or any repository_dispatch
event which is not of pong
type. So every time a user pushes a commit to the repo or every day at 12:00, this step will be executed. The run action is executing the curl
command described previously. In this case, the event_type
is ping
and is targeting repository B
, in the custom payload we’re including the name of the current repo available in $GITHUB_REPOSITORY
environment variable. Notice also how we set our credentials with the secret defined in the previous step using the expression ${{secrets.ACCESS_TOKEN}}
.
The second step in the job will trigger whenever github.event.action == 'pong'
, which will only happen whenever a respository_dispatch
event of type pong
is received. This step will (again using curl
) send an acknowledgment request to repository B
.
Repository B workflow (pong)
1name: Remote Dispatch Action Responder
2
3on: [repository_dispatch]
4
5jobs:
6 ping-pong:
7 runs-on: ubuntu-latest
8 steps:
9 - name: Event Information
10 run: |
11 echo "Event '${{ github.event.action }}' received from '${{ github.event.client_payload.repository }}'"
12 - name: PONG - Dispatch response to received PING
13 if: github.event.action == 'ping'
14 run: |
15 curl -X POST https://api.github.com/repos/marcnuri-demo/actions-remote-dispatch-a/dispatches \
16 -H 'Accept: application/vnd.github.everest-preview+json' \
17 -u ${{ secrets.ACCESS_TOKEN }} \
18 --data '{"event_type": "pong", "client_payload": { "repository": "'"$GITHUB_REPOSITORY"'" }}'
This repository contains the workflow that will be remotely invoked, i.e. it will only be triggered for repository_dispatch
events.
The workflow contains a single job with 2 steps. The first step (Event Information) will run always, it basically prints information about the event that triggered the invocation. The second step (PONG) will only run if a ping
event action is received, it will send a request (using the described curl
command) to repository A
with a pong
event_type
.
Conclusion
In this post we’ve seen how we can use GitHub Actions repository_dispatch
event to trigger workflow executions across different GitHub repositories. The same approach could be used to trigger manual action executions from a remote environment, a different CI tool, a chatbot, etc.
The full source code for this post can be found at Github (A & B).
Comments in "Triggering GitHub Actions across different repositories"