delqhi/sin-github-issues
0
1#!/usr/bin/env node2 3import { readFile } from 'node:fs/promises';4import path from 'node:path';5 6const cwd = process.cwd();7const args = process.argv.slice(2);8 9function parseArgs(argv) {10 const out = {};11 for (let i = 0; i < argv.length; i += 1) {12 const token = argv[i];13 if (!token.startsWith('--')) continue;14 const key = token.slice(2);15 const next = argv[i + 1];16 if (!next || next.startsWith('--')) {17 out[key] = 'true';18 continue;19 }20 out[key] = next;21 i += 1;22 }23 return out;24}25 26const parsedArgs = parseArgs(args);27const configPath = path.resolve(28 cwd,29 parsedArgs['config'] || process.env.SIN_GITHUB_APP_ROUTING_PATH || './config/github-app-routing.local.json',30);31 32function firstString(...values) {33 for (const value of values) {34 if (typeof value === 'string' && value.trim()) return value.trim();35 }36 return null;37}38 39function readEnv(name) {40 if (!name) return null;41 const value = process.env[name];42 return typeof value === 'string' && value.trim() ? value.trim() : null;43}44 45function parseList(value) {46 if (!value || typeof value !== 'string') return [];47 return value48 .split(',')49 .map((item) => item.trim())50 .filter(Boolean);51}52 53function toRoute(entry, defaultWebhookPath) {54 const appId = firstString(String(entry.appId || '').trim(), readEnv(entry.appIdEnv));55 const clientId = firstString(entry.clientId, readEnv(entry.clientIdEnv));56 const routePath = firstString(entry.routePath, entry.webhookPath, defaultWebhookPath || '/github/webhook');57 const agentSlug = firstString(entry.agentSlug, entry.preferredAgentSlug, entry.appSlug);58 const privateKey = firstString(entry.privateKeyPem, readEnv(entry.privateKeyEnv));59 const webhookSecret = firstString(entry.webhookSecret, readEnv(entry.webhookSecretEnv));60 return {61 agentSlug,62 appSlug: firstString(entry.appSlug),63 botName: firstString(entry.botName),64 routePath,65 appId,66 clientId,67 privateKeyRef: firstString(entry.privateKeyEnv, entry.privateKeyPath, entry.privateKeyPem ? 'inline' : null),68 webhookSecretRef: firstString(entry.webhookSecretEnv, entry.webhookSecretPath, entry.webhookSecret ? 'inline' : null),69 privateKeyResolved: Boolean(privateKey),70 webhookSecretResolved: Boolean(webhookSecret),71 };72}73 74async function main() {75 const raw = await readFile(configPath, 'utf8');76 const config = JSON.parse(raw);77 const defaultWebhookPath = firstString(config.defaultWebhookPath, '/github/webhook');78 const agentsFilter = new Set(parseList(parsedArgs.agents || parsedArgs.agent));79 const configuredOnly = parsedArgs['configured-only'] === 'true';80 const apps = (Array.isArray(config.apps) ? config.apps.map((entry) => toRoute(entry, defaultWebhookPath)) : []).filter((app) => {81 const matchesAgent = agentsFilter.size === 0 || agentsFilter.has(app.agentSlug || '') || agentsFilter.has(app.appSlug || '');82 const isConfigured = !configuredOnly || Boolean(app.appId || app.clientId || app.privateKeyResolved || app.webhookSecretResolved);83 return matchesAgent && isConfigured;84 });85 const missing = apps.flatMap((app) => {86 const failures = [];87 if (!app.agentSlug) failures.push('agentSlug');88 if (!app.appId) failures.push('appId');89 if (!app.clientId) failures.push('clientId');90 if (!app.privateKeyResolved) failures.push('privateKey');91 if (!app.webhookSecretResolved) failures.push('webhookSecret');92 return failures.length ? [{ agentSlug: app.agentSlug || app.appSlug || 'unknown', missing: failures }] : [];93 });94 95 console.log(JSON.stringify({96 ok: missing.length === 0,97 configPath,98 defaultWebhookPath,99 filters: {100 agents: [...agentsFilter],101 configuredOnly,102 },103 apps,104 missing,105 }, null, 2));106 107 if (missing.length > 0) process.exitCode = 1;108}109 110main().catch((error) => {111 console.error(error instanceof Error ? error.message : String(error));112 process.exit(1);113});114 