This repository is an attempt to better understand the details of triggering GitHub Actions events.
It depends on how many builds you want to run. Do you have a limited GHA budget for the repo in question?
WARNING: Merge queues throw a wrench into the previous advice. These recommendations have been updated accordingly. See further down for more detail.
This is nice if your builds aren't limited: it'll check every pushed commit, as well as the proposed merge for each pull request. If you push a commit before the workflows finish, it'll cancel the one checking the proposed merge (since that's no longer the active proposal) but it'll continue the check on the previously-pushed commit. Merge queue checks are treated separately, and won't be canceled by other activity.
on:
merge_group:
pull_request:
push:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }} @ ${{ github.event.compare || github.head_ref || github.ref }}
You might want this if you'd like to limit builds as much as possible but maintain safety around your main branch(es). This will only check pushes and pull requests that target your main branch. New workflows will cancel old ones for the same branch or PR. Merge queue checks are treated separately, and won't be canceled by other activity. (It would be nice to cancel PR merge checks in favor of merge queue checks, but that doesn't appear to be possible.)
on:
merge_group:
pull_request:
branches:
- main
push:
branches:
- main
workflow_dispatch:
concurrency:
group: ${{ github.workflow }} @ ${{ github.head_ref || github.ref }}
I want to run a build-and-test workflow in what seems like two categories of events:
- A commit is pushed to any branch
- Something changes affecting the merge result of a pull request:
- A pull request is opened
- A commit is pushed to the PR branch
- The PR target branch is updated
Early on, I tried something like this:
on:
push:
pull_request:
Note that pull_request:
without any filters is equivalent to pull_request: [opened, reopened, synchronize]
.
This achieves the goal as stated! However, if you push a commit to a branch corresponding to an open PR, the workflow runs twice: once for the push event and once for the pull request event. Depending on the situation, this could be fine, but it might be expensive or otherwise undesirable.
The most common advice I've seen is to put a branch filter on the push
event, like this:
on:
push:
branches:
- main
pull_request:
That may be fine or even preferable in many cases, but I want build-and-test feedback on all branches, not just
main
.
Consulting the information recorded below, here are the events that should trigger the workflow:
User action | Event(s) |
---|---|
Push to a branch | push |
Open a pull request | pull_request_target/opened , pull_request/opened |
Push to a pull request branch | push , pull_request_target/synchronize , pull_request/synchronize |
One interesting subtlety is that the proposed merge commit -- the commit most interesting to build and test -- is only
available in the github.sha
field of pull_request
events.
The github.sha
in the push
event is the commit that was
pushed, and the github.sha
in the pull_request_target
event is the base branch commit.
It could also be interesting to build and test the branch by itself, without merging. That commit is available in the
github.sha
field of the push
event, and in the github.event.after
field of the pull_request
and
pull_request_target
events.
If we want to test both, it's easy: we should run the workflow on both push
events to test the branch as-is, and on
pull_request
events to test the merge result. In both cases, we want to build and test the commit from the
github.sha
field, which is normal GHA behavior.
If we only want to test the merge, that's more difficult but still possible. We already established that we need to
run the workflow on pull_request
events, since that's the only way to get the merge commit. But what do we do about
push
events? How do we run on push
to a branch unless that branch is the head of an open PR? The push
event
doesn't include any information indicating that the commit or branch is the head of a PR.
Concurrency groups to the rescue! A later workflow run can cancel an earlier one if their concurrency groups match.
This only happens with queued workflows by default, but if cancel-in-progress
is set to true
, it will also cancel
workflows that are already running.
There are two convenient ways to do that. One way is to use github.sha
from the push
event and
github.event.after
from the pull_request
event, but that feels a bit like lying: the pull_request
event will
"win" but isn't actually running on the commit named in the concurrency group. The other way is to use the branch
name: that's github.ref_name
(not github.ref
!) in the push
event and github.head_ref
in the pull_request
event.
All things considered, I have two recommendations for this scenario:
- You can run the workflow on "either"
push
orpull_request
events by running on both and using something like${{ github.workflow }} @ ${{ github.head_ref || github.ref_name }}
for your concurrency group. That'll force a PR's proposed merge commit to take precedence. Optionally, filterpush
,pull_request
, or both events to specific branches. - Consider actually running on both events, since they're not actually testing the same thing: the
push
event tests the branch by itself, and thepull_request
event tests the result of the proposed merge. In that case, you could use something like${{ github.workflow }} @ ${{ github.ref }}
or${{ github.workflow }} @ ${{ github.head_ref || github.sha }}
as your concurrency group.
The recommendation above, configuring the workflow to run on both push
and pull_request
events with a concurrency
group like ${{ github.workflow }} @ ${{ github.head_ref || github.sha }}
, does not work as expected if the same
workflow also handles merge_group
events. The push
event will cancel the merge_group
event, which sets up a race
condition: if GitHub checks the status after that cancellation but before the merge_group
workflow registers itself,
then the cancellation will be considered a failure and the merge will be evicted from the queue. Oops!
The best solution is to use a different concurrency group for the merge_group
event. Here's the ideal goal:
- For
push
events, use a unique concurrency group for each commit. - For
pull_request
events, use a unique concurrency group for each PR. Newerpull_request
events for a PR should cancel older ones for the same PR. - For
merge_group
events, use a unique concurrency group for each PR (but different from thepull_request
group). Newermerge_group
events for a PR should cancel older ones for the same PR. - It's NOT acceptable for
push
events to cancelmerge_group
events. (It might be acceptable formerge_group
events to cancelpush
events, but I'm not sure. I haven't yet set up a test for this.)
Good candidates for each:
push
events:github.sha
,github.event.compare
pull_request
events:github.ref
,github.ref_name
,github.event.pull_request.head.ref
,github.event.pull_request.html_url
merge_group
events: Hmmmmm...
Unfortunately, the merge_group
event doesn't include anything quite like what we need. The closest options are
github.ref
, which is the same as github.event.merge_group.head_ref
, and github.ref_name
, which is similar. These
vary with both the PR and target branch head commit SHA, since the target head SHA is part of the temporary branch
name. At least it's not quite as unique as github.sha
.
By the way, this also means that if, for example, PR1 is in the queue before PR2, and PR1 fails so PR2 gets a new merge commit, the old PR2 workflow is difficult to cancel. See here for a somewhat heavy solution: https://github.com/marketplace/actions/cancel-previous-runs-action
Anyway, given the candidates above, I recommend flipping it all around. Treat push
as the unique situation, and use
head_ref
for "all the other" events. Unfortunately, pull_request
and merge_group
put their head_ref
values in
different places... fortunately(?) that place is ref
for merge_group
, which is a reasonable fallback for other
events. All together, that leads to something like this:
on:
merge_group:
pull_request:
push:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }} @ ${{ github.event.compare || github.head_ref || github.ref }}
Most of the GHA workflows in this repository are generated using the generate-workflows.sh
script. See the script
for more details.
github property |
Type | Value |
---|---|---|
repository |
string | "<owner>/<repo>" |
repository_owner |
string | "<owner>" |
repository_visibility |
string | "public" or "private" |
event_path |
string | "/home/runner/work/_temp/_github_workflow/event.json" |
runner.os |
string | Example: "Linux" |
runner.arch |
string | Example: "X64" (note the capital X ) |
runner.name |
string | Example: "GitHub Actions 5" |
runner.environment |
string | Example: "github-hosted" |
runner.tool_cache |
string | Example: "/opt/hostedtoolcache" |
runner.temp |
string | Example: "/home/runner/work/_temp" |
runner.workspace |
string | Example: "/home/runner/work/<repo>" |
github.event property |
Type | Value |
---|---|---|
repository |
object | Repository info, including info to construct various URLs |
sender |
object | Info about the GitHub user (or app?) who triggered the event |
github property |
Type | Value |
---|---|---|
ref |
string | "refs/heads/main" |
sha |
string | Git SHA of the commit that was pushed |
head_ref |
string | "" (empty string) |
base_ref |
string | "" (empty string) |
event_name |
string | "push" |
ref_name |
string | "main" |
ref_type |
string | "branch" |
github.event property |
Type | Value |
---|---|---|
after |
string | Git SHA of the repo after the push = Git SHA of the commit that was pushed |
before |
string | Git SHA of the repo before the push |
commits |
array | Array of commit objects that were pushed |
compare |
string | URL to compare before and after the push |
forced |
boolean | Whether the push was forced |
head_commit |
object | Commit object of the head commit |
pusher |
object | Pusher object (name, email) |
ref |
string | "refs/heads/main" |
github property |
Type | Value |
---|---|---|
ref |
string | "refs/heads/main" |
sha |
string | Git SHA of the default branch |
head_ref |
string | "" (empty string) |
base_ref |
string | "" (empty string) |
event_name |
string | "issues" |
ref_name |
string | "main" |
ref_type |
string | "branch" |
github.event property |
Type | Value |
---|---|---|
action |
string | "opened" |
issue |
object | Issue object |
github.event.issue property |
Type | Value |
---|---|---|
body |
string | Body text for the issue |
created_at |
string | Timestamp of issue creation |
number |
number | Issue number |
title |
string | Title of the issue |
Caused by something like: git push --set-upstream origin example-branch
This triggers two events: first a create
, then a push
github property |
Type | Value |
---|---|---|
ref |
string | "refs/heads/example-branch" |
sha |
string | Git SHA of the new branch |
head_ref |
string | "" (empty string) |
base_ref |
string | "" (empty string) |
event_name |
string | "create" |
ref_name |
string | "example-branch" |
ref_type |
string | "branch" |
github.event property |
Type | Value |
---|---|---|
description |
null | ? |
master_branch |
string | Example: "main" |
pusher_type |
string | "user" |
ref |
string | "example-branch" |
ref_type |
string | "branch" |
github property |
Type | Value |
---|---|---|
ref |
string | "refs/heads/example-branch" |
sha |
string | Git SHA of the commit that was pushed |
head_ref |
string | "" (empty string) |
base_ref |
string | "" (empty string) |
event_name |
string | "push" |
ref_name |
string | "example-branch" |
ref_type |
string | "branch" |
github.event property |
Type | Value |
---|---|---|
after |
string | Git SHA of the branch after the push = Git SHA of the commit that was pushed |
base_ref |
string | "refs/heads/main" |
before |
string | "0000000000000000000000000000000000000000" |
commits |
array | [] |
created |
boolean | true |
deleted |
boolean | false |
forced |
boolean | false |
head_commit |
object | Commit object of the branch head commit |
pusher |
object | Pusher object (name, email) |
ref |
string | "refs/heads/example-branch" |
In this case, the pull request from example-branch
to main
was opened with an assignee and a label, but no
reviewers.
This triggered several workflows, in this order:
pull_request_target
with actionassigned
pull_request_target
with actionlabeled
pull_request_target
with actionopened
pull_request_target
(without an action filter)pull_request
with actionopened
pull_request
(without an action filter)pull_request
with actionassigned
pull_request
with actionlabeled
All of these receive a github.event
containing the same pull_request
object:
github.event.pull_request property |
Type | Value |
---|---|---|
_links |
object | Links to various PR-related resources |
additions |
number | Number of additions in the PR |
assignee |
object | User object of the primary assignee |
assignees |
array | Array of user objects of all assignees |
base |
object | Base branch object |
body |
string | Body text of the PR |
changed_files |
number | Number of files changed in the PR |
commits |
number | Number of commits in the PR |
created_at |
string | Timestamp of PR creation |
deletions |
number | Number of deletions in the PR |
draft |
boolean | Whether the PR is a draft |
head |
object | Head branch object |
labels |
array | Array of label objects. Each object includes a name, color, etc. |
number |
number | PR number |
requested_reviewers |
array | Array of user objects of requested reviewers |
All the pull_request_target
workflows receive these github
properties:
github property |
Type | Value |
---|---|---|
ref |
string | "refs/heads/main" |
sha |
string | Git SHA of main |
head_ref |
string | "example-branch" |
base_ref |
string | "main" |
event_name |
string | "pull_request_target" |
ref_name |
string | "main" |
ref_type |
string | "branch" |
The event with types: [assigned]
receives:
github.event property |
Type | Value |
---|---|---|
action |
string | "assigned" |
assignee |
object | User object of the assignee |
number |
number | PR number |
pull_request |
object | PR object (see above) |
The event with types: [labeled]
receives:
github.event property |
Type | Value |
---|---|---|
action |
string | "labeled" |
label |
object | Label object |
number |
number | PR number |
pull_request |
object | PR object (see above) |
The event with types: [opened]
receives:
github.event property |
Type | Value |
---|---|---|
action |
string | "opened" |
number |
number | PR number |
pull_request |
object | PR object (see above) |
The event without an action filter receives the opened
event.
All the pull_request
workflows receive these github
properties:
github property |
Type | Value |
---|---|---|
ref |
string | "refs/pull/2/merge" |
sha |
string | Git SHA of the (proposed) PR merge commit |
head_ref |
string | "example-branch" |
base_ref |
string | "main" |
event_name |
string | "pull_request" |
ref_name |
string | "2/merge" |
ref_type |
string | "branch" |
The event with types: [opened]
receives:
github.event property |
Type | Value |
---|---|---|
action |
string | "opened" |
number |
number | PR number |
pull_request |
object | PR object (see above) |
The event without an action filter receives the opened
event.
The event with types: [assigned]
receives:
github.event property |
Type | Value |
---|---|---|
action |
string | "assigned" |
assignee |
object | User object of the assignee |
number |
number | PR number |
pull_request |
object | PR object (see above) |
The event with types: [labeled]
receives:
github.event property |
Type | Value |
---|---|---|
action |
string | "labeled" |
label |
object | Label object |
number |
number | PR number |
pull_request |
object | PR object (see above) |
In this case, the PR was changed from main
to another-base
.
This triggered two workflows:
pull_request_target
with actionedited
pull_request
with actionedited
github property |
Type | Value |
---|---|---|
ref |
string | "refs/heads/another-base" |
sha |
string | Git SHA of the new base branch |
head_ref |
string | "example-branch" |
base_ref |
string | "another-base" |
event_name |
string | "pull_request_target" |
ref_name |
string | "another-base" |
ref_type |
string | "branch" |
github.event property |
Type | Value |
---|---|---|
action |
string | "edited" |
changes |
object | Changes object with a "base" property |
number |
number | PR number |
pull_request |
object | PR object (see above) |
github property |
Type | Value |
---|---|---|
ref |
string | "refs/pull/2/merge" |
sha |
string | Git SHA of the new (proposed) PR merge commit |
head_ref |
string | "example-branch" |
base_ref |
string | "another-base" |
event_name |
string | "pull_request" |
ref_name |
string | "2/merge" |
ref_type |
string | "branch" |
github.event property |
Type | Value |
---|---|---|
action |
string | "edited" |
changes |
object | Changes object with a "base" property |
number |
number | PR number |
pull_request |
object | PR object (see above) |
In this case, I pushed a commit to example-branch
while a PR was open, proposing to merge example-branch
into
another-base
.
This triggered several workflows, in this order:
push
pull_request_target
with no action filterpull_request_target
with actionsynchronize
pull_request
with no action filterpull_request
with actionsynchronize
All of these receive a github.event
containing the same push
object:
github property |
Type | Value |
---|---|---|
ref |
string | "refs/heads/example-branch" |
sha |
string | Git SHA of the commit that was pushed |
head_ref |
string | "" (empty string) |
base_ref |
string | "" (empty string) |
event_name |
string | "push" |
ref_name |
string | "example-branch" |
ref_type |
string | "branch" |
github.event property |
Type | Value |
---|---|---|
after |
string | Git SHA of the branch after the push = Git SHA of the commit that was pushed |
base_ref |
string | null |
before |
string | Git SHA of the branch before the push |
commits |
array | Array of commit objects that were pushed |
compare |
string | URL to compare before and after the push |
forced |
boolean | Whether the push was forced |
head_commit |
object | Commit object of the head commit |
pusher |
object | Pusher object (name, email) |
ref |
string | "refs/heads/example-branch" |
Both pull_request_target
workflows receive generally the same information:
github property |
Type | Value |
---|---|---|
ref |
string | "refs/heads/another-base" |
sha |
string | Git SHA of the base branch (another-base ) |
head_ref |
string | "example-branch" |
base_ref |
string | "another-base" |
event_name |
string | "pull_request_target" |
ref_name |
string | "another-base" |
ref_type |
string | "branch" |
github.event property |
Type | Value |
---|---|---|
action |
string | "synchronize" |
after |
string | Git SHA of example-branch after the push = Git SHA of the commit that was pushed |
before |
string | Git SHA of example-branch before the push |
number |
number | PR number |
pull_request |
object | PR object (see above) |
Both pull_request
workflows receive generally the same information:
github property |
Type | Value |
---|---|---|
ref |
string | "refs/pull/2/merge" |
sha |
string | Git SHA of the (proposed) PR merge commit |
head_ref |
string | "example-branch" |
base_ref |
string | "another-base" |
event_name |
string | "pull_request" |
ref_name |
string | "2/merge" |
ref_type |
string | "branch" |
github.event property |
Type | Value |
---|---|---|
action |
string | "synchronize" |
after |
string | Git SHA of the branch after the push = Git SHA of the commit that was pushed |
before |
string | Git SHA of the branch before the push |
number |
number | PR number |
pull_request |
object | PR object (see above) |
A merge queue can generate checks on commits other than the head and merge commits of a PR. Behind the scenes, GitHub
creates a temporary branch to hold the merge result, then triggers the merge_group
event on that branch. Of course,
creating that branch also triggers other events, so depending on how the concurrency groups are set up, these events
can cancel each other. If the merge_group
event is canceled, that's considered a failure, causing the merge to be
evicted from the queue.
Setup:
- Create a rule set or classic branch protection rule:
- Enable status checks and add at least one check (I used
push.yml
for this test) - Enable merge queues
- Enable status checks and add at least one check (I used
- Add a 30-second sleep to
merge_group-checks-requested.yml
to make it take long enough to complicate merging - Create two PRs (#7 and #8) targeting
main
- Merge them both at about the same time
This created quite a few events. Note that the events were not quite exactly duplicated for the two PRs.
pull_request-enqueued
for PR #7pull_request-enqueued
for PR #8merge_group
for PR #7merge_group-checks-requested
for PR #7merge_group
for PR #8merge_group-checks-requested
for PR #8push
for the temporary branch for PR #7create
for the temporary branch for PR #7- presumably this actually happened before the push, but this is how it was displayed in the event list!
create
for the temporary branch for PR #8push
for the temporary branch for PR #8push
for the target branch, associated with PR #8pull_request-closed
for PR #7pull_request-closed
for PR #8pull_request_target-closed
for PR #8pull_request_target-closed
for PR #7delete
for the temporary branch for PR #8pull_request-dequeued
for PR #7pull_request-dequeued
for PR #8delete
for the temporary branch for PR #7
A few observations:
- The relative order of events related to the two PRs is not guaranteed.
- The relative order of events related to each PR is, surprisingly, also not guaranteed.
- There was no
push
event for the target branch associated with PR #7, even though it was merged. - The
push
events on the temporary branches happened after themerge_group
events, so if they share a concurrency group, thepush
events will cancel themerge_group
events. (This counts as a failure and can evict the merge from the queue!)
Select event details:
github property |
Type | Value |
---|---|---|
ref |
string | "refs/pull/8/merge" |
sha |
string | Git SHA of a merge commit merging PR #8 into main |
head_ref |
string | the source branch name of PR #8 |
base_ref |
string | "main" |
event_name |
string | "pull_request" |
ref_name |
string | "8/merge" |
ref_type |
string | "branch" |
github.event property |
Type | Value |
---|---|---|
action |
string | "enqueued" |
number |
number | PR number = 8 |
pull_request |
object | PR object (see above) |
github property |
Type | Value |
---|---|---|
ref |
string | "refs/heads/gh-readonly-queue/main/pr-7-d49b56145b99c35fcabcbdc1293bfe4500a660f8" |
sha |
string | Git SHA of the merge commit for this PR, which was later pushed to main = "061c9387c245a237e608cffb69999a9e564d2ffa" |
head_ref |
string | "" (empty string) |
base_ref |
string | "" (empty string) |
event_name |
string | "merge_group" |
ref_name |
string | "gh-readonly-queue/main/pr-7-d49b56145b99c35fcabcbdc1293bfe4500a660f8" |
ref_type |
string | "branch" |
github.event property |
Type | Value |
---|---|---|
action |
string | "checks_requested" |
merge_group |
object | Merge group object (see below) |
github.event.merge_group property |
Type | Value |
---|---|---|
base_ref |
string | "refs/heads/main" |
base_sha |
string | Git SHA of main (first parent of the merge commit) = "d49b56145b99c35fcabcbdc1293bfe4500a660f8" |
head_commit |
object | Commit object corresponding to head_sha |
head_ref |
string | "refs/head/gh-readonly-queue/main/pr-7-d49b56145b99c35fcabcbdc1293bfe4500a660f8" |
head_sha |
string | Same as github.sha = "061c9387c245a237e608cffb69999a9e564d2ffa" |
Note that for this event, github.ref
does NOT end with the same hash as github.sha
.
github property |
Type | Value |
---|---|---|
ref |
string | "refs/heads/gh-readonly-queue/main/pr-8-061c9387c245a237e608cffb69999a9e564d2ffa" |
sha |
string | Git SHA of the merge commit for PR #8 on top of PR #7, which was later pushed to main = "eaa88899f4e18417ab4ddf1e39ad9d7f10c92fc1" |
head_ref |
string | "" (empty string) |
base_ref |
string | "" (empty string) |
event_name |
string | "merge_group" |
ref_name |
string | "gh-readonly-queue/main/pr-8-061c9387c245a237e608cffb69999a9e564d2ffa" |
ref_type |
string | "branch" |
github.event property |
Type | Value |
---|---|---|
action |
string | "checks_requested" |
merge_group |
object | Merge group object (see below) |
github.event.merge_group property |
Type | Value |
---|---|---|
base_ref |
string | "refs/heads/main" |
base_sha |
string | Git SHA of PR #7's merge commit = "061c9387c245a237e608cffb69999a9e564d2ffa" |
head_commit |
object | Commit object corresponding to head_sha |
head_ref |
string | "refs/heads/gh-readonly-queue/main/pr-8-061c9387c245a237e608cffb69999a9e564d2ffa" |
head_sha |
string | Same as github.sha = "eaa88899f4e18417ab4ddf1e39ad9d7f10c92fc1" |
github property |
Type | Value |
---|---|---|
ref |
string | "refs/heads/gh-readonly-queue/main/pr-8-061c9387c245a237e608cffb69999a9e564d2ffa" |
sha |
string | Git SHA of the merge commit for PR #8 on top of PR #7, which was later pushed to main = "eaa88899f4e18417ab4ddf1e39ad9d7f10c92fc1" |
head_ref |
string | "" (empty string) |
base_ref |
string | "" (empty string) |
event_name |
string | "push" |
ref_name |
string | "gh-readonly-queue/main/pr-8-061c9387c245a237e608cffb69999a9e564d2ffa" |
ref_type |
string | "branch" |
github.event property |
Type | Value |
---|---|---|
after |
string | Same as github.sha = "eaa88899f4e18417ab4ddf1e39ad9d7f10c92fc1" |
before |
string | "0000000000000000000000000000000000000000" |
commits |
array | Array containing the head_commit object |
compare |
string | URL to view commit "eaa88899f4e18417ab4ddf1e39ad9d7f10c92fc1" |
forced |
boolean | false |
head_commit |
object | Commit object for "eaa88899f4e18417ab4ddf1e39ad9d7f10c92fc1" |
pusher |
object | {"email": null, "name": "github-merge-queue[bot]"} |
ref |
string | "refs/heads/gh-readonly-queue/main/pr-8-061c9387c245a237e608cffb69999a9e564d2ffa" |
github property |
Type | Value |
---|---|---|
ref |
string | "refs/heads/main" |
sha |
string | Git SHA of the merge commit for PR #8 on top of PR #7 = "eaa88899f4e18417ab4ddf1e39ad9d7f10c92fc1" |
head_ref |
string | "" (empty string) |
base_ref |
string | "" (empty string) |
event_name |
string | "push" |
ref_name |
string | "main" |
ref_type |
string | "branch" |
github.event property |
Type | Value |
---|---|---|
after |
string | Same as github.sha = "eaa88899f4e18417ab4ddf1e39ad9d7f10c92fc1" |
before |
string | Git SHA of main before the push = "d49b56145b99c35fcabcbdc1293bfe4500a660f8" |
commits |
array | Array containing the head_commit object |
compare |
string | URL to compare from "d49b56145b99c35fcabcbdc1293bfe4500a660f8" to "eaa88899f4e18417ab4ddf1e39ad9d7f10c92fc1" |
forced |
boolean | false |
head_commit |
object | Commit object for "eaa88899f4e18417ab4ddf1e39ad9d7f10c92fc1" |
pusher |
object | {"email": null, "name": "github-merge-queue[bot]"} |
ref |
string | "refs/heads/main" |