mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-10-30 09:16:55 +00:00
fix(cli): report failed tag installations to scripts and agents (#6086)
* fix(cli): report failed tag installations to scripts and agents * fix(cli): restore status markers in tag install summaries
This commit is contained in:
@@ -150,6 +150,10 @@ The positional name is a registry item, or a tag — a tag installs every block
|
||||
carrying it. `--dir` picks the project directory, `--json` prints the written
|
||||
files and paste snippet, and `--no-clipboard` skips the clipboard copy for CI.
|
||||
|
||||
If any block in a tag fails to install, `add` keeps the successful installs and
|
||||
exits with code 1. With `--json`, `ok` is `false` and `errors` lists each failed
|
||||
block's `name` and `error`; `installed` lists the blocks that succeeded.
|
||||
|
||||
Every install produces files plus a **paste snippet**: the `<iframe>` tag for a
|
||||
block, or the fragment path for a component. It goes to your clipboard by
|
||||
default. Naming an example instead of a registry item (`add warm-grain`) gets
|
||||
|
||||
@@ -618,6 +618,89 @@ describe("tagAddJson", () => {
|
||||
tag: "lower-thirds",
|
||||
installed: ["a", "b"],
|
||||
warnings: ["b: no id"],
|
||||
errors: [],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("tag installation failures", () => {
|
||||
let dir: string;
|
||||
|
||||
beforeEach(() => {
|
||||
dir = tmp();
|
||||
writeRegistryConfig(dir);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
vi.unstubAllGlobals();
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ names: ["my-block"], failed: ["my-block"], installed: [], json: true },
|
||||
{
|
||||
names: ["my-block", "deprecated-block"],
|
||||
failed: ["my-block"],
|
||||
installed: ["deprecated-block"],
|
||||
json: true,
|
||||
},
|
||||
{ names: ["my-block"], failed: [], installed: ["my-block"], json: true },
|
||||
{ names: ["my-block"], failed: ["my-block"], installed: [], json: false },
|
||||
{ names: ["my-block"], failed: [], installed: ["my-block"], json: false },
|
||||
])("reports failed downloads and keeps successful installs: %j", async (scenario) => {
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn(async (input: string | URL) => {
|
||||
const url = new URL(input);
|
||||
if (url.pathname.endsWith("/registry.json")) {
|
||||
return Response.json({
|
||||
...MANIFEST,
|
||||
items: MANIFEST.items.filter((item) => scenario.names.includes(item.name)),
|
||||
});
|
||||
}
|
||||
const name = scenario.names.find((item) => url.pathname.includes(`/blocks/${item}/`));
|
||||
if (!name) return new Response("not found", { status: 404 });
|
||||
if (url.pathname.endsWith("/registry-item.json")) {
|
||||
return Response.json({ ...ITEM_BY_NAME[name], tags: ["test-tag"] });
|
||||
}
|
||||
if (scenario.failed.includes(name)) return new Response("not found", { status: 404 });
|
||||
return new Response(FILE_BODIES[`${name}.html`]);
|
||||
}),
|
||||
);
|
||||
const log = vi.spyOn(console, "log").mockImplementation(() => {});
|
||||
const run = runCittyCommand(addCommand, {
|
||||
rawArgs: ["test-tag", "--dir", dir, "--no-clipboard", ...(scenario.json ? ["--json"] : [])],
|
||||
});
|
||||
if (scenario.failed.length > 0) {
|
||||
await expect(run).rejects.toMatchObject({ result: { exitCode: 1, presented: true } });
|
||||
} else {
|
||||
await expect(run).resolves.toBeDefined();
|
||||
}
|
||||
|
||||
if (scenario.json) {
|
||||
expect(log).toHaveBeenCalledTimes(1);
|
||||
expect(JSON.parse(String(log.mock.calls[0]?.[0]))).toMatchObject({
|
||||
ok: scenario.failed.length === 0,
|
||||
tag: "test-tag",
|
||||
installed: scenario.installed,
|
||||
errors: scenario.failed.map((name) => ({
|
||||
name,
|
||||
error: expect.stringContaining("HTTP 404"),
|
||||
})),
|
||||
});
|
||||
} else {
|
||||
expect(log).toHaveBeenLastCalledWith(
|
||||
`${scenario.failed.length > 0 ? "✗" : "✓"} Installed ${scenario.installed.length}/${scenario.names.length} blocks`,
|
||||
);
|
||||
if (scenario.failed.length > 0) {
|
||||
expect(log.mock.calls.flat().join(" ")).toContain("HTTP 404");
|
||||
}
|
||||
}
|
||||
for (const name of scenario.names) {
|
||||
expect(existsSync(join(dir, "compositions", `${name}.html`))).toBe(
|
||||
scenario.installed.includes(name),
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -146,13 +146,18 @@ export function buildSnippet(
|
||||
return "";
|
||||
}
|
||||
|
||||
/** `add <tag> --json` output: each item's warnings, prefixed with its name. */
|
||||
export function tagAddJson(tag: string, results: RunAddResult[]) {
|
||||
/** `add <tag> --json` output: successful installs and each item's warnings or failure. */
|
||||
export function tagAddJson(
|
||||
tag: string,
|
||||
results: RunAddResult[],
|
||||
errors: Array<{ name: string; error: string }> = [],
|
||||
) {
|
||||
return {
|
||||
ok: true,
|
||||
ok: errors.length === 0,
|
||||
tag,
|
||||
installed: results.map((r) => r.name),
|
||||
warnings: results.flatMap((r) => r.warnings.map((w) => `${r.name}: ${w}`)),
|
||||
errors,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -584,6 +589,7 @@ export default defineCommand({
|
||||
}
|
||||
|
||||
const results: RunAddResult[] = [];
|
||||
const errors: Array<{ name: string; error: string }> = [];
|
||||
for (const item of items) {
|
||||
try {
|
||||
const result = await runAdd({
|
||||
@@ -600,16 +606,19 @@ export default defineCommand({
|
||||
if (!json) console.log(` ${c.success("✓")} ${result.name}`);
|
||||
} catch (err) {
|
||||
const reason = err instanceof Error ? err.message : String(err);
|
||||
errors.push({ name: item.name, error: reason });
|
||||
if (!json) console.log(` ${c.error("✗")} ${item.name} (skipped: ${reason})`);
|
||||
}
|
||||
}
|
||||
|
||||
if (json) {
|
||||
console.log(JSON.stringify(tagAddJson(args.name, results)));
|
||||
console.log(JSON.stringify(tagAddJson(args.name, results, errors)));
|
||||
} else {
|
||||
console.log("");
|
||||
console.log(`${c.success("✓")} Installed ${results.length}/${items.length} blocks`);
|
||||
const summary = `Installed ${results.length}/${items.length} blocks`;
|
||||
console.log(`${errors.length > 0 ? c.error("✗") : c.success("✓")} ${summary}`);
|
||||
}
|
||||
if (errors.length > 0) failCommand();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user