-
Notifications
You must be signed in to change notification settings - Fork 0
feat: automate weekly label sync to customer repos #116
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
4088430
e990500
1d28a73
91f767c
36b3f20
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| name: Sync labels to customer repos | ||
|
|
||
| # Weekly, unattended run of the label sync. Copies labels matching the | ||
| # include rules in config.yaml from the leader repo (giantswarm/giantswarm) | ||
| # to giantswarm/roadmap and every repo listed in | ||
| # giantswarm/giantswarm/data/customers.yaml. Create/update only, never deletes. | ||
| # | ||
| # Hand-authored workflow (not a devctl zz_generated.* file). Do not rename to | ||
| # the zz_generated prefix, or it may be overwritten by repository automation. | ||
|
|
||
| on: | ||
| schedule: | ||
| - cron: "0 6 * * 1" # Mondays 06:00 UTC | ||
| workflow_dispatch: | ||
| inputs: | ||
| dry_run: | ||
| description: "Print the sync plan without applying any changes" | ||
| type: boolean | ||
| default: false | ||
|
|
||
| # The workflow's own GITHUB_TOKEN needs nothing; cross-repo writes use the | ||
| # App installation token minted below. | ||
| permissions: | ||
| contents: read | ||
|
|
||
| # Never let a manual dispatch race the scheduled run. | ||
| concurrency: | ||
| group: label-sync | ||
| cancel-in-progress: false | ||
|
|
||
| jobs: | ||
| sync: | ||
| name: Sync labels | ||
| runs-on: ubuntu-24.04 | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | ||
|
|
||
| - name: Setup Python | ||
| uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | ||
| with: | ||
| python-version: "3.12" | ||
|
|
||
| - name: Install dependencies | ||
| run: pip install -r requirements.txt | ||
|
|
||
| - name: Mint App installation token | ||
| id: app-token | ||
| uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 | ||
| with: | ||
| # A dedicated giantswarm-label-sync App with: | ||
| # - contents: read on giantswarm/giantswarm (leader labels + customers.yaml) | ||
| # - issues: write on roadmap + all customer repos (labels live under Issues) | ||
| client-id: ${{ vars.LABEL_SYNC_APP_CLIENT_ID }} | ||
| private-key: ${{ secrets.LABEL_SYNC_APP_PRIVATE_KEY }} | ||
| owner: giantswarm | ||
| # Empty list = all repositories the App is installed on. | ||
| repositories: "" | ||
|
|
||
| - name: Synchronize labels | ||
| env: | ||
| # cli.py reads the token from this env var, so nothing touches disk. | ||
| GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} | ||
| run: | | ||
| if [ "${{ inputs.dry_run }}" = "true" ]; then | ||
| python cli.py --dry-run | ||
| else | ||
| python cli.py --yes | ||
| fi | ||
|
|
||
| # A silently failing weekly cron is how label drift creeps back in, so make a | ||
| # broken scheduled run visible. Manual dispatches are watched by a human already. | ||
| - name: Report failed scheduled run to Slack | ||
| if: failure() && github.event_name == 'schedule' | ||
| env: | ||
| SLACK_WEBHOOK_URL: ${{ secrets.TEAM_PLANETEERS_SLACK_WEBHOOK_URL }} | ||
| RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | ||
| run: | | ||
| set -euo pipefail | ||
| jq -n --arg text ":warning: Weekly label sync to customer repos failed. Run log: $RUN_URL" \ | ||
| '{text: $text}' \ | ||
| | curl -sS --fail-with-body -X POST -H 'Content-type: application/json' \ | ||
| --data @- "$SLACK_WEBHOOK_URL" | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Line 112 — the per-label error handler raises instead of catching (pre-existing, outside this diff, so no inline anchor) except github.GithubException.GithubException as e:
Effect once this runs unattended: a single API error on Related, same block: the Generated by Claude Code
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in 36b3f20. The handler is One deliberate addition beyond the literal fix: the loop counts failures and, after the plan has run through, calls
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Line 103 — execute phase hardcodes the org for customer repos (pre-existing, outside this diff; latent, not a live bug — confirmed all customer repos are in the Read phase honours the per-entry org, execute phase does not: # read
target_labels[cr['repository']], _ = read_repo_labels(g, cr['organization'], cr['repository'], config['rules'])
# execute
repo_handlers[repo] = repo = g.get_repo(f"{config['github']['organization']}/{repo}")Since every Generated by Claude Code
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed, keeping this out of the PR. Tracked in #119 with your proposed fix: key |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
curlwithout--failexits 0 on 403/404/invalid_token, so a rotated or mistyped webhook makes this step pass green while the alert goes nowhere — the exact silent-failure mode the step exists to prevent.set -euo pipefaildoesn't cover it, since curl itself succeeded.-w 'HTTP %{http_code}'puts the status in the log, but nobody reads the log of a green run.Generated by Claude Code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Applied as suggested in 91f767c:
--fail-with-bodyreplaces-o /dev/null -w 'HTTP %{http_code}', so a rejected webhook call now fails the step and Slack's response body lands in the log instead of a green step withHTTP 403.