import { listTransactions, transactionStatuses } from "@olenbetong/appframe-updater"; import chalk from "chalk"; import inquirer from "inquirer"; import open from "open"; import { renderTransactionsEditor } from "./editor/TransactionsEditor.js"; import { Command, type ServerOption } from "./lib/Command.js"; import { drainStdin } from "./lib/drainStdin.js"; import { importJson } from "./lib/importJson.js"; import { Server } from "./lib/Server.js"; const isInteractive = process.stdout.isTTY; type ApplyOptions = ServerOption & { preapprove?: string[]; }; function collect(value: string, previous: string[]) { return previous.concat([value]); } async function applyTransactions(namespaceArg: string, options: ApplyOptions) { try { options.server = options.server || "test.obet.no"; let server = new Server(options.server); let namespace = await server.getNamespaceArgument(namespaceArg, options); while (true) { let transactions = await server.getTransactions("edit", namespace); let onHold = transactions.filter((r) => r.Status === transactionStatuses.ON_HOLD); let actionableTransactions = transactions.filter((r) => r.Status !== transactionStatuses.ON_HOLD); let errors = actionableTransactions.filter((r) => r.Status === transactionStatuses.ERROR); if (actionableTransactions.length === 0 && onHold.length === 0) { console.error(chalk.yellow("No transactions available to apply.")); return; } if (errors.length > 0) { console.error(chalk.red("There are transactions with errors blocking this namespace:")); console.table(errors.map(({ Status, ...tran }) => tran)); return; } let isApproved = actionableTransactions.length > 0; for (let transaction of actionableTransactions) { if (!options.preapprove?.includes(transaction.Name)) { isApproved = false; } } listTransactions(actionableTransactions); let shouldApply = isApproved || !isInteractive; if (isApproved) { console.log(chalk.blue("All transactions approved by CLI parameters.")); } else if (isInteractive) { if (onHold.length > 0) { console.log(""); console.log( chalk.yellow( `CAUTION! There are ${onHold.length} transactions on hold in namespace ${namespace}. Be certain that your updates don't depend on them before applying!`, ), ); console.log(""); } await drainStdin(); let { action } = await inquirer.prompt({ type: "select", name: "action", message: "Apply these transactions?", choices: [ { name: "Yes", value: "yes" }, { name: "No", value: "no" }, { name: "Edit transactions", value: "edit" }, ], default: "no", }); if (action === "yes") { shouldApply = true; } else if (action === "no") { process.exit(0); } else if (action === "edit") { let instance = await renderTransactionsEditor({ server, namespace }); await instance.waitUntilExit(); continue; } } if (shouldApply) { break; } } await server.apply(namespace); if (options.server.startsWith("test.")) { let result = await inquirer.prompt({ type: "confirm", name: "openChangelog", message: "Would you like to write an entry in the changelog for these updates? (y/N)", default: false, }); if (result.openChangelog) { open("https://test.obet.no/appdesigner?synergi.olenbetong.no/it-nytt"); } } server.logServerMessage("Done."); } catch (error) { console.error(chalk.red((error as Error).message)); process.exit(1); } } const appPkg = await importJson("../package.json"); const program = new Command(); program .version(appPkg.version) .addServerOption() .addNamespaceArgument() .option( "-p, --preapprove ", "names of updates that can be applied without prompting the user first", collect, [], ) .action(applyTransactions); await program.parseAsync(process.argv);