Skip to content

Commit cac6a39

Browse files
!test Comment Command (#73)
* Add config-comment-repro.yml workflow for repro tests on any branch! * Replace `against COMMITISH` with `compare COMMITISH` * Added ability to commit the result of the repro test to the PR * Added RUN_URL, removed some of the branch name stuff * Add additional artifact content paths section for repro-ci * Removed custom `[compare COMMITISH]` logic * Update artifact structure to be in line with #74 * Renamed file, made it a more generic `!test` command * Changed artifact paths back to initial version, removed `inputs.additional-artifact-content-paths` input * Removed issue property from `github.event.comment.body` * Reworked permission checking, bug fixes, comment updates * Further changes from testing * Added error-checking for `!test` command * Made comments more informative * Update README.md to account for new `!test repro` syntax * Update .github/workflows/config-comment-test.yml Co-authored-by: Aidan Heerdegen <[email protected]> * README.md: Note the case-sensitivity of the command * Add step in prepare-command to check the command starts with `!test` --------- Co-authored-by: Aidan Heerdegen <[email protected]>
1 parent e55ce12 commit cac6a39

File tree

2 files changed

+413
-7
lines changed

2 files changed

+413
-7
lines changed
+375
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,375 @@
1+
name: 'Comment Command: !test'
2+
run-name: '!test on ${{ github.repository }}'
3+
on:
4+
workflow_call:
5+
# Triggered in calling workflow by:
6+
# on:
7+
# issue_comment:
8+
# - edited
9+
# - created
10+
env:
11+
USAGE: '!test TYPE [commit]'
12+
USAGE_TYPES: repro
13+
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
14+
jobs:
15+
permission-check:
16+
name: Permission Check
17+
runs-on: ubuntu-latest
18+
env:
19+
GH_TOKEN: ${{ github.token }}
20+
permissions:
21+
pull-requests: write
22+
steps:
23+
- name: Determine if commenter has permission to run the command
24+
id: commenter
25+
# TODO: The permissions-checking section seems ripe for turning into an action!
26+
run: |
27+
commenter_permissions=$(gh api \
28+
repos/${{ github.repository }}/collaborators/${{ github.event.comment.user.login }}/permission \
29+
--jq '.permission'
30+
)
31+
if [[ "$commenter_permissions" == "admin" || "$commenter_permissions" == "write" ]]; then
32+
echo "Commenter has at least write permission"
33+
echo "permission=true" >> $GITHUB_OUTPUT
34+
else
35+
echo "Commenter does not have at least write permission"
36+
echo "permission=false" >> $GITHUB_OUTPUT
37+
fi
38+
39+
- name: React to '!test'
40+
uses: access-nri/actions/.github/actions/react-to-comment@main
41+
with:
42+
reaction: ${{ steps.commenter.outputs.permission == 'true' && 'rocket' || 'confused' }}
43+
token: ${{ github.token }}
44+
45+
- name: Fail if no permissions
46+
if: steps.commenter.outputs.permission == 'false'
47+
run: exit 1
48+
49+
ci-config:
50+
name: Read CI Testing Configuration
51+
needs:
52+
- permission-check
53+
runs-on: ubuntu-latest
54+
outputs:
55+
markers: ${{ steps.repro-config.outputs.markers }}
56+
payu-version: ${{ steps.repro-config.outputs.payu-version }}
57+
model-config-tests-version: ${{ steps.repro-config.outputs.model-config-tests-version }}
58+
steps:
59+
- name: Checkout main
60+
uses: actions/checkout@v4
61+
with:
62+
ref: main
63+
64+
- name: Validate `config/ci.json`
65+
uses: access-nri/schema/.github/actions/validate-with-schema@main
66+
with:
67+
# As with a lot of the `vars`/`secrets` in this repo, this will be defined in the caller repo
68+
schema-version: ${{ vars.CI_JSON_SCHEMA_VERSION }}
69+
meta-schema-version: draft-2020-12
70+
schema-location: au.org.access-nri/model/configuration/ci
71+
data-location: config/ci.json
72+
73+
- name: Get base branch for PR
74+
id: base
75+
env:
76+
GH_TOKEN: ${{ github.token }}
77+
run: |
78+
gh pr checkout ${{ github.event.issue.number }}
79+
echo "branch=$(gh pr view --json baseRefName --jq '.baseRefName')"
80+
81+
- name: Read reproducibility tests config
82+
id: repro-config
83+
uses: access-nri/model-config-tests/.github/actions/parse-ci-config@main
84+
with:
85+
check: reproducibility
86+
branch-or-tag: ${{ steps.base.outputs.branch }}
87+
config-filepath: "config/ci.json"
88+
89+
prepare-command:
90+
name: Prepare Command
91+
needs:
92+
- permission-check
93+
runs-on: ubuntu-latest
94+
env:
95+
GH_TOKEN: ${{ github.token }}
96+
outputs:
97+
test-type: ${{ steps.test.outputs.type }}
98+
# the full git hash of the configuration being tested
99+
config-hash: ${{ steps.pr.outputs.hash }}
100+
# the short git hash of the configuration being tested
101+
config-short-hash: ${{ steps.pr.outputs.short-hash }}
102+
# the git ref (branch or tag) of the configuration being tested
103+
config-ref: ${{ steps.pr.outputs.ref }}
104+
# the full git hash of the configuration being compared against
105+
compared-config-hash: ${{ steps.compared.outputs.hash }}
106+
# the short git hash of the configuration being compared against
107+
compared-config-short-hash: ${{ steps.compared.outputs.short-hash }}
108+
# the git ref (branch or tag) of the configuration being compared against
109+
compared-config-ref: ${{ steps.compared.outputs.ref }}
110+
# whether the commenter can commit to the repo (either 'true', 'false' or '' (when no 'commit' option given))
111+
commit-requested: ${{ steps.commit.outputs.requested }}
112+
# TODO: Make this an input to the command when we start deploying to multiple targets
113+
environment-name: Gadi
114+
permissions:
115+
pull-requests: write
116+
steps:
117+
- uses: actions/checkout@v4
118+
with:
119+
fetch-depth: 0
120+
fetch-tags: true
121+
122+
- name: Verify !test
123+
run: |
124+
if [[ "${{ startsWith(github.event.comment.body, '!test') }}" == "true" ]]; then
125+
echo 'Command starts with !test'
126+
else
127+
echo "::error::Usage: ${{ env.USAGE }}"
128+
echo "::error::Command must start with !test to invoke model-config-tests' config-comment-test.yml."
129+
exit 1
130+
131+
- name: Get !test type
132+
id: test
133+
run: |
134+
command="${{ github.event.comment.body }}"
135+
read -ra command_tokens <<< "$command"
136+
type_in_comment="${command_tokens[1]}"
137+
type_in_comment_valid=false
138+
139+
for type in ${{ env.USAGE_TYPES }}; do
140+
if [[ "$type_in_comment" == "$type" ]]; then
141+
type_in_comment_valid=true
142+
break
143+
fi
144+
done
145+
146+
if [[ "$type_in_comment_valid" == "false" ]]; then
147+
echo "::error::Usage: ${{ env.USAGE }}"
148+
echo "::error::The command '${{ github.event.comment.body }}' doesn't have a valid TYPE. Was given '$type_in_comment', but needed to be one of: ${{ env.USAGE_TYPES }}."
149+
exit 1
150+
fi
151+
152+
echo "type=$type_in_comment" >> $GITHUB_OUTPUT
153+
154+
- name: Get config ref from PR comment
155+
id: pr
156+
run: |
157+
gh pr checkout ${{ github.event.issue.number }}
158+
echo "hash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
159+
echo "short-hash=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
160+
echo "ref=$(git rev-parse --abbrev-ref HEAD)" >> $GITHUB_OUTPUT
161+
162+
- name: Get compared config ref
163+
id: compared
164+
# Set up a default ref
165+
# We need to do this roundabout way to get the source branch commit because
166+
# We can't access github.base_ref as this trigger is on.issue_comment
167+
run: |
168+
ref=$(gh pr view ${{ github.event.issue.number }} --json baseRefName --jq '.baseRefName')
169+
echo "Using $ref"
170+
hash=$(git show-branch --merge-base origin/$ref origin/${{ steps.pr.outputs.ref }})
171+
short_hash=$(git rev-parse --short $hash)
172+
echo "AKA $hash (shortened as $short_hash)"
173+
174+
echo "hash=$hash" >> $GITHUB_OUTPUT
175+
echo "short-hash=$short_hash" >> $GITHUB_OUTPUT
176+
echo "ref=$ref" >> $GITHUB_OUTPUT
177+
178+
- name: Determine whether to commit
179+
id: commit
180+
# Determine whether the commenter wants to commit something to the repository
181+
run: |
182+
command="${{ github.event.comment.body }}"
183+
read -ra command_tokens <<< "$command"
184+
potential_commit_token="${command_tokens[2]}"
185+
186+
if [[ "$potential_commit_token" == "commit" ]]; then
187+
echo "'commit' option given"
188+
echo "requested=true" >> $GITHUB_OUTPUT
189+
elif [ -z "$potential_commit_token" ]; then
190+
echo "'commit' option not given"
191+
echo "requested=false" >> $GITHUB_OUTPUT
192+
else
193+
echo "::error::Non-commit option '$potential_commit_token' given. Usage: ${{ env.USAGE }}"
194+
exit 1
195+
fi
196+
197+
- name: Erroneous tokens check
198+
# If commit is given, any tokens after the 3rd are considered erroneous - otherwise it is after the 2nd
199+
# TODO: This will need to be made more robust if there are other options added
200+
run: |
201+
command="${{ github.event.comment.body }}"
202+
read -ra command_tokens <<< "$command"
203+
204+
max_tokens_allowed=$([[ "${{ steps.commit.outputs.requested }}" == "true" ]] && echo "3" || echo "2")
205+
206+
if [ "${#command_tokens[@]}" -gt "$max_tokens_allowed" ]; then
207+
echo "::error::Erroneous tokens given. Usage: ${{ env.USAGE }}"
208+
exit 1
209+
fi
210+
211+
repro:
212+
name: Compare ${{ needs.prepare-command.outputs.config-ref }} against ${{ needs.prepare-command.outputs.compared-config-ref }}
213+
needs:
214+
- prepare-command
215+
- ci-config
216+
if: needs.prepare-command.outputs.test-type == 'repro'
217+
uses: access-nri/model-config-tests/.github/workflows/test-repro.yml@main
218+
with:
219+
config-ref: ${{ needs.prepare-command.outputs.config-hash }}
220+
compared-config-ref: ${{ needs.prepare-command.outputs.compared-config-hash }}
221+
environment-name: ${{ needs.prepare-command.outputs.environment-name }}
222+
payu-version: ${{ needs.ci-config.outputs.payu-version }}
223+
model-config-tests-version: ${{ needs.ci-config.outputs.model-config-tests-version }}
224+
test-markers: ${{ needs.ci-config.outputs.markers }}
225+
secrets: inherit
226+
permissions:
227+
contents: write
228+
229+
check-repro:
230+
# Parse the test report and return pass/fail result
231+
name: Results
232+
needs:
233+
- prepare-command
234+
- repro
235+
runs-on: ubuntu-latest
236+
env:
237+
TESTING_LOCAL_LOCATION: /opt/testing
238+
permissions:
239+
pull-requests: write
240+
checks: write
241+
outputs:
242+
result: ${{ steps.results.outputs.result }}
243+
steps:
244+
- name: Download Newly Created Checksum
245+
uses: actions/download-artifact@v4
246+
with:
247+
name: ${{ needs.repro.outputs.artifact-name }}
248+
path: ${{ env.TESTING_LOCAL_LOCATION }}
249+
250+
- name: Parse Test Report
251+
id: tests
252+
uses: EnricoMi/publish-unit-test-result-action/composite@82082dac68ad6a19d980f8ce817e108b9f496c2a #v2.17.1
253+
with:
254+
files: ${{ env.TESTING_LOCAL_LOCATION }}/checksum/test_report.xml
255+
comment_mode: off
256+
check_run: true
257+
compare_to_earlier_commit: false
258+
report_individual_runs: true
259+
report_suite_logs: any
260+
261+
- name: Repro results
262+
id: results
263+
run: |
264+
echo "check-url=${{ fromJson(steps.tests.outputs.json).check_url }}" >> $GITHUB_OUTPUT
265+
266+
if (( ${{ fromJson(steps.tests.outputs.json).stats.tests_fail }} > 0 )); then
267+
echo "result=fail" >> $GITHUB_OUTPUT
268+
else
269+
echo "result=pass" >> $GITHUB_OUTPUT
270+
fi
271+
272+
- name: Comment result
273+
env:
274+
RESULT: |-
275+
${{ steps.results.outputs.result == 'pass' && ':white_check_mark: The Bitwise Reproducibility Check Succeeded :white_check_mark:' || ':x: The Bitwise Reproducibility Check Failed :x:' }}
276+
CONFIG_REF_URL: '[${{ needs.prepare-command.outputs.config-short-hash }}](${{ github.server_url}}/${{ github.repository }}/tree/${{ needs.prepare-command.outputs.config-hash }})'
277+
COMPARED_CONFIG_REF_URL: '[${{ needs.prepare-command.outputs.compared-config-short-hash }}](${{ github.server_url }}/${{ github.repository }}/tree/${{ needs.prepare-command.outputs.compared-config-hash }})'
278+
uses: access-nri/actions/.github/actions/pr-comment@main
279+
with:
280+
comment: |
281+
${{ env.RESULT }}
282+
283+
When comparing:
284+
285+
- `${{ needs.prepare-command.outputs.config-ref }}` (checksums created using commit ${{ env.CONFIG_REF_URL }}), against
286+
- `${{ needs.prepare-command.outputs.compared-config-ref }}` (checksums in commit ${{ env.COMPARED_CONFIG_REF_URL }})
287+
288+
${{ (needs.prepare-command.outputs.commit-requested == 'true' && steps.results.outputs.result == 'fail') && ':wrench: The checksums will be committed to this PR, as they differ.' || '' }}
289+
290+
<details>
291+
<summary> Further information</summary>
292+
293+
The experiment can be found on Gadi at `${{ needs.repro.outputs.experiment-location }}`, and the test results at ${{ steps.results.outputs.check-run-url }}.
294+
295+
The checksums generated by this `!test` command are found in the `testing/checksum` directory of ${{ needs.repro.outputs.artifact-url }}.
296+
297+
The checksums compared against are found here ${{ github.server_url }}/${{ github.repository }}/tree/${{ needs.prepare-command.outputs.compared-config-hash }}/testing/checksum
298+
299+
</details>
300+
301+
commit:
302+
name: Commit Result
303+
if: needs.prepare-command.outputs.commit-requested == 'true' && needs.check-repro.outputs.result == 'fail'
304+
needs:
305+
- prepare-command
306+
- repro
307+
- check-repro
308+
runs-on: ubuntu-latest
309+
permissions:
310+
contents: write
311+
pull-requests: write
312+
env:
313+
ARTIFACT_LOCAL_LOCATION: /opt/artifact
314+
GH_TOKEN: ${{ github.token }}
315+
steps:
316+
- uses: actions/checkout@v4
317+
with:
318+
fetch-depth: 0
319+
token: ${{ secrets.GH_COMMIT_CHECK_TOKEN }}
320+
321+
- name: Checkout Associated PR ${{ github.event.issue.number }}
322+
# Since the trigger for this workflow was on.issue_comment, we need
323+
# to do a bit more wrangling to checkout the pull request
324+
run: gh pr checkout ${{ github.event.issue.number }}
325+
326+
- name: Download Newly Created Checksum
327+
uses: actions/download-artifact@v4
328+
with:
329+
name: ${{ needs.repro.outputs.artifact-name }}
330+
path: ${{ env.ARTIFACT_LOCAL_LOCATION }}
331+
332+
- name: Update files
333+
# This will copy checksums from the artifact to the repo
334+
run: |
335+
mkdir testing
336+
cp --recursive --verbose ${{ env.ARTIFACT_LOCAL_LOCATION }}/*/* testing
337+
338+
- name: Import Commit-Signing Key
339+
uses: crazy-max/ghaction-import-gpg@01dd5d3ca463c7f10f7f4f7b4f177225ac661ee4 # v6.1.0
340+
with:
341+
gpg_private_key: ${{ secrets.GH_ACTIONS_BOT_GPG_PRIVATE_KEY }}
342+
passphrase: ${{ secrets.GH_ACTIONS_BOT_GPG_PASSPHRASE }}
343+
git_config_global: true
344+
git_committer_name: ${{ vars.GH_ACTIONS_BOT_GIT_USER_NAME }}
345+
git_committer_email: ${{ vars.GH_ACTIONS_BOT_GIT_USER_EMAIL }}
346+
git_user_signingkey: true
347+
git_commit_gpgsign: true
348+
git_tag_gpgsign: true
349+
350+
- name: Commit and Push Updates
351+
run: |
352+
git add .
353+
git commit -m "Updated checksums as part of ${{ env.RUN_URL }}"
354+
git push
355+
356+
failure-notifier:
357+
name: Notify PR of Workflow Failure
358+
# We need the last jobs as 'needs' on the failure notifier so
359+
# any of the dependent jobs that fail are covered here
360+
needs:
361+
- permission-check
362+
- prepare-command
363+
- check-repro
364+
- commit
365+
if: failure()
366+
runs-on: ubuntu-latest
367+
steps:
368+
- uses: access-nri/actions/.github/actions/pr-comment@main
369+
with:
370+
comment: >-
371+
:x: `!test` Command Failed :x:
372+
${{ needs.prepare-command.result == 'failure' && format('The command given could not be parsed correctly. Usage: {0}', env.USAGE) || '' }}
373+
${{ needs.permission-check.result == 'failure' && 'You do not have at least write permissions on this repository.' || '' }}
374+
${{ needs.commit.result == 'failure' && 'There was a problem committing the result of the reproducibility run.' || '' }}
375+
See ${{ env.RUN_URL }}

0 commit comments

Comments
 (0)