mirror of
https://github.com/openclaw/clawhub.git
synced 2026-08-14 00:47:57 +00:00
fix(cli): clarify pending publication results (#3193)
This commit is contained in:
@@ -147,6 +147,10 @@ description: Automation workflow for recurring reports.
|
||||
skillId: "skills:demo",
|
||||
versionId: "skillVersions:demo",
|
||||
embeddingId: "skillEmbeddings:demo",
|
||||
status: "published",
|
||||
slug: "automation-helper",
|
||||
version: "1.0.0",
|
||||
publicationStatus: "published",
|
||||
});
|
||||
expect(runMutation).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
|
||||
@@ -510,7 +510,13 @@ async function publishVersionForUserInternal(
|
||||
skillInsertArgs,
|
||||
)) as PublishResult;
|
||||
await scheduleSkillPublishFollowups(ctx, publishResult, followup);
|
||||
return publishResult;
|
||||
return {
|
||||
...publishResult,
|
||||
status: "published",
|
||||
slug,
|
||||
version,
|
||||
publicationStatus: "published",
|
||||
};
|
||||
}
|
||||
|
||||
const pendingInsertArgs = {
|
||||
@@ -565,7 +571,13 @@ async function publishVersionForUserInternal(
|
||||
};
|
||||
|
||||
if (staged.status === "finalized" && staged.result) {
|
||||
return staged.result;
|
||||
return {
|
||||
...staged.result,
|
||||
status: "published",
|
||||
slug,
|
||||
version,
|
||||
publicationStatus: "published",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -9009,7 +9009,12 @@ describe("packages public queries", () => {
|
||||
},
|
||||
},
|
||||
}),
|
||||
).resolves.toEqual({ ok: true, packageId: "packages:demo", releaseId: "releases:demo-1" });
|
||||
).resolves.toEqual({
|
||||
ok: true,
|
||||
packageId: "packages:demo",
|
||||
releaseId: "releases:demo-1",
|
||||
publicationStatus: "published",
|
||||
});
|
||||
|
||||
expect(runMutation).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
|
||||
+12
-2
@@ -7899,7 +7899,13 @@ async function publishPackageImpl(
|
||||
}
|
||||
|
||||
if (staged.status === "finalized" && staged.result) {
|
||||
return inspectorFindings.length > 0 ? { ...staged.result, inspectorFindings } : staged.result;
|
||||
const finalizedResult = {
|
||||
...staged.result,
|
||||
publicationStatus: "published" as const,
|
||||
};
|
||||
return inspectorFindings.length > 0
|
||||
? { ...finalizedResult, inspectorFindings }
|
||||
: finalizedResult;
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -8003,7 +8009,11 @@ async function publishPackageImpl(
|
||||
source: "publish",
|
||||
});
|
||||
|
||||
return inspectorFindings.length > 0 ? { ...publishResult, inspectorFindings } : publishResult;
|
||||
const publishedResult = {
|
||||
...publishResult,
|
||||
publicationStatus: "published" as const,
|
||||
};
|
||||
return inspectorFindings.length > 0 ? { ...publishedResult, inspectorFindings } : publishedResult;
|
||||
}
|
||||
|
||||
function toPackageInspectorPublishResponseFinding(
|
||||
|
||||
@@ -1225,6 +1225,7 @@ describe("package commands", () => {
|
||||
ok: true,
|
||||
packageId: "pkg_1",
|
||||
releaseId: "rel_1",
|
||||
publicationStatus: "published",
|
||||
});
|
||||
|
||||
const options = {
|
||||
@@ -1320,7 +1321,7 @@ describe("package commands", () => {
|
||||
});
|
||||
|
||||
expect(uiMocks.spinner.succeed).toHaveBeenCalledWith(
|
||||
"OK. Uploaded @scope/pending-plugin@1.0.0; security checks are pending before it becomes public (rel_1)",
|
||||
"Update submitted for @scope/pending-plugin@1.0.0; pending security scans before it becomes public.",
|
||||
);
|
||||
expect(mockWrite).not.toHaveBeenCalled();
|
||||
} finally {
|
||||
@@ -1371,11 +1372,54 @@ describe("package commands", () => {
|
||||
expect(output).toEqual(
|
||||
expect.objectContaining({
|
||||
name: "@scope/json-pending-plugin",
|
||||
status: "pending-publication",
|
||||
releaseId: "rel_1",
|
||||
publicationStatus: "pending",
|
||||
attemptId: "attempt_1",
|
||||
}),
|
||||
);
|
||||
expect(output.status).not.toBe("published");
|
||||
} finally {
|
||||
await rm(workdir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("does not claim a package was published when the server omits publication status", async () => {
|
||||
const workdir = await makeTmpWorkdir();
|
||||
try {
|
||||
const folder = join(workdir, "unknown-status-plugin");
|
||||
await mkdir(join(folder, "dist"), { recursive: true });
|
||||
await writeFile(
|
||||
join(folder, "package.json"),
|
||||
makeCodePluginPackageJson({
|
||||
name: "@scope/unknown-status-plugin",
|
||||
displayName: "Unknown Status Plugin",
|
||||
version: "1.0.0",
|
||||
files: ["dist", "openclaw.plugin.json"],
|
||||
}),
|
||||
"utf8",
|
||||
);
|
||||
await writeFile(
|
||||
join(folder, "openclaw.plugin.json"),
|
||||
JSON.stringify({ id: "unknown.status.plugin" }),
|
||||
"utf8",
|
||||
);
|
||||
await writeFile(join(folder, "dist", "index.js"), "export const demo = true;\n", "utf8");
|
||||
httpMocks.apiRequestForm.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
packageId: "pkg_1",
|
||||
releaseId: "rel_1",
|
||||
});
|
||||
|
||||
await cmdPublishPackage(makeOpts(workdir), "unknown-status-plugin", {
|
||||
owner: "@openclaw",
|
||||
sourceRepo: "openclaw/unknown-status-plugin",
|
||||
sourceCommit: "abc123",
|
||||
});
|
||||
|
||||
expect(uiMocks.spinner.succeed).toHaveBeenCalledWith(
|
||||
"Update submitted for @scope/unknown-status-plugin@1.0.0; publication status was not reported.",
|
||||
);
|
||||
} finally {
|
||||
await rm(workdir, { recursive: true, force: true });
|
||||
}
|
||||
@@ -1601,6 +1645,7 @@ describe("package commands", () => {
|
||||
ok: true,
|
||||
packageId: "pkg_1",
|
||||
releaseId: "rel_1",
|
||||
publicationStatus: "published",
|
||||
});
|
||||
|
||||
await cmdPublishPackage(makeOpts(workdir), packName, {
|
||||
@@ -2645,6 +2690,7 @@ describe("package commands", () => {
|
||||
ok: true,
|
||||
packageId: "pkg_1",
|
||||
releaseId: "rel_1",
|
||||
publicationStatus: "published",
|
||||
inspectorFindings: [
|
||||
{
|
||||
findingKind: "warning",
|
||||
|
||||
@@ -975,14 +975,21 @@ export async function cmdPublishPackage(
|
||||
ApiV1PackagePublishResponseSchema,
|
||||
);
|
||||
|
||||
const isPendingPublication = result.publicationStatus === "pending";
|
||||
const publicationStatus = result.publicationStatus;
|
||||
const outputStatus =
|
||||
publicationStatus === "pending"
|
||||
? "pending-publication"
|
||||
: publicationStatus === "published"
|
||||
? "published"
|
||||
: "submitted";
|
||||
if (options.json) {
|
||||
process.stdout.write(
|
||||
`${JSON.stringify(
|
||||
{
|
||||
...plan.output,
|
||||
status: outputStatus,
|
||||
releaseId: result.releaseId,
|
||||
publicationStatus: result.publicationStatus,
|
||||
publicationStatus,
|
||||
attemptId: result.attemptId,
|
||||
inspectorFindings: result.inspectorFindings,
|
||||
},
|
||||
@@ -991,11 +998,19 @@ export async function cmdPublishPackage(
|
||||
)}\n`,
|
||||
);
|
||||
} else {
|
||||
spinner?.succeed(
|
||||
isPendingPublication
|
||||
? `OK. Uploaded ${plan.payload.name}@${plan.payload.version}; security checks are pending before it becomes public (${result.releaseId})`
|
||||
: `OK. Published ${plan.payload.name}@${plan.payload.version} (${result.releaseId})`,
|
||||
);
|
||||
if (publicationStatus === "pending") {
|
||||
spinner?.succeed(
|
||||
`Update submitted for ${plan.payload.name}@${plan.payload.version}; pending security scans before it becomes public.`,
|
||||
);
|
||||
} else if (publicationStatus === "published") {
|
||||
spinner?.succeed(
|
||||
`OK. Published ${plan.payload.name}@${plan.payload.version} (${result.releaseId})`,
|
||||
);
|
||||
} else {
|
||||
spinner?.succeed(
|
||||
`Update submitted for ${plan.payload.name}@${plan.payload.version}; publication status was not reported.`,
|
||||
);
|
||||
}
|
||||
printPackageInspectorFindings(result);
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
@@ -82,6 +82,7 @@ describe("cmdPublish", () => {
|
||||
ok: true,
|
||||
skillId: "skill_1",
|
||||
versionId: "ver_2",
|
||||
publicationStatus: "published",
|
||||
});
|
||||
|
||||
const result = await cmdPublish(makeOpts(workdir), "metadata-update", {
|
||||
@@ -117,6 +118,7 @@ describe("cmdPublish", () => {
|
||||
ok: true,
|
||||
skillId: "skill_1",
|
||||
versionId: "ver_1",
|
||||
publicationStatus: "published",
|
||||
});
|
||||
|
||||
const result = await cmdPublish(makeOpts(workdir), "new-skill", {});
|
||||
@@ -160,7 +162,67 @@ describe("cmdPublish", () => {
|
||||
attemptId: "attempt_1",
|
||||
});
|
||||
expect(uiMocks.spinner.succeed).toHaveBeenCalledWith(
|
||||
"OK. Uploaded pending-skill@1.0.0; security checks are pending before it becomes public (ver_pending)",
|
||||
"Update submitted for pending-skill@1.0.0; pending security scans before it becomes public.",
|
||||
);
|
||||
} finally {
|
||||
await rm(workdir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("makes pending publication unambiguous in json output", async () => {
|
||||
const workdir = await makeTmpWorkdir();
|
||||
const writeSpy = vi.spyOn(process.stdout, "write").mockImplementation(() => true);
|
||||
try {
|
||||
const folder = join(workdir, "json-pending-skill");
|
||||
await mkdir(folder, { recursive: true });
|
||||
await writeFile(join(folder, "SKILL.md"), "# Skill\n", "utf8");
|
||||
httpMocks.apiRequest.mockRejectedValueOnce(
|
||||
new Error("Skill not found or unavailable to this account."),
|
||||
);
|
||||
httpMocks.apiRequestForm.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
skillId: "skill_1",
|
||||
versionId: "ver_pending",
|
||||
publicationStatus: "pending",
|
||||
attemptId: "attempt_1",
|
||||
});
|
||||
|
||||
await cmdPublish(makeOpts(workdir), "json-pending-skill", { json: true });
|
||||
|
||||
expect(uiMocks.spinner.succeed).not.toHaveBeenCalled();
|
||||
expect(writeSpy).toHaveBeenCalledTimes(1);
|
||||
const output = JSON.parse(String(writeSpy.mock.calls[0]?.[0] ?? ""));
|
||||
expect(output).toMatchObject({
|
||||
status: "pending-publication",
|
||||
slug: "json-pending-skill",
|
||||
version: "1.0.0",
|
||||
publicationStatus: "pending",
|
||||
attemptId: "attempt_1",
|
||||
});
|
||||
expect(output.status).not.toBe("published");
|
||||
} finally {
|
||||
writeSpy.mockRestore();
|
||||
await rm(workdir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("does not claim a skill was published when the server omits publication status", async () => {
|
||||
const workdir = await makeTmpWorkdir();
|
||||
try {
|
||||
const folder = join(workdir, "unknown-status-skill");
|
||||
await mkdir(folder, { recursive: true });
|
||||
await writeFile(join(folder, "SKILL.md"), "# Skill\n", "utf8");
|
||||
httpMocks.apiRequestForm.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
skillId: "skill_1",
|
||||
versionId: "ver_1",
|
||||
});
|
||||
|
||||
const result = await cmdPublish(makeOpts(workdir), "unknown-status-skill", {});
|
||||
|
||||
expect(result.status).toBe("submitted");
|
||||
expect(uiMocks.spinner.succeed).toHaveBeenCalledWith(
|
||||
"Update submitted for unknown-status-skill@1.0.0; publication status was not reported.",
|
||||
);
|
||||
} finally {
|
||||
await rm(workdir, { recursive: true, force: true });
|
||||
@@ -181,6 +243,7 @@ describe("cmdPublish", () => {
|
||||
ok: true,
|
||||
skillId: "skill_1",
|
||||
versionId: "ver_2",
|
||||
publicationStatus: "published",
|
||||
});
|
||||
|
||||
const result = await cmdPublish(makeOpts(workdir), "changed-skill", {});
|
||||
@@ -210,6 +273,7 @@ describe("cmdPublish", () => {
|
||||
ok: true,
|
||||
skillId: "skill_1",
|
||||
versionId: "ver_2",
|
||||
publicationStatus: "published",
|
||||
});
|
||||
|
||||
const result = await cmdPublish(makeOpts(workdir), "explicit-version", {
|
||||
|
||||
@@ -18,7 +18,7 @@ import { normalizeGitHubRepo } from "./github.js";
|
||||
|
||||
type SkillPublishResult = {
|
||||
ok: true;
|
||||
status: "unchanged" | "would-publish" | "published" | "pending-publication";
|
||||
status: "unchanged" | "would-publish" | "submitted" | "published" | "pending-publication";
|
||||
slug: string;
|
||||
displayName: string;
|
||||
folder: string;
|
||||
@@ -200,9 +200,14 @@ export async function cmdPublish(
|
||||
ApiV1PublishResponseSchema,
|
||||
);
|
||||
|
||||
const isPendingPublication = result.publicationStatus === "pending";
|
||||
const publicationStatus = result.publicationStatus;
|
||||
const publishResult = buildPublishResult({
|
||||
status: isPendingPublication ? "pending-publication" : "published",
|
||||
status:
|
||||
publicationStatus === "pending"
|
||||
? "pending-publication"
|
||||
: publicationStatus === "published"
|
||||
? "published"
|
||||
: "submitted",
|
||||
slug,
|
||||
displayName,
|
||||
folder,
|
||||
@@ -214,11 +219,17 @@ export async function cmdPublish(
|
||||
publicationStatus: result.publicationStatus,
|
||||
attemptId: result.attemptId,
|
||||
});
|
||||
spinner?.succeed(
|
||||
isPendingPublication
|
||||
? `OK. Uploaded ${slug}@${version}; security checks are pending before it becomes public (${result.versionId})`
|
||||
: `OK. Published ${slug}@${version} (${result.versionId})`,
|
||||
);
|
||||
if (publicationStatus === "pending") {
|
||||
spinner?.succeed(
|
||||
`Update submitted for ${slug}@${version}; pending security scans before it becomes public.`,
|
||||
);
|
||||
} else if (publicationStatus === "published") {
|
||||
spinner?.succeed(`OK. Published ${slug}@${version} (${result.versionId})`);
|
||||
} else {
|
||||
spinner?.succeed(
|
||||
`Update submitted for ${slug}@${version}; publication status was not reported.`,
|
||||
);
|
||||
}
|
||||
writePublishJsonIfRequested(options.json, publishResult);
|
||||
return publishResult;
|
||||
} catch (error) {
|
||||
|
||||
@@ -345,7 +345,9 @@ describe("cmdSync", () => {
|
||||
process.stdout.write("child publish output\n");
|
||||
}
|
||||
return {
|
||||
status: "published",
|
||||
version: (options as { version?: string } | undefined)?.version ?? "1.0.0",
|
||||
publicationStatus: "published",
|
||||
};
|
||||
});
|
||||
|
||||
@@ -368,6 +370,97 @@ describe("cmdSync", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps pending sync submissions out of the published json summary", async () => {
|
||||
interactive = false;
|
||||
const stdoutWrite = vi.spyOn(process.stdout, "write").mockImplementation(() => true);
|
||||
mockApiRequest.mockImplementation(async (_registry: string, args: { path?: string }) => {
|
||||
if (args.path?.startsWith("/api/v1/resolve?")) {
|
||||
const u = new URL(`https://x.test${args.path}`);
|
||||
const slug = u.searchParams.get("slug");
|
||||
if (slug === "new-skill") throw new Error("Skill not found");
|
||||
if (slug === "synced-skill") {
|
||||
return { match: { version: "1.2.3" }, latestVersion: { version: "1.2.3" } };
|
||||
}
|
||||
if (slug === "update-skill") {
|
||||
return { match: null, latestVersion: { version: "1.0.0" } };
|
||||
}
|
||||
}
|
||||
throw new Error(`Unexpected apiRequest: ${String(args.path)}`);
|
||||
});
|
||||
mockCmdPublish.mockImplementation(
|
||||
(_opts: unknown, _folder: unknown, options?: { slug?: string; version?: string }) =>
|
||||
options?.slug === "new-skill"
|
||||
? {
|
||||
status: "pending-publication",
|
||||
version: options.version,
|
||||
publicationStatus: "pending",
|
||||
}
|
||||
: {
|
||||
status: "published",
|
||||
version: options?.version,
|
||||
publicationStatus: "published",
|
||||
},
|
||||
);
|
||||
|
||||
let output = "";
|
||||
try {
|
||||
await cmdSync(makeOpts(), { root: ["/scan"], all: true, json: true }, false);
|
||||
output = String(stdoutWrite.mock.calls[0]?.[0] ?? "");
|
||||
} finally {
|
||||
stdoutWrite.mockRestore();
|
||||
}
|
||||
|
||||
const parsed = JSON.parse(output);
|
||||
expect(parsed.summary).toMatchObject({ published: 1, submitted: 1, failed: 0 });
|
||||
expect(parsed.published).toEqual([
|
||||
expect.objectContaining({ slug: "update-skill", version: "1.0.1" }),
|
||||
]);
|
||||
expect(parsed.submitted).toEqual([
|
||||
expect.objectContaining({
|
||||
slug: "new-skill",
|
||||
version: "1.0.0",
|
||||
status: "pending-publication",
|
||||
publicationStatus: "pending",
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
it("does not call pending sync submissions published in human summaries", async () => {
|
||||
interactive = false;
|
||||
mockApiRequest.mockImplementation(async (_registry: string, args: { path?: string }) => {
|
||||
if (args.path?.startsWith("/api/v1/resolve?")) {
|
||||
const u = new URL(`https://x.test${args.path}`);
|
||||
const slug = u.searchParams.get("slug");
|
||||
if (slug === "new-skill") throw new Error("Skill not found");
|
||||
if (slug === "synced-skill") {
|
||||
return { match: { version: "1.2.3" }, latestVersion: { version: "1.2.3" } };
|
||||
}
|
||||
if (slug === "update-skill") {
|
||||
return { match: null, latestVersion: { version: "1.0.0" } };
|
||||
}
|
||||
}
|
||||
throw new Error(`Unexpected apiRequest: ${String(args.path)}`);
|
||||
});
|
||||
mockCmdPublish.mockImplementation(
|
||||
(_opts: unknown, _folder: unknown, options?: { slug?: string; version?: string }) =>
|
||||
options?.slug === "new-skill"
|
||||
? {
|
||||
status: "pending-publication",
|
||||
version: options.version,
|
||||
publicationStatus: "pending",
|
||||
}
|
||||
: {
|
||||
status: "published",
|
||||
version: options?.version,
|
||||
publicationStatus: "published",
|
||||
},
|
||||
);
|
||||
|
||||
await cmdSync(makeOpts(), { root: ["/scan"], all: true }, false);
|
||||
|
||||
expect(mockOutro).toHaveBeenCalledWith("Published 1 skill(s). Submitted 1 update(s).");
|
||||
});
|
||||
|
||||
it("does not report a raced unchanged publish as uploaded", async () => {
|
||||
interactive = false;
|
||||
const stdoutWrite = vi.spyOn(process.stdout, "write").mockImplementation(() => true);
|
||||
|
||||
@@ -197,6 +197,7 @@ export async function cmdSync(opts: GlobalOpts, options: SyncOptions, inputAllow
|
||||
alreadySynced: synced.map(formatSyncedJson),
|
||||
wouldPublish,
|
||||
published: [],
|
||||
submitted: [],
|
||||
failed: [],
|
||||
}),
|
||||
);
|
||||
@@ -209,6 +210,13 @@ export async function cmdSync(opts: GlobalOpts, options: SyncOptions, inputAllow
|
||||
const tags = options.tags ?? "latest";
|
||||
const failedUploads: Array<{ slug: string; message: string }> = [];
|
||||
const published: Array<{ slug: string; folder: string; version: string }> = [];
|
||||
const submitted: Array<{
|
||||
slug: string;
|
||||
folder: string;
|
||||
version: string;
|
||||
status: "pending-publication" | "submitted";
|
||||
publicationStatus?: "pending";
|
||||
}> = [];
|
||||
const racedNoOps: Array<{ slug: string; folder: string; version: string }> = [];
|
||||
|
||||
for (const { skill, source } of plannedPublishes) {
|
||||
@@ -254,11 +262,20 @@ export async function cmdSync(opts: GlobalOpts, options: SyncOptions, inputAllow
|
||||
});
|
||||
continue;
|
||||
}
|
||||
published.push({
|
||||
const output = {
|
||||
slug: skill.slug,
|
||||
folder: skill.folder,
|
||||
version: result?.version ?? publishVersion,
|
||||
});
|
||||
};
|
||||
if (result?.status === "published") {
|
||||
published.push(output);
|
||||
} else {
|
||||
submitted.push({
|
||||
...output,
|
||||
status: result?.status === "pending-publication" ? "pending-publication" : "submitted",
|
||||
...(result?.publicationStatus === "pending" ? { publicationStatus: "pending" } : {}),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
failedUploads.push({ slug: skill.slug, message: formatError(error) });
|
||||
}
|
||||
@@ -277,6 +294,7 @@ export async function cmdSync(opts: GlobalOpts, options: SyncOptions, inputAllow
|
||||
alreadySynced: [...synced.map(formatSyncedJson), ...racedNoOps],
|
||||
wouldPublish: [],
|
||||
published,
|
||||
submitted,
|
||||
failed: failedUploads,
|
||||
}),
|
||||
);
|
||||
@@ -290,9 +308,7 @@ export async function cmdSync(opts: GlobalOpts, options: SyncOptions, inputAllow
|
||||
20,
|
||||
),
|
||||
);
|
||||
outro(
|
||||
`Published ${published.length} of ${selected.length} skill(s). ${failedUploads.length} failed.`,
|
||||
);
|
||||
outro(formatSyncPublishSummary({ published, submitted, selected, failedUploads }));
|
||||
process.exitCode = 1;
|
||||
return;
|
||||
}
|
||||
@@ -309,6 +325,7 @@ export async function cmdSync(opts: GlobalOpts, options: SyncOptions, inputAllow
|
||||
alreadySynced: [...synced.map(formatSyncedJson), ...racedNoOps],
|
||||
wouldPublish: [],
|
||||
published,
|
||||
submitted,
|
||||
failed: [],
|
||||
}),
|
||||
);
|
||||
@@ -316,11 +333,9 @@ export async function cmdSync(opts: GlobalOpts, options: SyncOptions, inputAllow
|
||||
}
|
||||
|
||||
if (racedNoOps.length > 0) {
|
||||
outro(
|
||||
`Published ${published.length} of ${selected.length} skill(s). ${racedNoOps.length} already synced.`,
|
||||
);
|
||||
outro(formatSyncPublishSummary({ published, submitted, selected, racedNoOps }));
|
||||
} else {
|
||||
outro(`Published ${published.length} skill(s).`);
|
||||
outro(formatSyncPublishSummary({ published, submitted }));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,6 +361,7 @@ function writeNoActionOutput(params: {
|
||||
alreadySynced: params.synced.map(formatSyncedJson),
|
||||
wouldPublish: [],
|
||||
published: [],
|
||||
submitted: [],
|
||||
failed: [],
|
||||
}),
|
||||
);
|
||||
@@ -479,6 +495,13 @@ function buildSyncJsonOutput(params: {
|
||||
alreadySynced: Array<{ slug: string; folder: string; version: string }>;
|
||||
wouldPublish: Array<ReturnType<typeof formatPublishJson>>;
|
||||
published: Array<{ slug: string; folder: string; version: string }>;
|
||||
submitted: Array<{
|
||||
slug: string;
|
||||
folder: string;
|
||||
version: string;
|
||||
status: "pending-publication" | "submitted";
|
||||
publicationStatus?: "pending";
|
||||
}>;
|
||||
failed: Array<{ slug: string; message: string }>;
|
||||
}) {
|
||||
const skipped = params.duplicates.map((duplicate) => ({
|
||||
@@ -495,18 +518,41 @@ function buildSyncJsonOutput(params: {
|
||||
summary: {
|
||||
wouldPublish: params.wouldPublish.length,
|
||||
published: params.published.length,
|
||||
submitted: params.submitted.length,
|
||||
alreadySynced: params.alreadySynced.length,
|
||||
skipped: skipped.length,
|
||||
failed: params.failed.length,
|
||||
},
|
||||
wouldPublish: params.wouldPublish,
|
||||
published: params.published,
|
||||
submitted: params.submitted,
|
||||
alreadySynced: params.alreadySynced,
|
||||
skipped,
|
||||
failed: params.failed,
|
||||
};
|
||||
}
|
||||
|
||||
function formatSyncPublishSummary(params: {
|
||||
published: unknown[];
|
||||
submitted: unknown[];
|
||||
selected?: unknown[];
|
||||
racedNoOps?: unknown[];
|
||||
failedUploads?: unknown[];
|
||||
}) {
|
||||
const selectedSuffix = params.selected ? ` of ${params.selected.length}` : "";
|
||||
const parts = [`Published ${params.published.length}${selectedSuffix} skill(s).`];
|
||||
if (params.submitted.length > 0) {
|
||||
parts.push(`Submitted ${params.submitted.length} update(s).`);
|
||||
}
|
||||
if (params.racedNoOps && params.racedNoOps.length > 0) {
|
||||
parts.push(`${params.racedNoOps.length} already synced.`);
|
||||
}
|
||||
if (params.failedUploads && params.failedUploads.length > 0) {
|
||||
parts.push(`${params.failedUploads.length} failed.`);
|
||||
}
|
||||
return parts.join(" ");
|
||||
}
|
||||
|
||||
function writeSyncJson(value: unknown) {
|
||||
process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user