Set Commit Status In Another Repo

How the tests executed in one repo can set the commit status in another repo.

See the blog post Separate Application And Tests Repos GitHub Actions Setup for how I set up two repos: one for a web application, another for its tests. In this blog post I will show how I set the commit status in the first repo before the tests execute and then when the tests finish. Having an explicit test commit status lets you protected the main branch from pull requests that have not executed the tests, or where the tests have failed.

🎁 You can find the web application repo at bahmutov/todo-app, and the tests repo at bahmutov/todo-app-tests.

Trigger the workflow

When the user types /cypress comment on a pull request in the first repo "test-app", we can get the commit SHA from the pull request itself.

.github/workflows/slash-command-cypress.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- name: Trigger Cypress run
# https://github.com/actions/github-script
uses: actions/github-script@v6
id: trigger
# pass this repo's name and PR number
# plus the comment ID so the tests workflow can post results back
env:
REPO_NAME: ${{ github.event.client_payload.slash_command.args.named.repository }}
PR_NUMBER: ${{ github.event.client_payload.pull_request.number }}
REF: ${{ github.event.client_payload.slash_command.args.named.ref }}
FEEDBACK_COMMENT_ID: ${{ github.event.client_payload.github.payload.comment.id }}
COMMIT_SHA: ${{ github.event.client_payload.pull_request.head.sha }}
with:
# unfortunately we need to use a personal token to trigger
# a workflow in another repo, cannot use just GITHUB_TOKEN
github-token: ${{ secrets.PERSONAL_TOKEN }}
script: |
const result = await require('./.github/workflows/trigger-cypress.js')({ github, core });
return result;

You can see the full workflow here. For example, in the PR 3 the last commit short SHA was 9053795.

The branch head commit SHA

We can see this commit being sent to the test repo inside the payload body

The full commit SHA is sent with the test trigger

The tests workflow

When the tests workflow starts, it should update the commit status twice. First, before anything runs we set the commit status to "pending" to let the user know the tests are executing. After the tests finish, we can set the final result (success, failure, error) based on the outcome of the test job.

You can find the full testing workflow here. The relevant part is below

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# add a pending commit status check
# https://github.com/bahmutov/cypress-set-github-status
- name: Set pending commit status 🚦
if: ${{ github.event.client_payload.commit }}
env:
PERSONAL_GH_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
run: |
npx --package cypress-set-github-status set-gh-status \
--repo ${{ github.event.client_payload.repo }} \
--commit ${{ github.event.client_payload.commit }} \
--status pending --context "todo-app-tests" \
--description "E2E tests" \
--target-url ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}

# check out both the app and the tests
- name: Checkout this repo 🛎
uses: actions/checkout@v4

- name: Checkout the application repo 🛎
uses: actions/checkout@v4
with:
repository: ${{ github.event.client_payload.repo }}
ref: ${{ github.event.client_payload.ref }}
path: app

- name: Install app dependencies 📦
uses: bahmutov/npm-install@v1
with:
working-directory: app

- name: Start the application 🎬
run: |
cd app
npm run start &

- name: Run E2E tests 🏃🏻‍♂️
id: tests
uses: cypress-io/github-action@v6

- name: Post results 📨
if: ${{ always() && github.event.client_payload.repo && github.event.client_payload.feedbackCommentId }}
uses: peter-evans/create-or-update-comment@v3
with:
# need a personal token to be able to post a comment back
# in the original repo
token: ${{ secrets.PERSONAL_TOKEN }}
comment-id: ${{ github.event.client_payload.feedbackCommentId }}
issue-number: ${{ github.event.client_payload.pullRequestNumber }}
repository: ${{ github.event.client_payload.repo }}
body: |
Tests result: ${{ steps.tests.outcome }}
reactions: |
${{ steps.tests.outcome == 'success' && 'hooray' || '-1' }}

# set the commit status based on the E2E test job outcome
- name: Update the commit status 🚦
if: ${{ github.event.client_payload.commit }}
env:
PERSONAL_GH_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
run: |
npx --package cypress-set-github-status set-gh-status \
--repo ${{ github.event.client_payload.repo }} \
--commit ${{ github.event.client_payload.commit }} \
--status ${{ steps.tests.outcome }} --context "todo-app-tests" \
--description "E2E tests" \
--target-url ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
echo 'Posted job outcome ${{ steps.tests.outcome }} to \
${{ github.event.client_payload.repo }} commit ${{ github.event.client_payload.commit }}' >> $GITHUB_STEP_SUMMARY

I am using my own utility cypress-set-github-status. Typically you would pass the commit status which is one of the values "pending", "success", "failure", or "error". But we can also use --status ${{ steps.tests.outcome }} which will automatically convert job status string to the commit status: job outcome "success" is commit status "success", "failure" is "failure", and both job outcomes "canceled" and "skipped" are commit status "error".

Here is a passing test commit status set back in the "todo-app" pull request

The passing tests commit status

Now we can configure the branch protection rule in the "todo-app" repo that requires the "todo-app-tests" status check to be green before merging.

Branch protection requiring the todo-app-tests to pass