Skip to content

fix: validate snapshot hint and CRC invariants #9447

fix: validate snapshot hint and CRC invariants

fix: validate snapshot hint and CRC invariants #9447

name: Validate PR Title
on:
pull_request:
types: [opened, edited, reopened, synchronize, labeled, unlabeled]
merge_group:
workflow_run:
workflows: ["semver-label"] # we need this since auto-labels from jobs don't trigger a workflow
types: [completed]
jobs:
validate-title:
runs-on: ubuntu-latest
permissions:
contents: read
actions: read # cross-workflow artifact download (workflow_run path)
steps:
# for workflow triggered there's no tie back because we're triggered via another workflow
# (semvar-label) so we have to resort to having had semvar-label upload an artifact for us to
# pull
- name: Download PR number artifact
if: github.event_name == 'workflow_run'
continue-on-error: true
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: pr-number
github-token: ${{ github.token }}
run-id: ${{ github.event.workflow_run.id }}
- name: Resolve PR metadata
id: pr
env:
GH_TOKEN: ${{ github.token }}
EVENT_NAME: ${{ github.event_name }}
REPO: ${{ github.repository }}
# Captured as env vars to prevent expression injection into the shell command.
PR_TITLE: ${{ github.event.pull_request.title }}
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_LABELS_JSON: ${{ toJson(github.event.pull_request.labels.*.name) }}
MERGE_GROUP_REF: ${{ github.event.merge_group.head_ref }}
# We have to deal with three different triggers, and get the title + labels in a different
# way for each:
# pr: just use the $ {{ github.* }} stuff
# merge_group: we have a head_ref which includes the pr number, so sed it out of that and
# then fetch the title and labels via gh
# workflow: see comment for "Download PR number artifact" comment
run: |
set -euo pipefail
emit_title() {
# Multiline delimiter syntax so a title containing newlines cannot inject
# additional key=value pairs into GITHUB_OUTPUT.
{
echo 'title<<PR_TITLE_EOF'
echo "$1"
echo 'PR_TITLE_EOF'
} >> "$GITHUB_OUTPUT"
}
# Fetch the current title + labels for a known PR number via gh
resolve_from_pr() {
local json
json=$(gh pr view "$1" --repo "$REPO" --json number,title,labels)
echo "number=$1" >> "$GITHUB_OUTPUT"
emit_title "$(jq -r '.title' <<< "$json")"
echo "labels=$(jq -c '[.labels[].name]' <<< "$json")" >> "$GITHUB_OUTPUT"
}
case "$EVENT_NAME" in
pull_request)
echo "number=$PR_NUMBER" >> "$GITHUB_OUTPUT"
emit_title "$PR_TITLE"
echo "labels=$(jq -c '.' <<< "$PR_LABELS_JSON")" >> "$GITHUB_OUTPUT"
;;
merge_group)
# head_ref looks like refs/heads/gh-readonly-queue/<base>/pr-<NUMBER>-<sha>.
num=$(sed -nE 's/.*\/pr-([0-9]+)-.*/\1/p' <<< "$MERGE_GROUP_REF")
if [[ -z "$num" ]]; then
echo "::error::could not parse a PR number from merge_group ref '$MERGE_GROUP_REF'"
exit 1
fi
resolve_from_pr "$num"
;;
workflow_run)
if [[ ! -f pr-number.txt ]]; then
echo "No pr-number artifact (semver-label was skipped); nothing to validate."
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
resolve_from_pr "$(cat pr-number.txt)"
;;
*)
echo "::error::unsupported event '$EVENT_NAME'"
exit 1
;;
esac
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
if: steps.pr.outputs.skip != 'true'
- uses: ./.github/actions/pr-title-validator
if: steps.pr.outputs.skip != 'true'
with:
regex: '^(feat|fix|chore|docs|perf|refactor|test|ci)!?(\(.+\))?: .{1,72}$'
breaking-change-regex: '^(feat|fix|chore|docs|perf|refactor|test|ci)!(\(.+\))?: .{1,72}$'
labels: ${{ steps.pr.outputs.labels }}
title: ${{ steps.pr.outputs.title }}