Github Actions Repository Dispatch

Janne Kemppainen |

There are lots and lots of events that can be used to trigger GitHub Actions. But if you want to control your actions programmatically from the outside you will need to use repository dispatch. In this post I will go through the essential things you need to know to trigger actions programmatically.

I assume that you have a basic understanding of GitHub Actions. If you need a refresher or are new to the subject I recommend that you check out my Gentle Introduction to GitHub Actions first.

Repository dispatch is a way to send named events to your GitHub repository with an optional data payload that can be accessed in the Actions workflows. The dispatch event must contain an event type argument which determines if the action should run or not. We can configure to workflow to run on multiple events, and they can have custom names.

Configuration

To use repository dispatch in your workflow you need to configure the trigger in the workflow YAML file. You need to define the repository_dispatch event and set the types that you want it to react to.

on:
  repository_dispatch:
    types: [build, release]

The types are custom names for the webhook event and you can define them freely. If you set multiple types the action is triggered when any one of them is received.

Note that you can still use your existing trigger configurations in the workflow and add the repository_dispatch event as an extra. If you want to trigger the action manually from the GitHub UI you can use the workflow dispatch event.

The configuration needs to be in the default branch of your repository, so you cannot trigger the workflow from other branches. You are still free to check out any branch you wish within the workflow. So you could specify a non-default branch in the event payload and provide that to the actions/checkout step.

Triggering

The repository dispatch events are triggered by sending a POST request to the following address:

https://api.github.com/repos/{owner}/{repo}/dispatches

Replace {owner} and {repo} with the correct values for your repository. Here is an example on how you can trigger the event from the command line with curl:

curl -X POST \
    -H "Accept: application/vnd.github.v3+json" \
    -H "Authorization: token <token>" \
    --data '{"event_type": "build", "client_payload": { "version": "1.2.3"}}' \
    https://api.github.com/repos/jannekem/actions-playground/dispatches

You need to provide a personal access token in the authorization header to authenticate the request. Follow these instructions in the GitHub docs to create your token and replace <token> in the command with your actual token. You also need to set the Accept header to the value you see in the example.

Note: The authorization header must be in the Authorization: token abc123... format, not the bearer token format Authorization: Bearer abc123!

The request body must contain the event_type entry which must match with one of the event types you defined in the workflow file. If the event type doesn't match any configuration then nothing happens and your request passes silently. The API does not tell if a workflow was triggered.

If you need to do it from a Python script you could use the requests library:

import os
import requests

TOKEN = os.environ.get("GITHUB_TOKEN")
OWNER = os.environ.get("GITHUB_OWNER")
REPO = os.environ.get("GITHUB_REPO")
VERSION = os.environ.get("VERSION")

headers = {
  "Accept": "application/vnd.github.v3+json",
  "Authorization": f"token {TOKEN}",
}

data = {
  "event_type": "build",
  "client_payload": {
    "version": VERSION
  }
}

requests.post(
  f"https://api.github.com/repos/{OWNER}/{REPO}/dispatches",
  data=data,
  headers=headers
)

The above script would get argument values from environment variables and then create a POST request to the GitHub API endpoint.

Access data in a workflow

You can easily access the data that is sent as the client payload. All received values are available through the github.event.client_payload object. In the previous example we sent a version string as the payload. It can be forwarded to an action step in the same way you would access other variables.

with:
  version: ${{ github.event.client_payload.version }}

I already mentioned that there is a limitation where the workflow can only be triggered from the default branch. You can overcome this by defining the branch or commit manually.

- name: Checkout
  uses: actions/checkout@v2
  with:
    ref: ${{ github.event.client_payload.ref }}

Then define the branch, tag, or SHA in the request payload:

{
  "event_type": "my-event",
  "client_payload": {
    "ref": "my_branch"
  }
}

Access data from JavaScript

If you are building your own action with JavaScript, it is possible to access the payload data directly. Use the @actions/github package to access the context of the running action:

const { context } = require("@actions/github");
const action = context.payload.action;
const version = context.payload.client_payload.version;

The context payload contains the action name that was used to trigger the workflow. The value is equal to the event_type field that you send in the repository dispatch event.

The client payload is available via context.payload.client_payload and it contains the payload object.

One possible use case is to accept input parameters from both the workflow arguments and the repository dispatch event. For example this code would default to the workflow input and fall back to the dispatch payload :

const core = require("@actions/core");
const { context } = require("@actions/github");

const version = core.getInput("version") || context.payload.client_payload.version

Now you're free to do whatever you want with the value!

Conclusion

If you try to go through the official documentation it can be a little difficult to understand how the repository dispatch event works. Hopefully this post has made things clearer to you!

Discuss on Twitter

Subscribe to my newsletter

What's new with PäksTech? Subscribe to receive occasional emails where I will sum up stuff that has happened at the blog and what may be coming next.

powered by TinyLetter | Privacy Policy