Run And Trigger GitHub Workflow

Execute GitHub Actions in different ways and merge input parameters with defaults.

If you finished reading my Trying GitHub Actions blog post, you might be thinking: what are all the ways I can run the same workflow? This blog post is the answer.

Let's say you can to run a GitHub Actions workflow:

  • on each pushed commit(s)
  • periodically (every day for example)
  • by manually calling it from the GitHub Actions page
  • by making an API call to GitHub API

My example is very simple. I just want to greet the user.

.github/workflows/ci.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
name: ci

# run the workflow for different events
# on every commit
# and every morning
# and when we trigger the workflow manually
# and when we dispatch an event
on:
push:
workflow_dispatch:
schedule:
# UTC time
- cron: '0 7 * * *'
repository_dispatch:
types: [on-greeting]
jobs:
greet:
runs-on: ubuntu-20.04
steps:
- name: Greeting
run: echo "Hello to you 1 times"

🎁 You can find the source code in the repo bahmutov/workflow-examples

Add workflow dispatch defaults

Let's add input parameters to the manual dispatch event "workflow_dispatch". We can enter the custom greeting message and the number of times.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
name: ci

# run the workflow for different events
# on every commit
# and every morning
# and when we trigger the workflow manually
# and when we dispatch an event
on:
push:
workflow_dispatch:
inputs:
n:
description: Magic number
type: number
default: 0
required: false
greeting:
description: A greeting message
type: string
default: ''
required: false
schedule:
# UTC time
- cron: '0 7 * * *'
repository_dispatch:
types: [on-greeting]
jobs:
greet:
runs-on: ubuntu-20.04
steps:
- name: Greeting
run: echo "${{ github.event.inputs.greeting }} to you ${{ github.event.inputs.n }} times"

When the user triggers the flow, they can enter the input parameters

Manual dispatch inputs

Hmm, what about other events? How can we provide the default greeting and number of times for the push event, for example?

Default values

Let's put the default values into the workflow's env object

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
name: ci

# default values to use
env:
greeting: 'Hello'
n: 1

# run the workflow for different events
# on every commit
# and every morning
# and when we trigger the workflow manually
# and when we dispatch an event
on:
push:
workflow_dispatch:
inputs:
n:
description: Magic number
type: number
default: 0
required: false
greeting:
description: A greeting message
type: string
default: ''
required: false
schedule:
# UTC time
- cron: '0 7 * * *'
repository_dispatch:
types: [on-greeting]
jobs:
greet:
runs-on: ubuntu-20.04
steps:
- name: Greeting
run: echo "${{ github.event.inputs.greeting || env.greeting }} to you ${{ github.event.inputs.n | env.n }} times"

This is better. Now the push and schedule workflows will use the default parameters.

The dispatch event API call

But what about repository_dispatch? We can trigger this workflow via an API call and pass parameters like this:

1
2
3
4
5
6
7
curl -L \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $MY_GITHUB_TOKEN"\
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/bahmutov/workflow-examples/dispatches \
-d '{"event_type":"on-greeting","client_payload":{"greeting":"what is up","n":10}}'

Tip: I used to wrap a similar command to trigger CircleCI workflows in my CLI tool trigger-circleci-pipeline. For GitHub Actions the API calls seems to be simple enough to use CURL and any request library to not require a separate plugin.

To use the client_payload we need to include it in the echo command too

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
name: ci

# default values to use
env:
greeting: 'Hello'
n: 1

# run the workflow for different events
# on every commit
# and every morning
# and when we trigger the workflow manually
# and when we dispatch an event
on:
push:
workflow_dispatch:
inputs:
n:
description: Magic number
type: number
default: 0
required: false
greeting:
description: A greeting message
type: string
default: ''
required: false
schedule:
# UTC time
- cron: '0 7 * * *'
repository_dispatch:
types: [on-greeting]
jobs:
greet:
runs-on: ubuntu-20.04
steps:
- name: Greeting
run: |
echo "${{ github.event.inputs.greeting || github.event.client_payload.greeting || env.n }} \
to you ${{ github.event.inputs.n || github.event.client_payload.n || env.n }} times"

Ughh it is getting ugly.

Merge inputs with defaults

Let's have a separate workflow step to merge all inputs with defaults. I will pass the merged values through the environment variables. We can output the variables into $GITHUB_ENV file to pass them to the next steps.

1
2
3
4
5
steps:
- name: Set merged variables
run: |
echo "GREETING=${{ github.event.inputs.greeting || github.event.client_payload.greeting || env.greeting }}" >> $GITHUB_ENV
echo "N=${{ github.event.inputs.n || github.event.client_payload.n || env.n }}" >> $GITHUB_ENV

The full workflow:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
name: ci

# default values to use
env:
greeting: 'Hello'
n: 1

# run the workflow for different events
# on every commit
# and every morning
# and when we trigger the workflow manually
# and when we dispatch an event
on:
push:
workflow_dispatch:
inputs:
n:
description: Magic number
type: number
default: 0
required: false
greeting:
description: A greeting message
type: string
default: ''
required: false
schedule:
# UTC time
- cron: '0 7 * * *'
repository_dispatch:
types: [on-greeting]
jobs:
greet:
runs-on: ubuntu-20.04
steps:
- name: Set merged variables
run: |
echo "GREETING=${{ github.event.inputs.greeting || github.event.client_payload.greeting || env.greeting }}" >> $GITHUB_ENV
echo "N=${{ github.event.inputs.n || github.event.client_payload.n || env.n }}" >> $GITHUB_ENV

- name: Print workflow_dispatch inputs
run: echo "${{ toJson(github.event.inputs) }}"

- name: Print repository_dispatch inputs
run: echo "${{ toJson(github.event.client_payload) }}"

- name: Greeting
run: echo "$GREETING to you $N times"

Now we are cooking! If we trigger the workflow manually, we get the expected results

Manual workflow with inputs Aloha and 3

If we simply push the code to the remote origin, we get the default parameters

The pushed code runs the workflow with default parameters

Dispatching an API call

Workflow triggered using Curl API call

Finally, every morning UTC time shows the following defaults

Workflow ran on cron schedule

Tip: you can get the event's name and type to distinguish between the 4 workflow triggers

1
2
3
steps:
- run: echo "event name is:" ${{ github.event_name }}
- run: echo "event type is:" ${{ github.event.action }}

Update 1: pass an environment variable

When making a dispatch call, if you want to pass an environment variable, use an expression syntax inside the single quotes

1
2
3
4
# 🚨 does NOT work
-d '{"event_type":"on-greeting","client_payload":{"branch":"$GITHUB_HEAD_REF"}}'
# ✅ does work
-d '{"event_type":"on-greeting","client_payload":{"branch":"${{ github.head_ref }}"}}'