1// `forge jev [owner/repo]`: ask Jev about what a repo's pages show and store2// the answers (see lib/jev.ts). Runs after a push, before the build. Only3// what has no stored answer is asked, so a second run asks nothing.4//5// node lib/jev-fill.ts [owner/repo] every repo without an argument6import fs from 'node:fs';7import path from 'node:path';8import { fileURLToPath } from 'node:url';9import { config, discover } from './forge.ts';10import { tryRun } from './git.ts';11import { JevStore, enabled, spent } from './jev.ts';12import * as commits from './jev-commits.ts';13import * as reviews from './jev-reviews.ts';14import type { Config } from './types.ts';1516export interface FillContext {17 cfg: Config;18 gitDir: string;19 owner: string;20 name: string;21 slug: string;22 defaultBranch: string;23 store: JevStore;24}2526// Each part asks about one area of the pages; a failure in one doesn't stop the others.27const PARTS: [string, (ctx: FillContext) => Promise<void>][] = [28 ['reviews', reviews.fill],29 ['commits', commits.fill],30];3132export async function fillRepo(cfg: Config, owner: string, name: string): Promise<void> {33 const gitDir = path.join(cfg.root, owner, `${name}.git`);34 if (!fs.existsSync(path.join(gitDir, 'HEAD'))) throw new Error(`no repo ${owner}/${name}`);35 const defaultBranch = tryRun(gitDir, ['symbolic-ref', '--short', 'HEAD'])?.trim() || 'main';36 const ctx: FillContext = { cfg, gitDir, owner, name, slug: `${owner}/${name}`, defaultBranch, store: new JevStore(gitDir) };37 for (const [part, fill] of PARTS) {38 try {39 await fill(ctx);40 } catch (err) {41 process.stderr.write(`jev: ${ctx.slug} ${part}: ${err instanceof Error ? err.stack : String(err)}\n`);42 }43 }44}4546if (process.argv[1] && fs.realpathSync(process.argv[1]) === fileURLToPath(import.meta.url)) {47 if (!enabled()) process.exit(0);48 const cfg = config();49 const only = process.argv[2];50 // Private repos have no pages to show answers on (bin/forge's cmd_jev skips them too).51 const targets = only ? [only] : discover(cfg).repos.filter((r) => !r.private).map((r) => `${r.owner}/${r.name}`);52 const t0 = Date.now();53 for (const slug of targets) {54 const [owner, name] = slug.split('/');55 await fillRepo(cfg, owner, name);56 }57 if (spent.requests || spent.failed) {58 process.stderr.write(`jev: ${only ?? 'all repos'}: ${spent.requests} requests, ${spent.tokens} tokens, ${spent.failed} failed, ${Date.now() - t0}ms\n`);59 }60}