Skip to content

Commit ae7a2b8

Browse files
authored
ci: fixup title and body validator actions for merge/workflow (delta-io#2697)
## What changes are proposed in this pull request? These actions were broken for runs that were not `pull_request` triggered, either because the logic to try and pull the info was wrong, or just missing. This PR adds code to check why the jobs where triggered and to fetch the title/body in the appropriate way given the trigger. There are code comments that should explain the changes. Also this renames pr-validator.yml to pr-title-validator.yml as that's really what it is. ## How was this change tested? I've ensured the PR based ones still work as expected. We have to merge to test the other ones, but those are already broken so at least this won't make things _worse_ :) I've also run the scripts locally with crafted environment variables to try and ensure as much as possible that things kinda work.
1 parent 3ca5f50 commit ae7a2b8

4 files changed

Lines changed: 147 additions & 55 deletions

File tree

.github/workflows/pr-body-validator.yml

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,34 @@ jobs:
1212
- name: Validate PR Body
1313
shell: bash
1414
env:
15+
GH_TOKEN: ${{ github.token }}
16+
REPO: ${{ github.repository }}
17+
EVENT_NAME: ${{ github.event_name }}
18+
PR_NUMBER: ${{ github.event.pull_request.number }}
1519
PR_BODY: ${{ github.event.pull_request.body }}
20+
MERGE_GROUP_REF: ${{ github.event.merge_group.head_ref }}
1621
run: |
22+
set -euo pipefail
23+
24+
# merge_group runs don't have github.event.pull_request, so fetch the body from the PR
25+
# referenced by the merge queue ref (refs/heads/gh-readonly-queue/<base>/pr-<NUMBER>-<sha>).
26+
if [[ "$EVENT_NAME" == "merge_group" ]]; then
27+
PR_NUMBER=$(sed -nE 's/.*\/pr-([0-9]+)-.*/\1/p' <<< "$MERGE_GROUP_REF")
28+
if [[ -z "$PR_NUMBER" ]]; then
29+
echo "::error::could not parse a PR number from merge_group ref '$MERGE_GROUP_REF'"
30+
exit 1
31+
fi
32+
PR_BODY=$(gh pr view "$PR_NUMBER" --repo "$REPO" --json body --jq '.body // ""')
33+
fi
34+
35+
echo "Validating body of PR #${PR_NUMBER} (event: ${EVENT_NAME})"
36+
echo "::group::PR body being validated"
37+
printf '%s\n' "$PR_BODY"
38+
echo "::endgroup::"
39+
1740
if LC_ALL=C grep -q '[^[:print:][:space:]]' <<< "$PR_BODY"; then
1841
echo "PR body contains non-ascii characters. Please remove them."
1942
exit 1
2043
else
2144
echo "PR body contains ascii characters only"
2245
fi
23-
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
name: Validate PR Title
2+
3+
on:
4+
pull_request:
5+
types: [opened, edited, reopened, synchronize, labeled, unlabeled]
6+
merge_group:
7+
workflow_run:
8+
workflows: ["semver-label"] # we need this since auto-labels from jobs don't trigger a workflow
9+
types: [completed]
10+
11+
jobs:
12+
validate-title:
13+
runs-on: ubuntu-latest
14+
permissions:
15+
contents: read
16+
actions: read # cross-workflow artifact download (workflow_run path)
17+
steps:
18+
# for workflow triggered there's no tie back because we're triggered via another workflow
19+
# (semvar-label) so we have to resort to having had semvar-label upload an artifact for us to
20+
# pull
21+
- name: Download PR number artifact
22+
if: github.event_name == 'workflow_run'
23+
continue-on-error: true
24+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
25+
with:
26+
name: pr-number
27+
github-token: ${{ github.token }}
28+
run-id: ${{ github.event.workflow_run.id }}
29+
30+
- name: Resolve PR metadata
31+
id: pr
32+
env:
33+
GH_TOKEN: ${{ github.token }}
34+
EVENT_NAME: ${{ github.event_name }}
35+
REPO: ${{ github.repository }}
36+
# Captured as env vars to prevent expression injection into the shell command.
37+
PR_TITLE: ${{ github.event.pull_request.title }}
38+
PR_NUMBER: ${{ github.event.pull_request.number }}
39+
PR_LABELS_JSON: ${{ toJson(github.event.pull_request.labels.*.name) }}
40+
MERGE_GROUP_REF: ${{ github.event.merge_group.head_ref }}
41+
# We have to deal with three different triggers, and get the title + labels in a different
42+
# way for each:
43+
# pr: just use the $ {{ github.* }} stuff
44+
# merge_group: we have a head_ref which includes the pr number, so sed it out of that and
45+
# then fetch the title and labels via gh
46+
# workflow: see comment for "Download PR number artifact" comment
47+
run: |
48+
set -euo pipefail
49+
50+
emit_title() {
51+
# Multiline delimiter syntax so a title containing newlines cannot inject
52+
# additional key=value pairs into GITHUB_OUTPUT.
53+
{
54+
echo 'title<<PR_TITLE_EOF'
55+
echo "$1"
56+
echo 'PR_TITLE_EOF'
57+
} >> "$GITHUB_OUTPUT"
58+
}
59+
60+
# Fetch the current title + labels for a known PR number via gh
61+
resolve_from_pr() {
62+
local json
63+
json=$(gh pr view "$1" --repo "$REPO" --json number,title,labels)
64+
echo "number=$1" >> "$GITHUB_OUTPUT"
65+
emit_title "$(jq -r '.title' <<< "$json")"
66+
echo "labels=$(jq -c '[.labels[].name]' <<< "$json")" >> "$GITHUB_OUTPUT"
67+
}
68+
69+
case "$EVENT_NAME" in
70+
pull_request)
71+
echo "number=$PR_NUMBER" >> "$GITHUB_OUTPUT"
72+
emit_title "$PR_TITLE"
73+
echo "labels=$(jq -c '.' <<< "$PR_LABELS_JSON")" >> "$GITHUB_OUTPUT"
74+
;;
75+
merge_group)
76+
# head_ref looks like refs/heads/gh-readonly-queue/<base>/pr-<NUMBER>-<sha>.
77+
num=$(sed -nE 's/.*\/pr-([0-9]+)-.*/\1/p' <<< "$MERGE_GROUP_REF")
78+
if [[ -z "$num" ]]; then
79+
echo "::error::could not parse a PR number from merge_group ref '$MERGE_GROUP_REF'"
80+
exit 1
81+
fi
82+
resolve_from_pr "$num"
83+
;;
84+
workflow_run)
85+
if [[ ! -f pr-number.txt ]]; then
86+
echo "No pr-number artifact (semver-label was skipped); nothing to validate."
87+
echo "skip=true" >> "$GITHUB_OUTPUT"
88+
exit 0
89+
fi
90+
resolve_from_pr "$(cat pr-number.txt)"
91+
;;
92+
*)
93+
echo "::error::unsupported event '$EVENT_NAME'"
94+
exit 1
95+
;;
96+
esac
97+
98+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
99+
if: steps.pr.outputs.skip != 'true'
100+
101+
- uses: ./.github/actions/pr-title-validator
102+
if: steps.pr.outputs.skip != 'true'
103+
with:
104+
regex: '^(feat|fix|chore|docs|perf|refactor|test|ci)!?(\(.+\))?: .{1,72}$'
105+
breaking-change-regex: '^(feat|fix|chore|docs|perf|refactor|test|ci)!(\(.+\))?: .{1,72}$'
106+
labels: ${{ steps.pr.outputs.labels }}
107+
title: ${{ steps.pr.outputs.title }}

.github/workflows/pr-validator.yml

Lines changed: 0 additions & 54 deletions
This file was deleted.

.github/workflows/semver-label.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,20 @@ jobs:
7575
echo "ERROR: unexpected semver outcome '${STEP_OUTCOME}' in semver-outcome.txt"
7676
exit 1
7777
fi
78+
79+
# Create an artifact for pr-title-validator, which is triggered by this workflow via
80+
# workflow_run. Due to how it's triggered, that run will have no idea what PR it's running
81+
# against, so we output it here so it can do its job
82+
- name: Record PR number
83+
if: steps.pr-context.outputs.number != ''
84+
env:
85+
PR_NUMBER: ${{ steps.pr-context.outputs.number }}
86+
run: echo "$PR_NUMBER" > pr-number.txt
87+
88+
- name: Upload PR number for title validator
89+
if: steps.pr-context.outputs.number != ''
90+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
91+
with:
92+
name: pr-number
93+
path: pr-number.txt
94+
if-no-files-found: error

0 commit comments

Comments
 (0)