157 lines
4.7 KiB
TypeScript
157 lines
4.7 KiB
TypeScript
import type { Argv, ArgumentsCamelCase } from "yargs"
|
|
import { CLICommand } from "../cli/CLICommand"
|
|
import { parseTodoFile } from "../file"
|
|
import { BugzillaClient } from "../bugzilla/client"
|
|
import { loadConfig } from "../bugzilla/config"
|
|
|
|
interface ProductState {
|
|
name: string
|
|
exists: boolean
|
|
components: {
|
|
name: string
|
|
exists: boolean
|
|
}[]
|
|
}
|
|
|
|
export class InitCommand extends CLICommand {
|
|
readonly name = "init"
|
|
readonly help = "Initialize Bugzilla products and components"
|
|
readonly description = "Check and optionally create Bugzilla products/components from TODO tracker config"
|
|
|
|
addArguments(yargs: Argv): Argv {
|
|
return yargs
|
|
.option("dry-run", {
|
|
type: "boolean",
|
|
default: false,
|
|
description: "Show what would be created without creating",
|
|
})
|
|
.option("confirm", {
|
|
type: "boolean",
|
|
default: false,
|
|
description: "Actually create missing products/components (required for writes)",
|
|
})
|
|
.option("assignee", {
|
|
type: "string",
|
|
description: "Default assignee email for new components",
|
|
})
|
|
}
|
|
|
|
async execute(args: ArgumentsCamelCase): Promise<number> {
|
|
const todo = await parseTodoFile()
|
|
|
|
if (!todo.bugzilla) {
|
|
console.error("No application/bugzilla part found in TODO file")
|
|
return 1
|
|
}
|
|
|
|
const config = loadConfig()
|
|
const client = new BugzillaClient(config)
|
|
const dryRun = args.dryRun as boolean
|
|
const confirm = args.confirm as boolean
|
|
const assignee = args.assignee as string | undefined
|
|
|
|
// Collect unique products and their components from the mappings
|
|
const productMap = new Map<string, Set<string>>()
|
|
for (const m of todo.bugzilla.mappings) {
|
|
if (!productMap.has(m.product)) productMap.set(m.product, new Set())
|
|
productMap.get(m.product)!.add(m.component)
|
|
}
|
|
|
|
// Check what exists on the server
|
|
const states: ProductState[] = []
|
|
const existingProducts = await client.getAccessibleProducts()
|
|
const existingByName = new Map(existingProducts.map(p => [p.name, p]))
|
|
|
|
for (const [productName, componentNames] of productMap) {
|
|
const existing = existingByName.get(productName)
|
|
const existingComponents = new Set(
|
|
existing?.components?.map(c => c.name) ?? []
|
|
)
|
|
|
|
states.push({
|
|
name: productName,
|
|
exists: !!existing,
|
|
components: [...componentNames].map(name => ({
|
|
name,
|
|
exists: existingComponents.has(name),
|
|
})),
|
|
})
|
|
}
|
|
|
|
// Report
|
|
let allOk = true
|
|
for (const product of states) {
|
|
const pStatus = product.exists ? "ok" : "MISSING"
|
|
console.log(`Product: ${product.name} [${pStatus}]`)
|
|
for (const comp of product.components) {
|
|
const cStatus = comp.exists ? "ok" : "MISSING"
|
|
console.log(` Component: ${comp.name} [${cStatus}]`)
|
|
if (!comp.exists) allOk = false
|
|
}
|
|
if (!product.exists) allOk = false
|
|
}
|
|
|
|
if (allOk) {
|
|
console.log("\nAll products and components exist. Nothing to do.")
|
|
return 0
|
|
}
|
|
|
|
if (dryRun) {
|
|
console.log("\n[dry-run] Would create the MISSING items above.")
|
|
return 0
|
|
}
|
|
|
|
if (!confirm) {
|
|
console.log("\nMissing items found. Run with --confirm to create them.")
|
|
return 1
|
|
}
|
|
|
|
// Need assignee for component creation
|
|
const needsComponents = states.some(p =>
|
|
p.components.some(c => !c.exists)
|
|
)
|
|
if (needsComponents && !assignee) {
|
|
console.error("\n--assignee is required when creating components")
|
|
return 1
|
|
}
|
|
|
|
// Create missing products and components
|
|
let created = 0
|
|
for (const product of states) {
|
|
if (!product.exists) {
|
|
try {
|
|
const id = await client.createProduct({
|
|
name: product.name,
|
|
description: product.name,
|
|
version: "unspecified",
|
|
})
|
|
console.log(`Created product #${id}: ${product.name}`)
|
|
created++
|
|
} catch (err: any) {
|
|
console.error(`Error creating product "${product.name}": ${err.message}`)
|
|
continue
|
|
}
|
|
}
|
|
|
|
for (const comp of product.components) {
|
|
if (!comp.exists) {
|
|
try {
|
|
const id = await client.createComponent({
|
|
product: product.name,
|
|
name: comp.name,
|
|
description: comp.name,
|
|
default_assignee: assignee!,
|
|
})
|
|
console.log(`Created component #${id}: ${comp.name} (in ${product.name})`)
|
|
created++
|
|
} catch (err: any) {
|
|
console.error(`Error creating component "${comp.name}": ${err.message}`)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log(`\nInit complete: ${created} items created`)
|
|
return 0
|
|
}
|
|
}
|