feat(add-to-project): add an org-wide backfill scope - #124
Merged
Conversation
Boarding an issue costs a billable minute in whichever repo fires the event. The private repos fire ~130 of those a week -- ~516 minutes a month -- for a job that averages 7 seconds and touches no repository content at all. Actions minutes are billed to the caller, never to the repo holding the reusable workflow, so the existing 'uses:' shims into this repo save nothing. The only way this work stops costing minutes is for the run itself to be triggered in a public repo, where standard runners are free. A new 'scope: org' input makes that possible: one sweep, called from the public .github repo, boards every open issue and pull request across the organization. scope: repo remains the default and the existing behaviour is untouched. The token now also requests metadata: read, needed to enumerate the organization's repositories. collect_repo writes to a file rather than returning urls on stdout. Calling it inside a command substitution would run it in a subshell, losing the unreachable counter and capturing the ::warning:: lines into the url list instead of emitting them. A repo the App cannot see warns and is skipped rather than failing an org-wide sweep -- that is a fact about installation scope, not about the board. For a single named repo it stays a hard error, because the caller asked for exactly that one. Verified by running the collection logic against the live org: 30 repos, 159 open issues and pull requests, 0 unreadable. Refs #122 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AJYmuNrmiYv9wGRABoQYUi
ExtraToast
added a commit
to JorisJonkers-dev/.github
that referenced
this pull request
Aug 29, 2026
#36) Actions minutes are billed to the caller, never to the repo holding the reusable workflow. The per-repo 'uses:' shims already point at github-workflows and that saves nothing -- a private repo firing the event pays for the run. Standard runners are free in public repositories, and this repo is public, so every run of this sweep costs nothing. That is also why the cron can be this frequent: there is no minute budget to ration, only GitHub's scheduling latency. Boarding across the private repos currently costs ~516 billable minutes a month for a job that averages 7 seconds and reads no repository content -- it is pure Projects-V2 GraphQL and never needed to run where the content is. Second reason this exists: when GitHub stops creating runs from webhook events -- which has happened in this estate, stranding 24 issues at once -- the event-driven path silently stops boarding anything. A sweep keeps working, because it is not waiting on a webhook. Depends on the scope: org input added in JorisJonkers-dev/github-workflows#124. Refs #33 Claude-Session: https://claude.ai/code/session_01AJYmuNrmiYv9wGRABoQYUi Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a
scope: orginput to the reusableadd-to-project.yml. When set, it boards every open issue and pull request across the whole organization instead of just the calling repo.Why
Boarding an issue costs a billable minute in whichever repo fires the event. The private repos fire ~130 of those a week — ~516 minutes/month — for a job that averages 7 seconds and touches no repository content at all. It is pure Projects-V2 GraphQL.
The key fact: Actions minutes are billed to the caller, never to the repo holding the reusable workflow. The existing
uses:shims into this repo already centralise the code, and that saves exactly nothing — a private repo firing the event pays for the run.The only way this work stops costing minutes is for the run itself to be triggered in a public repo, where standard runners are free. That is what
scope: orgenables, driven by the scheduled caller in JorisJonkers-dev/.github (companion PR).Behaviour
scope: repois the default and existing behaviour is untouched — event-driven adds, manual single-URL adds, and the per-repo backfill all work exactly as before.Two details worth reviewing
collect_repowrites to a file rather than returning URLs on stdout. Calling it inside a command substitution would run it in a subshell — losing theunreachablecounter, and capturing the::warning::lines into the URL list instead of emitting them. I wrote it the naive way first and caught this before pushing.Unreadable repos warn but don't fail an org-wide sweep. A repo the App was never granted is a fact about installation scope, not about the board. For a single named repo it stays a hard error, because the caller asked for exactly that one.
The token now also requests
metadata: read, needed to enumerate the org's repositories.Verification
I ran the collection logic against the live org:
actionlintandshellcheck -S warningboth clean.The add itself is unchanged and already idempotent —
addProjectV2ItemByIdreturns the existing item on re-add — which is what makes a 15-minute sweep safe.Closes #122