#!/usr/bin/env node // Standalone Bugzilla CLI — interact with Bugzilla REST API directly import yargs from "yargs" import { hideBin } from "yargs/helpers" import { BugzillaClient } from "../lib/bugzilla/client" import { loadConfig } from "../lib/bugzilla/config" function getClient() { const config = loadConfig() return new BugzillaClient(config) } yargs(hideBin(process.argv)) .scriptName("bugzilla") .usage("$0 [options]") .command( "get ", "Get a bug by ID", (y) => y.positional("id", { type: "number", demandOption: true }), async (argv) => { const client = getClient() const bug = await client.getBug(argv.id as number) console.log(JSON.stringify(bug, null, 2)) } ) .command( "search", "Search for bugs", (y) => y .option("product", { type: "string", alias: "p" }) .option("component", { type: "string", alias: "c" }) .option("status", { type: "string", alias: "s" }) .option("summary", { type: "string" }) .option("limit", { type: "number", default: 50 }), async (argv) => { const client = getClient() const config = loadConfig() const bugs = await client.searchBugs({ product: argv.product ?? config.product, component: argv.component ?? config.component, status: argv.status, summary: argv.summary, limit: argv.limit, }) for (const bug of bugs) { console.log(`#${bug.id} [${bug.severity}] (${bug.status}) ${bug.summary}`) } console.log(`\n${bugs.length} bug(s) found`) } ) .command( "create", "Create a new bug", (y) => y .option("summary", { type: "string", alias: "s", demandOption: true }) .option("description", { type: "string", alias: "d" }) .option("priority", { type: "string", default: "Normal" }) .option("severity", { type: "string", default: "normal" }) .option("product", { type: "string", alias: "p" }) .option("component", { type: "string", alias: "c" }), async (argv) => { const client = getClient() const config = loadConfig() const id = await client.createBug({ product: argv.product ?? config.product, component: argv.component ?? config.component, summary: argv.summary, description: argv.description, priority: argv.priority, severity: argv.severity, }) console.log(`Created bug #${id}`) } ) .command( "update ", "Update a bug", (y) => y .positional("id", { type: "number", demandOption: true }) .option("summary", { type: "string", alias: "s" }) .option("status", { type: "string" }) .option("resolution", { type: "string" }) .option("priority", { type: "string" }), async (argv) => { const client = getClient() const payload: Record = {} if (argv.summary) payload.summary = argv.summary if (argv.status) payload.status = argv.status if (argv.resolution) payload.resolution = argv.resolution if (argv.priority) payload.priority = argv.priority await client.updateBug(argv.id as number, payload) console.log(`Updated bug #${argv.id}`) } ) .command( "comments ", "List comments on a bug", (y) => y.positional("id", { type: "number", demandOption: true }), async (argv) => { const client = getClient() const comments = await client.getComments(argv.id as number) for (const c of comments) { console.log(`--- Comment #${c.count} by ${c.creator} (${c.time}) ---`) console.log(c.text) console.log() } } ) .command( "comment ", "Add a comment to a bug", (y) => y .positional("id", { type: "number", demandOption: true }) .option("text", { type: "string", alias: "t", demandOption: true }), async (argv) => { const client = getClient() await client.addComment(argv.id as number, argv.text as string) console.log(`Comment added to bug #${argv.id}`) } ) .command( "products", "List accessible products", () => {}, async () => { const client = getClient() const products = await client.getAccessibleProducts() for (const p of products) { const compCount = p.components?.length ?? 0 console.log(`#${p.id} ${p.name} (${compCount} components) — ${p.description}`) } console.log(`\n${products.length} product(s)`) } ) .command( "product ", "Get product details and its components", (y) => y.positional("name", { type: "string", demandOption: true }), async (argv) => { const client = getClient() const product = await client.getProduct(argv.name as string) console.log(`Product: ${product.name} (#${product.id})`) console.log(`Description: ${product.description}`) console.log(`Active: ${product.is_active}`) console.log(`\nComponents:`) for (const c of product.components ?? []) { const status = c.is_active ? "active" : "inactive" console.log(` ${c.name} (${status}) — ${c.description}`) } } ) .command( "create-product", "Create a new product", (y) => y .option("name", { type: "string", alias: "n", demandOption: true }) .option("description", { type: "string", alias: "d", demandOption: true }) .option("version", { type: "string", default: "unspecified" }), async (argv) => { const client = getClient() const id = await client.createProduct({ name: argv.name, description: argv.description, version: argv.version, }) console.log(`Created product #${id}: ${argv.name}`) } ) .command( "components ", "List components for a product", (y) => y.positional("product", { type: "string", demandOption: true }), async (argv) => { const client = getClient() const components = await client.getComponents(argv.product as string) for (const c of components) { const status = c.is_active ? "active" : "inactive" console.log(`#${c.id} ${c.name} (${status}) — ${c.description}`) } console.log(`\n${components.length} component(s)`) } ) .command( "create-component", "Create a new component", (y) => y .option("product", { type: "string", alias: "p", demandOption: true }) .option("name", { type: "string", alias: "n", demandOption: true }) .option("description", { type: "string", alias: "d", demandOption: true }) .option("assignee", { type: "string", alias: "a", demandOption: true }), async (argv) => { const client = getClient() const id = await client.createComponent({ product: argv.product, name: argv.name, description: argv.description, default_assignee: argv.assignee, }) console.log(`Created component #${id}: ${argv.name} (in ${argv.product})`) } ) .demandCommand(1, "Please specify a command") .strict() .help() .parse()