init
This commit is contained in:
commit
932d4ad420
46 changed files with 5800 additions and 0 deletions
157
lib/commands/InitCommand.ts
Normal file
157
lib/commands/InitCommand.ts
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
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
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue