feat(tasktree): add GitHub disaster recovery backups
This commit is contained in:
@@ -40,6 +40,9 @@ function help() {
|
||||
"hwlab-cli tasktree report list|get --task ID|--report ID",
|
||||
"hwlab-cli tasktree report write --task ID --title TEXT (--body TEXT|--body-file PATH|--stdin) [--status STATUS]",
|
||||
"hwlab-cli tasktree timeline --group ID [--overapi]",
|
||||
"hwlab-cli tasktree backup create [--dry-run] [--reason TEXT]",
|
||||
"hwlab-cli tasktree backup status|verify",
|
||||
"hwlab-cli tasktree backup restore [--confirm --expected-sha SHA]",
|
||||
"hwlab-cli tasktree workflow start --task ID"
|
||||
],
|
||||
transportContract: "--overapi only changes transport; commands and options are identical",
|
||||
@@ -105,6 +108,18 @@ async function commandFrom(parsed: Parsed, stdin?: string): Promise<TaskTreeComm
|
||||
if (group === "report" && action === "list") return { operation: "report.list", taskId: required(parsed, "task") };
|
||||
if (group === "report" && action === "get") return { operation: "report.get", reportId: required(parsed, "report") };
|
||||
if (group === "report" && action === "write") return { operation: "report.write", taskId: required(parsed, "task"), title: required(parsed, "title"), body: await bodyInput(parsed, stdin), status: parsed.values.status };
|
||||
if (group === "backup" && action === "create") return {
|
||||
operation: "backup.create",
|
||||
dryRun: parsed.flags.has("dry-run"),
|
||||
reason: parsed.values.reason
|
||||
};
|
||||
if (group === "backup" && action === "status") return { operation: "backup.status" };
|
||||
if (group === "backup" && action === "verify") return { operation: "backup.verify" };
|
||||
if (group === "backup" && action === "restore") return {
|
||||
operation: "backup.restore",
|
||||
confirm: parsed.flags.has("confirm"),
|
||||
expectedSha256: parsed.values["expected-sha"]
|
||||
};
|
||||
if (group === "workflow" && action === "start") return { operation: "workflow.start", taskId: required(parsed, "task") };
|
||||
throw codedError("unsupported_command", `unsupported TaskTree command: ${group} ${action}`.trim());
|
||||
}
|
||||
@@ -144,7 +159,7 @@ function parse(argv: string[]): Parsed {
|
||||
if (parsed.overapi) throw codedError("duplicate_option", "--overapi may only be provided once");
|
||||
parsed.overapi = true;
|
||||
}
|
||||
else if (token === "--dry-run" || token === "--stdin") {
|
||||
else if (token === "--dry-run" || token === "--stdin" || token === "--confirm") {
|
||||
const flag = token.slice(2);
|
||||
if (parsed.flags.has(flag)) throw codedError("duplicate_option", `${token} may only be provided once`);
|
||||
parsed.flags.add(flag);
|
||||
@@ -211,6 +226,10 @@ const COMMAND_SPECS: Record<string, CommandSpec> = {
|
||||
"report list": { positionals: 2, options: ["task"] },
|
||||
"report get": { positionals: 2, options: ["report"] },
|
||||
"report write": { positionals: 2, options: ["task", "title", "body", "body-file", "status"], flags: ["stdin"] },
|
||||
"backup create": { positionals: 2, options: ["reason"], flags: ["dry-run"] },
|
||||
"backup status": { positionals: 2 },
|
||||
"backup verify": { positionals: 2 },
|
||||
"backup restore": { positionals: 2, options: ["expected-sha"], flags: ["confirm"] },
|
||||
"workflow start": { positionals: 2, options: ["task"] }
|
||||
};
|
||||
|
||||
@@ -236,4 +255,11 @@ function validateArguments(parsed: Parsed) {
|
||||
for (const flag of parsed.flags) {
|
||||
if (!allowedFlags.has(flag)) throw codedError("invalid_option", `--${flag} is not valid for ${key}`);
|
||||
}
|
||||
if (key === "backup restore") {
|
||||
const confirm = parsed.flags.has("confirm");
|
||||
const expectedSha = parsed.values["expected-sha"];
|
||||
if (confirm !== (expectedSha !== undefined)) {
|
||||
throw codedError("restore_confirmation_invalid", "--confirm and --expected-sha must be provided together");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user