build-registry.mjs

Derives registry.json — the register of onboarded repositories — by walking the estate for .redline.json.

What this is

Derives registry.json — the register of onboarded repositories — by walking every repo in the org for a .redline.json. The register is never hand-edited and redline init never writes it: an entry exists exactly as long as the repository's own file does, so a repo that removes Redline leaves the register on the next run.

How to onboard it

Nothing to onboard, and nothing an onboarded repository ever runs. This is maintainer tooling: it ships in this repository and runs where it already has an environment.

  • Runs in: This (source) repo.
  • Trigger: .github/workflows/registry.yml — nightly at 04:00 UTC, plus workflow_dispatch.

To run it yourself you need a checkout of this repository and Node 22 or newer. There are no runtime dependencies to install — every script uses only Node builtins — so a clone and the environment below is the whole setup.

How to use it

terminal
$ GH_TOKEN=... npx redlinegate registry --org acme --source owner/repo

Environment:

  • GH_TOKEN — read access to org repos.
  • ORG — the owner whose repositories are walked.
  • SOURCE — owner/name of this repo, recorded in the register so a consumer knows which estate it describes.
  • OUT — output path, default registry.json.

Nothing, normally — the nightly workflow runs it. Run it by hand after onboarding a batch of repositories if you do not want to wait for the next refresh.

Expected output

registry.json, with entries ordered by org then repo so a nightly commit only diffs when the estate actually changed. Prints one line per problem — a repository whose .redline.json is malformed, one with no default branch — and exits 1 without writing anything if no onboarded repository was found at all: an empty register is indistinguishable from a token that lost access, and publishing it would erase the dashboard's coverage figure and every sync target in one commit.

How to edit it

  1. Edit the .mjs file directlyNothing generates these — scripts/ is hand-written maintainer tooling with no runtime dependencies. Keep it that way: package.json declares none, and these run in CI with only Node's builtins available.
  2. node --check scripts/<file>.mjsCI's lint job parses every script in scripts/. A syntax error there fails the build without running anything.
  3. Run it locally with the same env CI gives itEach script is env-configured with no argument parsing, so a local run is the CI run. The environment variables it needs are listed above.

The full file

scripts/build-registry.mjs · 43 lines · 1.8 KB
#!/usr/bin/env node
// Builds registry.json: the derived register of onboarded repositories.
//
// Runs in the Redline source repo on a schedule. The register is DERIVED — it is
// discovered from .redline.json across the org, never hand-edited, and never written
// by `redline init`. An entry exists exactly as long as the repository's own file
// does, so a repository that removes Redline leaves the register on its next run.
//
// Requires `npm run build` first: it imports the compiled CLI from dist/.
//
// Env: GH_TOKEN (read access to org repos), ORG, SOURCE (owner/name of this repo),
//      [OUT=registry.json]

import { writeFileSync } from 'node:fs';
import { createGitHubClient } from '../dist/platforms/github/client.js';
import { discoverGitHub } from '../dist/registry/discover.js';
import { serializeRegistry } from '../dist/registry/serialize.js';

const { GH_TOKEN, ORG, SOURCE, OUT = 'registry.json' } = process.env;
if (!GH_TOKEN || !ORG || !SOURCE) throw new Error('GH_TOKEN, ORG and SOURCE are required');

const client = createGitHubClient({ token: GH_TOKEN });
const { entries, problems } = await discoverGitHub(client, ORG);

for (const problem of problems) console.warn(`  ${problem}`);

// An empty register is indistinguishable from a token that lost access, and
// publishing it would erase the dashboard's coverage figure and every sync
// target at once. Refuse rather than overwrite.
if (entries.length === 0) {
  console.error(
    `No onboarded repositories discovered in ${ORG}. Refusing to write an empty register.`
  );
  process.exit(1);
}

writeFileSync(
  OUT,
  serializeRegistry({ generatedAt: new Date().toISOString(), source: SOURCE, entries })
);

console.log(`${OUT}: ${entries.length} onboarded repositories, ${problems.length} problem(s)`);