label-sync.yml

 1name: "Label Sync"
 2
 3on:
 4  push:
 5    branches: [master]
 6    paths:
 7      - .github/labels.yml
 8      - .github/workflows/label-sync.yml
 9  workflow_dispatch:
10    inputs:
11      prune:
12        description: "Delete labels not in labels.yml"
13        type: boolean
14        default: false
15
16permissions:
17  contents: read
18  issues: write
19  pull-requests: write
20
21jobs:
22  sync:
23    runs-on: ubuntu-latest
24    env:
25      GH_TOKEN: ${{ secrets.HOMEBREW_GITHUB_TOKEN }}
26      LABELS_FILE: .github/labels.yml
27      PRUNE: ${{ github.event.inputs.prune || 'false' }}
28    steps:
29      - uses: actions/checkout@v7
30
31      - name: Upsert labels
32        run: |
33          set -euo pipefail
34          count=$(yq '. | length' "$LABELS_FILE")
35          for i in $(seq 0 $((count - 1))); do
36            name=$(yq ".[$i].name" "$LABELS_FILE")
37            color=$(yq ".[$i].color" "$LABELS_FILE")
38            description=$(yq ".[$i].description // \"\"" "$LABELS_FILE")
39            echo "::group::$name"
40            gh label create "$name" \
41              --repo "$GITHUB_REPOSITORY" \
42              --color "$color" \
43              --description "$description" \
44              --force
45            echo "::endgroup::"
46          done
47
48      - name: Prune extra labels
49        if: env.PRUNE == 'true'
50        run: |
51          set -euo pipefail
52          desired=$(yq -r '.[].name' "$LABELS_FILE" | sort -u)
53          current=$(gh label list --repo "$GITHUB_REPOSITORY" --limit 500 --json name -q '.[].name' | sort -u)
54          extras=$(comm -23 <(echo "$current") <(echo "$desired"))
55          if [ -z "$extras" ]; then
56            echo "No extra labels."
57            exit 0
58          fi
59          echo "Deleting:"
60          echo "$extras"
61          while IFS= read -r name; do
62            [ -z "$name" ] && continue
63            gh label delete "$name" --repo "$GITHUB_REPOSITORY" --yes
64          done <<< "$extras"