Skip to content
Open
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
5 changes: 3 additions & 2 deletions .github/workflows/check-change-note.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
name: Check change note

permissions:
contents: read
pull-requests: read

on:
pull_request_target:
pull_request:
types: [labeled, unlabeled, opened, synchronize, reopened, ready_for_review]
paths:
- "*/ql/src/**/*.ql"
Expand All @@ -23,7 +24,7 @@ jobs:
env:
REPO: ${{ github.repository }}
PULL_REQUEST_NUMBER: ${{ github.event.number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ github.token }}
runs-on: ubuntu-latest
steps:

Expand Down
143 changes: 136 additions & 7 deletions .github/workflows/labeler.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,144 @@
name: "Pull Request Labeler"

on:
- pull_request_target
schedule:
# Reconcile recently updated PRs promptly, including unapproved forks and
# conflicted PRs for which pull_request workflows do not run.
- cron: "7,22,37,52 * * * *"
# Reconcile one stable shard of all open PRs each hour to recover from
# delayed or missed scheduled runs.
- cron: "12 * * * *"
workflow_dispatch:
inputs:
pr_number:
description: "Open pull request number to reconcile"
required: true
type: string

permissions: {}

permissions:
contents: read
pull-requests: write
concurrency:
group: pull-request-labeler
cancel-in-progress: false

jobs:
triage:
if: github.ref_name == github.event.repository.default_branch
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/labeler@v4
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
- uses: actions/checkout@v5
with:
persist-credentials: false
sparse-checkout: .github/labeler.yml
sparse-checkout-cone-mode: false

- name: Collect pull requests to reconcile
id: collect
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
EVENT_NAME: ${{ github.event_name }}
SCHEDULE: ${{ github.event.schedule }}
REQUESTED_PR: ${{ inputs.pr_number }}
run: |
set -euo pipefail

if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
if [[ ! "$REQUESTED_PR" =~ ^[1-9][0-9]*$ ]]; then
echo "Invalid pull request number: $REQUESTED_PR"
exit 1
fi

pr_json=$(gh api "repos/$REPO/pulls/$REQUESTED_PR")
candidates=$(jq -c '[{
number: .number,
head_sha: .head.sha
}]' <<<"$pr_json")
else
pulls_json=$(gh api --paginate \
"repos/$REPO/pulls?state=open&sort=updated&direction=desc&per_page=100" |
jq -cs 'add')

if [ "$SCHEDULE" = "12 * * * *" ]; then
shard=$(( ($(date -u +%s) / 3600) % 6 ))
candidates=$(jq -c --argjson shard "$shard" \
'[.[] | select((.number % 6) == $shard) | {
number: .number,
head_sha: .head.sha
}]' <<<"$pulls_json")
else
cutoff=$(date -u -d "1 hour ago" "+%Y-%m-%dT%H:%M:%SZ")
# Hourly shards reconcile any candidates beyond this API budget.
candidates=$(jq -c --arg cutoff "$cutoff" \
'[.[] | select(.updated_at >= $cutoff) | {
number: .number,
head_sha: .head.sha
}][0:100]' <<<"$pulls_json")
fi
fi

echo "Collected $(jq 'length' <<<"$candidates") pull request(s)."
{
echo "candidates<<EOF"
echo "$candidates"
echo "EOF"
} >> "$GITHUB_OUTPUT"

- name: Validate pull request state
id: validate
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
CANDIDATES: ${{ steps.collect.outputs.candidates }}
run: |
set -euo pipefail

valid_numbers=()
while IFS=$'\t' read -r pr_number expected_sha; do
if [[ ! "$pr_number" =~ ^[1-9][0-9]*$ ]] ||
[[ ! "$expected_sha" =~ ^[0-9a-f]{40}$ ]]; then
echo "Skipping malformed pull request candidate."
continue
fi

if ! pr_json=$(gh api "repos/$REPO/pulls/$pr_number"); then
echo "Pull request #$pr_number could not be fetched; skipping."
continue
fi

if ! jq -e \
--arg repo "$REPO" \
--arg sha "$expected_sha" \
'.state == "open" and
.base.repo.full_name == $repo and
.head.sha == $sha and
(.head.repo.full_name | type == "string")' \
>/dev/null <<<"$pr_json"; then
echo "Pull request #$pr_number changed or is no longer open; skipping."
continue
fi

valid_numbers+=("$pr_number")
done < <(jq -r '.[] | [.number, .head_sha] | @tsv' <<<"$CANDIDATES")

if [ "${#valid_numbers[@]}" -eq 0 ]; then
echo "has_prs=false" >> "$GITHUB_OUTPUT"
exit 0
fi

{
echo "has_prs=true"
echo "pr_numbers<<EOF"
printf '%s\n' "${valid_numbers[@]}"
echo "EOF"
} >> "$GITHUB_OUTPUT"

- uses: actions/labeler@v4
if: steps.validate.outputs.has_prs == 'true'
with:
repo-token: "${{ github.token }}"
pr-number: ${{ steps.validate.outputs.pr_numbers }}
Loading