Skip to content

Commit 8d14b71

Browse files
authored
Trigger integration tests by setting a "tests-requested" label. (#339)
This PR adds some label-based triggers for integration tests, which allow the integration tests to be requested on a PR by setting the "tests-requested" label. There are two modes for requesting tests, "quick" and "full": "quick" tries to be clever and figure out a subset of APIs (and even platforms) to test. This heuristic is really simple right now (it just looks at directory names and, for platform, filenames) and if there is any ambiguity it will just run the normal suite of tests. "full" just runs all the tests. Currently this is set to use an expanded test matrix, though this could be changed in the future. The test workflow handles updating the labels and posting/updating a comment with the status of the integration tests as they run. All of the jobs that mess with labels and comments are skipped if the workflow wasn't run from the PR label. (See #337—which won't be merged—for an example of "quick" mode in action, autodetecting to just run a subset of tests.)
1 parent d221c5f commit 8d14b71

File tree

3 files changed

+343
-8
lines changed

3 files changed

+343
-8
lines changed

.github/workflows/check-labels.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: Check PR Labels
2+
on:
3+
pull_request:
4+
types: [opened, reopened, synchronize, labeled, unlabeled]
5+
6+
env:
7+
triggerLabelFull: "tests-requested: full"
8+
triggerLabelQuick: "tests-requested: quick"
9+
statusLabelInProgress: "tests: in-progress"
10+
statusLabelFailed: "tests: failed"
11+
12+
jobs:
13+
check_integration_test_labels:
14+
# This check fails if integration tests are queued, in progress, or failed.
15+
runs-on: ubuntu-latest
16+
steps:
17+
- uses: docker://agilepathway/pull-request-label-checker:latest
18+
with:
19+
none_of: "${{ env.statusLabelInProgress }},${{ env.statusLabelFailed }},${{ env.triggerLabelFull }},${{ env.triggerLabelQuick }}"
20+
repo_token: ${{ github.token }}

.github/workflows/integration_tests.yml

Lines changed: 236 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ name: Integration tests
33
on:
44
schedule:
55
- cron: "0 9 * * *" # 9am UTC = 1am PST / 2am PDT
6+
pull_request:
7+
types: [ labeled ]
8+
69
workflow_dispatch:
710
inputs:
811
platforms:
@@ -39,12 +42,119 @@ on:
3942
required: true
4043
test_packaged_sdk:
4144
description: 'Optional: Packaging run # to build against?'
45+
env:
46+
triggerLabelPrefix: "tests-requested: "
47+
triggerLabelFull: "tests-requested: full"
48+
triggerLabelQuick: "tests-requested: quick"
49+
statusLabelInProgress: "tests: in-progress"
50+
statusLabelFailed: "tests: failed"
51+
statusLabelSucceeded: "tests: succeeded"
52+
statusCommentIdentifier: "integration-test-status-comment"
4253

4354
jobs:
55+
check_trigger:
56+
### This job only runs when the workflow was triggered by a PR getting labeled.
57+
### It checks whether the label is a test-request trigger (cancelling
58+
### the workflow if not), and then sets the in-progress label. It sets
59+
### outputs to control the test matrix (full or quick) and to tell
60+
### subsequent steps to update the labels as well.
61+
runs-on: ubuntu-latest
62+
if: github.event_name == 'pull_request' && github.event.action == 'labeled'
63+
outputs:
64+
should_update_labels: ${{ steps.set_outputs.outputs.should_update_labels }}
65+
requested_tests: ${{ steps.set_outputs.outputs.requested_tests }}
66+
steps:
67+
### If the label isn't one of the test-request triggers, cancel the workflow.
68+
- name: cancel workflow if label is irrelevant
69+
if: ${{ !startsWith(github.event.label.name, env.triggerLabelPrefix) }}
70+
uses: andymckay/cancel-action@0.2
71+
- name: wait for above cancellation if label is irrelevant
72+
if: ${{ !startsWith(github.event.label.name, env.triggerLabelPrefix) }}
73+
run: |
74+
sleep 300
75+
exit 1 # fail out if the cancellation above somehow failed.
76+
### Below this line, the label is one of the test-request triggers.
77+
- name: cancel previous runs on the same PR
78+
uses: styfle/cancel-workflow-action@0.8.0
79+
with:
80+
access_token: ${{ github.token }}
81+
- name: remove triggering label
82+
uses: buildsville/add-remove-label@v1
83+
with:
84+
token: ${{ github.token }}
85+
label: "${{ github.event.label.name }}"
86+
type: remove
87+
### Fail the workflow if the user does not have admin access to run the tests.
88+
- name: check if user has permission to trigger tests
89+
uses: lannonbr/repo-permission-check-action@2.0.0
90+
with:
91+
permission: "admin"
92+
env:
93+
GITHUB_TOKEN: ${{ github.token }}
94+
- id: set_outputs
95+
run: |
96+
echo "::set-output name=should_update_labels::1"
97+
if [[ "${{ github.event.label.name }}" == "${{ env.triggerLabelFull }}" ]]; then
98+
echo "::set-output name=requested_tests::full"
99+
elif [[ "${{ github.event.label.name }}" == "${{ env.triggerLabelQuick }}" ]]; then
100+
echo "::set-output name=requested_tests::auto"
101+
fi
102+
### Add the in-progress label and remove any previous success/fail labels.
103+
- name: add in-progress label
104+
uses: buildsville/add-remove-label@v1
105+
with:
106+
token: ${{ github.token }}
107+
label: "${{ env.statusLabelInProgress }}"
108+
type: add
109+
- name: remove previous success label
110+
uses: buildsville/add-remove-label@v1
111+
with:
112+
token: ${{ github.token }}
113+
label: "${{ env.statusLabelSucceeded }}"
114+
type: remove
115+
- name: remove previous failure label
116+
uses: buildsville/add-remove-label@v1
117+
with:
118+
token: ${{ github.token }}
119+
label: "${{ env.statusLabelFailed }}"
120+
type: remove
121+
- name: find old status comment, if any
122+
uses: peter-evans/find-comment@v1
123+
id: find-comment
124+
with:
125+
issue-number: ${{github.event.number}}
126+
body-includes: ${{ env.statusCommentIdentifier }}
127+
token: ${{github.token}}
128+
- name: delete old status comment
129+
if: ${{ steps.find-comment.outputs.comment-id != 0 }}
130+
uses: jungwinter/comment@v1
131+
with:
132+
type: delete
133+
comment_id: ${{ steps.find-comment.outputs.comment-id }}
134+
token: ${{ github.token }}
135+
- name: get current time for status comment
136+
id: get-time
137+
shell: bash
138+
run: |
139+
echo -n "::set-output name=time::"
140+
TZ=America/Los_Angeles date
141+
- name: add in progress status comment
142+
uses: phulsechinmay/rewritable-pr-comment@v0.2.1
143+
with:
144+
message: |
145+
### ⏳  Integration test in progress...
146+
Requested by @${{github.actor}} on commit ${{github.event.pull_request.head.sha}}
147+
Last updated: ${{ steps.get-time.outputs.time }}
148+
**[View integration test run](https://github.com/${{github.repository}}/actions/runs/${{github.run_id}})**
149+
GITHUB_TOKEN: ${{ github.token }}
150+
COMMENT_IDENTIFIER: ${{ env.statusCommentIdentifier }}
151+
44152
# To feed input into the job matrix, we first need to convert to a JSON
45153
# list. Then we can use fromJson to define the field in the matrix for the tests job.
46154
prepare_matrix:
47155
runs-on: ubuntu-latest
156+
needs: check_trigger
157+
if: ${{ !cancelled() && !failure() }} # Run even if check_trigger was skipped.
48158
outputs:
49159
matrix_platform: ${{ steps.export-result.outputs.matrix_platform }}
50160
matrix_os: ${{ steps.export-result.outputs.matrix_os }}
@@ -57,17 +167,24 @@ jobs:
57167
steps:
58168
- uses: actions/checkout@v2
59169
with:
170+
fetch-depth: 0
60171
submodules: false
61172
- name: Use expanded matrix
62-
if: github.event_name == 'schedule' || github.event.inputs.use_expanded_matrix == '1'
173+
if: github.event_name == 'schedule' || github.event.inputs.use_expanded_matrix == '1' || needs.check_trigger.outputs.requested_tests == 'full'
63174
run: |
64175
echo "EXPANDED_MATRIX_PARAM=-e=1" >> $GITHUB_ENV
65-
176+
- name: Set auto-diff option if specified.
177+
if: needs.check_trigger.outputs.requested_tests == 'auto'
178+
run: |
179+
echo "Autodetecting which tests to run."
180+
echo "AUTO_DIFF_PARAM=--auto_diff ${{github.event.pull_request.base.sha}}" >> $GITHUB_ENV
181+
66182
- id: export-result
67183
# e.g. 'ubuntu-latest,macos-latest' -> '["ubuntu-latest","macos-latest"]'
68184
run: |
69-
echo "::set-output name=matrix_platform::$( python scripts/gha/print_matrix_configuration.py -w integration_tests ${EXPANDED_MATRIX_PARAM} -k platform -o "${{github.event.inputs.platforms}}" )"
70-
echo "::set-output name=matrix_os::$( python scripts/gha/print_matrix_configuration.py -w integration_tests ${EXPANDED_MATRIX_PARAM} -k os -o "${{github.event.inputs.operating_systems}}" )"
185+
echo "::set-output name=apis::$( python scripts/gha/print_matrix_configuration.py -c -w integration_tests -k apis -o "${{github.event.inputs.apis}}" ${AUTO_DIFF_PARAM})"
186+
echo "::set-output name=matrix_platform::$( python scripts/gha/print_matrix_configuration.py -w integration_tests ${EXPANDED_MATRIX_PARAM} -k platform -o "${{github.event.inputs.platforms}}" ${AUTO_DIFF_PARAM})"
187+
echo "::set-output name=matrix_os::$( python scripts/gha/print_matrix_configuration.py -w integration_tests ${EXPANDED_MATRIX_PARAM} -k os -o "${{github.event.inputs.operating_systems}}" ${AUTO_DIFF_PARAM})"
71188
# If building against a packaged SDK, only use boringssl.
72189
if [[ -n "${{ github.event.inputs.test_packaged_sdk }}" ]]; then
73190
echo "::warning ::Downloading SDK package from previous run: https://github.com/firebase/firebase-cpp-sdk/actions/runs/${{ github.event.inputs.test_packaged_sdk }}"
@@ -76,16 +193,18 @@ jobs:
76193
else
77194
echo "::set-output name=matrix_ssl::$( python scripts/gha/print_matrix_configuration.py -w integration_tests ${EXPANDED_MATRIX_PARAM} -k ssl_lib -o "${{github.event.inputs.desktop_ssl_variants}}" )"
78195
fi
79-
echo "::set-output name=apis::$( python scripts/gha/print_matrix_configuration.py -c -w integration_tests -k apis -o "${{github.event.inputs.apis}}" )"
80196
echo "::set-output name=android_device::$( python scripts/gha/print_matrix_configuration.py -c -w integration_tests -k android_device -o "${{github.event.inputs.android_device}}" )"
81197
echo "::set-output name=android_api::$( python scripts/gha/print_matrix_configuration.py -c -w integration_tests -k android_api -o "${{github.event.inputs.android_api}}" )"
82198
echo "::set-output name=ios_device::$( python scripts/gha/print_matrix_configuration.py -c -w integration_tests -k ios_device -o "${{github.event.inputs.ios_device}}" )"
83199
echo "::set-output name=ios_version::$( python scripts/gha/print_matrix_configuration.py -c -w integration_tests -k ios_version -o "${{github.event.inputs.ios_version}}" )"
84200
85201
tests:
86202
name: ${{ matrix.os }}-${{ matrix.target_platform }}-${{ matrix.ssl_variant }}
87-
needs: prepare_matrix
203+
needs: [prepare_matrix, check_trigger]
88204
runs-on: ${{ matrix.os }}
205+
# Skip this if there is an empty matrix (which can happen if "auto" was set above).
206+
# But check cancelled() && !failure() so it runs even if check_trigger was skipped.
207+
if: needs.prepare_matrix.outputs.matrix_platform != '[]' && needs.prepare_matrix.outputs.apis != '' && !cancelled() && !failure()
89208
strategy:
90209
fail-fast: false
91210
matrix:
@@ -139,7 +258,7 @@ jobs:
139258
with:
140259
path: /tmp/android-ndk-r16b
141260
key: android-ndk-${{ matrix.os }}-r16b
142-
261+
143262
- name: Setup python
144263
uses: actions/setup-python@v2
145264
with:
@@ -240,6 +359,36 @@ jobs:
240359
if: matrix.target_platform != 'Desktop' && !cancelled()
241360
run: |
242361
python scripts/gha/test_lab.py --android_model ${{ needs.prepare_matrix.outputs.android_device }} --android_api ${{ needs.prepare_matrix.outputs.android_api }} --ios_model ${{ needs.prepare_matrix.outputs.ios_device }} --ios_version ${{ needs.prepare_matrix.outputs.ios_version }} --testapp_dir testapps --code_platform cpp --key_file scripts/gha-encrypted/gcs_key_file.json
362+
363+
### The below allow us to set the failure label and comment early, when the first failure
364+
### in the matrix occurs. It'll be cleaned up in a subsequent job.
365+
- name: add failure label
366+
# We can do mark a failure as soon as any one test fails.
367+
if: ${{ needs.check_trigger.outputs.should_update_labels && failure() && !cancelled() }}
368+
uses: buildsville/add-remove-label@v1
369+
with:
370+
token: ${{ github.token }}
371+
label: "${{ env.statusLabelFailed }}"
372+
type: add
373+
- name: get current time for status comment
374+
id: get-time
375+
if: ${{ needs.check_trigger.outputs.should_update_labels && failure() && !cancelled() }}
376+
shell: bash
377+
run: |
378+
echo -n "::set-output name=time::"
379+
TZ=America/Los_Angeles date
380+
- name: add failure status comment
381+
uses: phulsechinmay/rewritable-pr-comment@v0.2.1
382+
if: ${{ needs.check_trigger.outputs.should_update_labels && failure() && !cancelled() }}
383+
with:
384+
message: |
385+
### ❌  Integration test FAILED (but still ⏳  in progress)
386+
Requested by @${{github.actor}} on commit ${{github.event.pull_request.head.sha}}
387+
Last updated: ${{ steps.get-time.outputs.time }}
388+
**[View integration test results](https://github.com/${{github.repository}}/actions/runs/${{github.run_id}})**
389+
GITHUB_TOKEN: ${{ github.token }}
390+
COMMENT_IDENTIFIER: ${{ env.statusCommentIdentifier }}
391+
243392
- name: Summarize build and test results
244393
if: ${{ !cancelled() }}
245394
shell: bash
@@ -248,3 +397,83 @@ jobs:
248397
if [[ "${{ job.status }}" != "success" ]]; then
249398
exit 1
250399
fi
400+
401+
add_success_label:
402+
name: "add-success-label"
403+
needs: [check_trigger, tests]
404+
runs-on: ubuntu-latest
405+
if: ${{ needs.check_trigger.outputs.should_update_labels && !cancelled() && !failure() }}
406+
steps:
407+
- name: add success label
408+
uses: buildsville/add-remove-label@v1
409+
with:
410+
token: ${{github.token}}
411+
label: "${{ env.statusLabelSucceeded }}"
412+
type: add
413+
- name: get current time for status comment
414+
id: get-time
415+
shell: bash
416+
run: |
417+
echo -n "::set-output name=time::"
418+
TZ=America/Los_Angeles date
419+
- name: add success status comment
420+
uses: phulsechinmay/rewritable-pr-comment@v0.2.1
421+
with:
422+
message: |
423+
### ✅  Integration test succeeded!
424+
Requested by @${{github.actor}} on commit ${{github.event.pull_request.head.sha}}
425+
Last updated: ${{ steps.get-time.outputs.time }}
426+
**[View integration test results](https://github.com/${{github.repository}}/actions/runs/${{github.run_id}})**
427+
GITHUB_TOKEN: ${{ github.token }}
428+
COMMENT_IDENTIFIER: ${{ env.statusCommentIdentifier }}
429+
430+
add_failure_label:
431+
name: "add-failure-label"
432+
needs: [check_trigger, tests]
433+
runs-on: ubuntu-latest
434+
if: ${{ needs.check_trigger.outputs.should_update_labels && !cancelled() && failure() }}
435+
steps:
436+
- name: add failure label
437+
uses: buildsville/add-remove-label@v1
438+
with:
439+
token: ${{ github.token }}
440+
label: "${{ env.statusLabelFailed }}"
441+
type: add
442+
- name: get current time for status comment
443+
id: get-time
444+
shell: bash
445+
run: |
446+
echo -n "::set-output name=time::"
447+
TZ=America/Los_Angeles date
448+
- name: add failure status comment
449+
uses: phulsechinmay/rewritable-pr-comment@v0.2.1
450+
with:
451+
message: |
452+
### ❌  Integration test FAILED
453+
Requested by @${{github.actor}} on commit ${{github.event.pull_request.head.sha}}
454+
Last updated: ${{ steps.get-time.outputs.time }}
455+
**[View integration test results](https://github.com/${{github.repository}}/actions/runs/${{github.run_id}})**
456+
GITHUB_TOKEN: ${{ github.token }}
457+
COMMENT_IDENTIFIER: ${{ env.statusCommentIdentifier }}
458+
459+
remove_in_progress_label:
460+
name: "remove-in-progress-label"
461+
needs: [check_trigger, tests]
462+
runs-on: ubuntu-latest
463+
if: ${{ needs.check_trigger.outputs.should_update_labels && !cancelled() }}
464+
steps:
465+
# Use a different token to remove the "in-progress" label,
466+
# to allow the removal to trigger the "Check Labels" workflow.
467+
- name: Generate token for GitHub API
468+
uses: tibdex/github-app-token@v1
469+
id: generate-token
470+
with:
471+
app_id: ${{ secrets.WORKFLOW_TRIGGER_APP_ID }}
472+
private_key: ${{ secrets.WORKFLOW_TRIGGER_APP_PRIVATE_KEY }}
473+
- name: remove in progress label
474+
uses: buildsville/add-remove-label@v1
475+
with:
476+
token: ${{steps.generate-token.outputs.token}}
477+
label: "${{ env.statusLabelInProgress }}"
478+
type: remove
479+

0 commit comments

Comments
 (0)