mirror of https://github.com/garrytan/gstack.git
76 lines
3.2 KiB
Plaintext
Executable File
76 lines
3.2 KiB
Plaintext
Executable File
#!/usr/bin/env bun
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
import { spawnSync } from 'child_process';
|
|
import { loadLensRegistry } from '../scripts/lenses/registry';
|
|
import { loadLensPolicy, routeLenses, type LensRouteMode } from '../scripts/lenses/routing';
|
|
|
|
function arg(name: string): string | undefined {
|
|
const index = process.argv.indexOf(name);
|
|
if (index < 0) return undefined;
|
|
const value = process.argv[index + 1];
|
|
return value && !value.startsWith('--') ? value : undefined;
|
|
}
|
|
function flag(name: string): boolean {
|
|
return process.argv.includes(name);
|
|
}
|
|
function csv(value: string | undefined): string[] {
|
|
return value ? value.split(',').map((item) => item.trim()).filter(Boolean) : [];
|
|
}
|
|
function git(args: string[], cwd: string): string {
|
|
const result = spawnSync('git', args, { cwd, encoding: 'utf8' });
|
|
if (result.status !== 0) throw new Error(`git ${args.join(' ')} failed: ${result.stderr.trim()}`);
|
|
return result.stdout;
|
|
}
|
|
|
|
const repoRoot = path.resolve(arg('--repo-root') ?? process.cwd());
|
|
const mode = (arg('--mode') ?? 'mandatory') as LensRouteMode;
|
|
if (!['mandatory', 'recommended', 'all', 'explicit'].includes(mode)) throw new Error(`Invalid --mode ${mode}`);
|
|
|
|
let changedPaths: string[];
|
|
let addedLines: string[];
|
|
const pathsFile = arg('--paths-file');
|
|
const addedFile = arg('--added-lines-file');
|
|
if (pathsFile) {
|
|
changedPaths = fs.readFileSync(pathsFile, 'utf8').split(/\r?\n/).map((item: string) => item.trim()).filter(Boolean);
|
|
addedLines = addedFile ? fs.readFileSync(addedFile, 'utf8').split(/\r?\n/) : [];
|
|
} else {
|
|
const base = arg('--base') ?? 'main';
|
|
let diffBase: string;
|
|
try {
|
|
git(['fetch', 'origin', base, '--quiet'], repoRoot);
|
|
diffBase = git(['merge-base', `origin/${base}`, 'HEAD'], repoRoot).trim();
|
|
} catch {
|
|
diffBase = git(['merge-base', base, 'HEAD'], repoRoot).trim();
|
|
}
|
|
changedPaths = git(['diff', '--name-only', diffBase], repoRoot).split(/\r?\n/).filter(Boolean);
|
|
addedLines = git(['diff', '--unified=0', diffBase], repoRoot)
|
|
.split(/\r?\n/)
|
|
.filter((line) => line.startsWith('+') && !line.startsWith('+++'))
|
|
.map((line) => line.slice(1));
|
|
}
|
|
|
|
let prLabels = csv(arg('--pr-labels'));
|
|
if (prLabels.length === 0 && !flag('--no-gh')) {
|
|
const gh = spawnSync('gh', ['pr', 'view', '--json', 'labels', '-q', '.labels[].name'], { cwd: repoRoot, encoding: 'utf8' });
|
|
if (gh.status === 0) prLabels = gh.stdout.split(/\r?\n/).map((item: string) => item.trim()).filter(Boolean);
|
|
}
|
|
|
|
const policyPath = path.resolve(arg('--policy') ?? path.join(repoRoot, '.gstack', 'lens-policy.yaml'));
|
|
const bypassReason = arg('--no-mandatory-lenses') ?? arg('--no-mandatory-reason');
|
|
const bypassMandatory = flag('--no-mandatory') || process.argv.includes('--no-mandatory-lenses');
|
|
const specs = loadLensRegistry(repoRoot);
|
|
const output = routeLenses(specs, {
|
|
mode,
|
|
requested: csv(arg('--requested')),
|
|
allow_draft: flag('--allow-draft'),
|
|
changed_paths: changedPaths,
|
|
pr_labels: prLabels,
|
|
added_lines: addedLines,
|
|
declared_surfaces: csv(arg('--surface')),
|
|
policy: loadLensPolicy(policyPath),
|
|
no_mandatory: bypassMandatory,
|
|
no_mandatory_reason: bypassReason,
|
|
});
|
|
process.stdout.write(`${JSON.stringify({ ...output, changed_paths: changedPaths, pr_labels: prLabels }, null, 2)}\n`);
|