Skip to content

Automate PR label classification and update documentation #6

Automate PR label classification and update documentation

Automate PR label classification and update documentation #6

Workflow file for this run

name: PR Labels
on:
workflow_dispatch:
inputs:
pr_numbers:
description: One or more PR numbers to label. Separate multiple PRs with spaces, commas, or new lines.
required: true
type: string
pull_request_target:
types:
- opened
- reopened
- labeled
- unlabeled
- synchronize
- edited
concurrency:
group: pr-labels-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event.pull_request.number }}
cancel-in-progress: true
jobs:
labeler:
name: apply labels
if: github.repository == 'wallentx/jobscout'
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
issues: write
pull-requests: write
steps:
- name: Normalize manual PR numbers
id: pr-numbers
if: github.event_name == 'workflow_dispatch'
env:
INPUT_PR_NUMBERS: ${{ inputs.pr_numbers }}
run: |
set -eu
pr_numbers=$(
printf '%s\n' "$INPUT_PR_NUMBERS" |
tr ',;' '\n' |
tr ' ' '\n' |
awk '
NF {
if ($0 !~ /^[0-9]+$/) {
printf "Invalid PR number: %s\n", $0 > "/dev/stderr"
exit 1
}
print
}
'
)
if [ -z "$pr_numbers" ]; then
echo "No PR numbers provided" >&2
exit 1
fi
{
echo "value<<EOF"
printf '%s\n' "$pr_numbers"
echo "EOF"
} >> "$GITHUB_OUTPUT"
- name: Label pull request
uses: actions/labeler@v6
with:
configuration-path: .github/labeler.yml
pr-number: ${{ github.event_name == 'workflow_dispatch' && steps.pr-numbers.outputs.value || github.event.pull_request.number }}
classify-release-label:
name: classify release label
needs: labeler
if: github.repository == 'wallentx/jobscout' && github.event_name == 'pull_request_target'
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
pull-requests: write
issues: write
steps:
- name: Check out repository
uses: actions/checkout@v6
- name: Actions Toolbox
uses: wallentx/gh-actions/composite/actions-toolbox@main
env:
GH_TOKEN: ${{ github.token }}
with:
checkout: false
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 22
- name: Install Copilot CLI
run: npm install -g @github/copilot
- name: Classify PR for release label
id: classify
env:
GH_TOKEN: ${{ github.token }}
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
run: |
set -euo pipefail
: "${GH_PR:?Actions Toolbox did not set GH_PR}"
: "${GH_PR_TITLE:?Actions Toolbox did not set GH_PR_TITLE}"
: "${GH_PR_URL:?Actions Toolbox did not set GH_PR_URL}"
: "${COPILOT_GITHUB_TOKEN:?Missing repository secret COPILOT_GITHUB_TOKEN}"
pr_body="$(printf '%s\n' "${GH_PR_BODY_JSON:-\"\"}" | jq -r '.')"
pr_files="$(gh pr view "$GH_PR" --repo "$GITHUB_REPOSITORY" --json files --jq '.files[].path')"
raw_label="$(
{
echo "Classify this pull request and return only the release-note label."
printf '\nPR CONTEXT:\n'
echo "PR TITLE:"
printf '%s\n' "$GH_PR_TITLE" | sed -n '1,80p'
printf '\nPR BODY:\n'
printf '%s\n' "$pr_body" | sed -n '1,240p'
printf '\nCHANGED FILES:\n'
printf '%s\n' "$pr_files" | sed -n '1,300p'
printf '\nDIFF:\n'
curl -fsSL "${GH_PR_URL}.diff" | sed -n '1,1500p'
} | copilot \
-s \
--no-color \
--no-banner \
--no-auto-update \
--no-ask-user \
--deny-tool=shell \
--deny-tool=write \
--deny-tool=read \
--deny-tool=url \
--deny-tool=memory \
| tr -d '\r' \
| awk 'NF { print; exit }'
)"
label="$(
printf '%s' "$raw_label" \
| sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//' \
| tr '[:upper:]' '[:lower:]'
)"
case "$label" in
added|changed|fixed|dependencies)
;;
*)
echo "Copilot returned invalid label: ${raw_label:-<empty>}" >&2
exit 1
;;
esac
echo "label=$label" >> "$GITHUB_OUTPUT"
- name: Replace release-note labels
env:
GH_TOKEN: ${{ github.token }}
NEW_LABEL: ${{ steps.classify.outputs.label }}
run: |
set -euo pipefail
: "${GH_PR:?Actions Toolbox did not set GH_PR}"
existing="$(
gh pr view "$GH_PR" --repo "$GITHUB_REPOSITORY" --json labels \
--jq '.labels[].name'
)"
release_labels="added changed fixed dependencies"
has_new_label=false
has_other_release_label=false
for label in $release_labels; do
if printf '%s\n' "$existing" | grep -Fxq "$label"; then
if [ "$label" = "$NEW_LABEL" ]; then
has_new_label=true
else
has_other_release_label=true
fi
fi
done
if [ "$has_new_label" = true ] && [ "$has_other_release_label" = false ]; then
echo "Release label already set to $NEW_LABEL"
exit 0
fi
for label in $release_labels; do
if [ "$label" != "$NEW_LABEL" ] && printf '%s\n' "$existing" | grep -Fxq "$label"; then
gh pr edit "$GH_PR" --repo "$GITHUB_REPOSITORY" --remove-label "$label"
fi
done
if [ "$has_new_label" = false ]; then
gh pr edit "$GH_PR" --repo "$GITHUB_REPOSITORY" --add-label "$NEW_LABEL"
fi
echo "Applied label: $NEW_LABEL"