Skip to content

Org backfill + discovery scripts (seed and fill the org leaderboard)#104

Merged
peetzweg merged 7 commits into
mainfrom
feature-backfill-orgs
Jul 10, 2026
Merged

Org backfill + discovery scripts (seed and fill the org leaderboard)#104
peetzweg merged 7 commits into
mainfrom
feature-backfill-orgs

Conversation

@peetzweg

@peetzweg peetzweg commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Two companion operational scripts for populating the organization leaderboard, side by side so they can be tested and merged together. Neither is wired into the app — both are manual, politely rate-limited bun scripts. No schema change.

scripts/discover-orgs.ts — find orgs to track

GitHub has no endpoint that ranks orgs by public-member count, so this searches type:org by followers (best prominence proxy), enriches each candidate with fetchOrgProfile for its real member count, sorts/filters by that, and records the survivors as entities rows (kind='org', builtAt null). Flags: --limit (max 1000), --min-members, --query, --dry-run. Skips already-tracked orgs.

scripts/backfill-orgs.ts — fill the ones we track

Fills recorded-but-unbuilt orgs (the request path refuses to build >25-member orgs live) with each public member's lifetime org-scoped totals, rolled onto the entities row. Orders smallest-first so small orgs resolve fast and mega-orgs fall to the back; true cross-run resume (skips members already fetched); --force to re-refresh.

Workflow

env -u GITHUB_TOKEN bun scripts/discover-orgs.ts --min-members 25 --limit 300
env -u GITHUB_TOKEN bun scripts/backfill-orgs.ts

Both need the read:org token from .env (hence env -u GITHUB_TOKEN), pace under GitHub's limits with a 500-request floor for live traffic, and are idempotent/interruptible. tsc + tests green.

(Folds in the discovery script from #117.)

peetzweg added 7 commits July 8, 2026 22:29
The request path won't build orgs over 25 members on demand (it records them
and moves on). This standalone script fills those recorded-but-empty orgs — and
any org named explicitly (google, microsoft, github) — with the same lifetime
per-member totals a small org's page produces, writing only the existing
org_members + entities tables. No migration, no monthly resolution.

Politely paced under GitHub's hourly limit with a reserved floor for live
traffic; idempotent, resumable, interruptible.

  bun scripts/backfill-orgs.ts                    # every recorded org not yet filled
  bun scripts/backfill-orgs.ts google microsoft   # specific orgs (records them if new)
- No-arg mode now orders by memberCount ascending (was lastFetched), so small
  orgs resolve fast and mega-orgs like microsoft fall to the back instead of
  blocking every other org behind one 1,000-member fill.
- Fix resume: a member is skipped when its lastFetched is set (from this run OR
  a previous aborted one), not just when fetched since this run started. So
  aborting mid-org and re-running now continues from where it stopped rather
  than re-fetching the whole org. Add --force to re-fetch everyone (refresh).
LOGIN_RE required a login to start AND end alphanumeric, rejecting real legacy
grandfathered accounts that end in a hyphen or contain double hyphens (e.g.
"Link-", "gil--", "p-" — all real accounts created 2010-2013). The regex is only
an injection guard for interpolating the login into GraphQL, so relax it to the
actual GitHub charset+length ([A-Za-z0-9-]{1,39}); quotes/spaces/etc still blocked.

This fixes org backfill (and the request-path org build) aborting on such members.
Also harden backfill-orgs: a per-member 400/404 now records zeros + continues
instead of sinking the whole org; rate limits and 5xx still abort as before.
Prefills the DB with more organizations to look up. GitHub has no endpoint that
ranks orgs by public-member count, so this searches type:org by followers (the
best prominence proxy — repos-sort returns education/spam orgs), enriches each
candidate with fetchOrgProfile to get its real public member count, sorts/filters
by that, and records the survivors as entities rows (kind='org', builtAt null).
backfill-orgs.ts then fills them.

Separate from backfill-orgs.ts by design. --limit / --min-members / --query /
--dry-run flags; skips already-tracked orgs; idempotent upsert; paced under the
search (30/min) and GraphQL (5k/hr, 500 floor) limits.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant