Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 25 additions & 47 deletions .github/workflows/pr-request-report-labels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,75 +5,53 @@ name: PR - Request report labels
# Don't add any steps that act on external code.
on:
pull_request_target:
types: [ opened, reopened, synchronize, labeled, unlabeled ]
types: [ opened, reopened, synchronize, labeled, unlabeled, edited ]
branches:
- main
- beta
- release

permissions:
contents: none
contents: read
pull-requests: write

jobs:
require-report-label:
permissions:
pull-requests: write

runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Validate report label
id: validate
run: |
set -euo pipefail

labels_json='${{ toJson(github.event.pull_request.labels) }}'

echo "Current labels:"
echo "$labels_json" | jq -r '.[].name'

include_count="$(jq '[.[] | select(.name == "report: include")] | length' <<< "$labels_json")"
exclude_count="$(jq '[.[] | select(.name == "report: exclude")] | length' <<< "$labels_json")"
highlight_count="$(jq '[.[] | select(.name == "report: highlight")] | length' <<< "$labels_json")"

total_count=$((include_count + exclude_count + highlight_count))

if [ "$total_count" -eq 0 ]; then
echo "valid=false" >> "$GITHUB_OUTPUT"
echo "message=Missing report label. Set exactly one of: \`report: include\`, \`report: exclude\` OR \`report: highlight\`." >> "$GITHUB_OUTPUT"
elif [ "$total_count" -gt 1 ]; then
echo "valid=false" >> "$GITHUB_OUTPUT"
echo "message=Only one report label is allowed: \`report: include\`, \`report: exclude\` OR \`report: highlight\`." >> "$GITHUB_OUTPUT"
fi

feature_flag_count="$(jq '[.[] | select(.name == "feature flag")] | length' <<< "$labels_json")"
if [ "$feature_flag_count" -gt 0 ]; then
pr_body='${{ toJson(github.event.pull_request.body) }}'
pr_feature_flag_key="$(jq -nr --arg body "$pr_body" 'try ($body | gsub("\r"; "") | capture("(?m)^feature-flag:\\s*`(?<flag>[^`]+)`$").flag) catch ""')"
if [ -z "$pr_feature_flag_key" ]; then
echo "valid=false" >> "$GITHUB_OUTPUT"
echo "message=PR body must contain the feature flag key in the format: \`feature-flag: \\\`<feature-flag-key>\\\`\`." >> "$GITHUB_OUTPUT"
else
echo "valid=true" >> "$GITHUB_OUTPUT"
fi
else
echo "valid=true" >> "$GITHUB_OUTPUT"
fi

- name: Comment on PR (only on error)
if: steps.validate.outputs.valid == 'false'
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
MESSAGE: ${{ steps.validate.outputs.message }}
run: |
gh pr comment "$PR_NUMBER" \
--repo "${{ github.repository }}" \
--body "$MESSAGE"
./scripts/ci/validate-pr-report-labels.sh "$PR_NUMBER" | while read -r line; do
echo "$line"
if [[ "$line" == "valid="* ]] || [[ "$line" == "message="* ]]; then
echo "$line" >> "$GITHUB_OUTPUT"
fi
done

- name: Fail if invalid
if: steps.validate.outputs.valid == 'false'
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
MESSAGE: ${{ steps.validate.outputs.message }}
IDENTIFIER: "<!-- pr-report-label-validation-comment -->"
run: |
./scripts/ci/manage-pr-comment.sh "$PR_NUMBER" "$IDENTIFIER" "$MESSAGE" "invalid"
echo "::error::$MESSAGE"
exit 1

- name: Mark as resolved if valid
if: steps.validate.outputs.valid == 'true'
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
IDENTIFIER: "<!-- pr-report-label-validation-comment -->"
run: |
./scripts/ci/manage-pr-comment.sh "$PR_NUMBER" "$IDENTIFIER" "" "valid"
44 changes: 44 additions & 0 deletions scripts/ci/manage-pr-comment.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env bash

set -euo pipefail

# This script manages a single PR comment by using a unique identifier.
# Usage:
# ./manage-pr-comment.sh <pr-number> <identifier> <message> <status: invalid|valid>

if [[ $# -ne 4 ]]; then
echo "Usage: $0 <pr-number> <identifier> <message> <status>"
exit 1
fi

PR_NUMBER="$1"
IDENTIFIER="$2"
MESSAGE="$3"
STATUS="$4"

COMMENT_ID=$(gh api "repos/{owner}/{repo}/issues/${PR_NUMBER}/comments" | \
jq -r ".[] | select(.body | contains(\"${IDENTIFIER}\")) | .id" | head -n 1)

if [[ -z "$COMMENT_ID" || "$COMMENT_ID" == "null" ]]; then
COMMENT_ID=""
fi

FULL_MESSAGE="${MESSAGE}${IDENTIFIER}"

if [[ "$STATUS" == "invalid" ]]; then
if [[ -n "$COMMENT_ID" ]]; then
echo "Updating existing comment $COMMENT_ID"
gh api -X PATCH "repos/{owner}/{repo}/issues/comments/${COMMENT_ID}" -f body="$FULL_MESSAGE" > /dev/null
else
echo "Creating new comment"
gh api -X POST "repos/{owner}/{repo}/issues/${PR_NUMBER}/comments" -f body="$FULL_MESSAGE" > /dev/null
fi
elif [[ "$STATUS" == "valid" ]]; then
if [[ -n "$COMMENT_ID" ]]; then
RESOLVED_MESSAGE="✅ **Validation Passed**: All report and feature-flag labels are correctly set.${IDENTIFIER}"
echo "Marking comment $COMMENT_ID as resolved"
gh api -X PATCH "repos/{owner}/{repo}/issues/comments/${COMMENT_ID}" -f body="$RESOLVED_MESSAGE" > /dev/null
else
echo "PR is valid and no comment exists. Nothing to do."
fi
fi
63 changes: 63 additions & 0 deletions scripts/ci/validate-pr-report-labels.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env bash

set -euo pipefail

# This script validates that a pull request has the required report labels.
# It uses the GitHub CLI (gh) to fetch PR information, allowing it to be tested locally.
#
# Usage:
# ./validate-pr-report-labels.sh <pr-number>

if [[ $# -ne 1 ]]; then
echo "Usage: $0 <pr-number>"
exit 1
fi

PR_NUMBER="$1"

pr_data=$(gh pr view "$PR_NUMBER" --json labels,body)

labels_json=$(echo "$pr_data" | jq '.labels')
pr_body=$(echo "$pr_data" | jq -r '.body')

echo "Current labels:"
echo "$labels_json" | jq -r '.[].name'

include_count="$(jq '[.[] | select(.name == "report: include")] | length' <<< "$labels_json")"
exclude_count="$(jq '[.[] | select(.name == "report: exclude")] | length' <<< "$labels_json")"
highlight_count="$(jq '[.[] | select(.name == "report: highlight")] | length' <<< "$labels_json")"

total_count=$((include_count + exclude_count + highlight_count))

if [ "$total_count" -eq 0 ]; then
echo "valid=false"
echo "message=Missing report label. Set exactly one of: \`report: include\`, \`report: exclude\` OR \`report: highlight\`."
exit 0
elif [ "$total_count" -gt 1 ]; then
echo "valid=false"
echo "message=Only one report label is allowed: \`report: include\`, \`report: exclude\` OR \`report: highlight\`."
exit 0
fi

feature_flag_count="$(jq '[.[] | select(.name == "feature-flag")] | length' <<< "$labels_json")"
if [ "$feature_flag_count" -gt 0 ]; then
pr_feature_flag_key="$(jq -nr --arg body "$pr_body" '
try (
$body
| split("\n")
| .[]
| sub("^\\s+"; "")
| select(test("^feature-flag:\\s*`[^`]+`"; "i"))
| capture("`(?<flag>[^`]+)`")
| .flag
) catch ""
' | head -n 1)"

if [ -z "$pr_feature_flag_key" ]; then
echo "valid=false"
echo "message=PR body must contain the feature flag key in the format: 'feature-flag: \`<feature-flag-key>\`'."
exit 0
fi
fi

echo "valid=true"
Loading