From 76177722b78d6531260430fad6e92d9b1b6cf6e3 Mon Sep 17 00:00:00 2001 From: Jeremy Sfez Date: Thu, 9 Apr 2026 11:07:34 +0200 Subject: [PATCH] feat(cli): use new endpoint in build command --- packages/api-client/src/schema.ts | 108 +++------ packages/cli/e2e/{builds.js => build.js} | 48 ++-- packages/cli/package.json | 2 +- .../cli/src/commands/{builds.ts => build.ts} | 207 +++++++----------- packages/cli/src/index.ts | 4 +- packages/gitlab/src/statuses.ts | 24 +- skills/argos-cli/SKILL.md | 24 +- .../references/{builds.md => build.md} | 63 +++--- 8 files changed, 191 insertions(+), 289 deletions(-) rename packages/cli/e2e/{builds.js => build.js} (76%) rename packages/cli/src/commands/{builds.ts => build.ts} (58%) rename skills/argos-cli/references/{builds.md => build.md} (67%) diff --git a/packages/api-client/src/schema.ts b/packages/api-client/src/schema.ts index 99b1af5a..b48c8e1a 100644 --- a/packages/api-client/src/schema.ts +++ b/packages/api-client/src/schema.ts @@ -43,7 +43,7 @@ export interface paths { path?: never; cookie?: never; }; - get: operations["getBuild"]; + get?: never; put: operations["updateBuild"]; post?: never; delete?: never; @@ -52,14 +52,14 @@ export interface paths { patch?: never; trace?: never; }; - "/builds/{buildId}/diffs": { + "/project": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - get: operations["getBuildDiffs"]; + get: operations["getAuthProject"]; put?: never; post?: never; delete?: never; @@ -68,14 +68,14 @@ export interface paths { patch?: never; trace?: never; }; - "/project": { + "/projects/{owner}/{project}/builds": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - get: operations["getAuthProject"]; + get: operations["getProjectBuilds"]; put?: never; post?: never; delete?: never; @@ -84,14 +84,14 @@ export interface paths { patch?: never; trace?: never; }; - "/project/builds": { + "/projects/{owner}/{project}/builds/{buildNumber}": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - get: operations["getAuthProjectBuilds"]; + get: operations["getBuildByNumber"]; put?: never; post?: never; delete?: never; @@ -100,14 +100,14 @@ export interface paths { patch?: never; trace?: never; }; - "/project/builds/{buildNumber}": { + "/projects/{owner}/{project}/builds/{buildNumber}/diffs": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - get: operations["getAuthBuildByNumber"]; + get: operations["getBuildDiffs"]; put?: never; post?: never; delete?: never; @@ -589,6 +589,12 @@ export interface components { /** @description Project */ Project: { id: string; + /** @description Account that owns the project */ + account: { + id: string; + slug: string; + }; + name: string; defaultBaseBranch: string; hasRemoteContentAccess: boolean; }; @@ -801,65 +807,6 @@ export interface operations { }; }; }; - getBuild: { - parameters: { - query?: never; - header?: never; - path: { - /** @description A unique identifier for the build */ - buildId: components["schemas"]["BuildId"]; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Build */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["Build"]; - }; - }; - /** @description Invalid parameters */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["Error"]; - }; - }; - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["Error"]; - }; - }; - /** @description Not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["Error"]; - }; - }; - /** @description Server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["Error"]; - }; - }; - }; - }; updateBuild: { parameters: { query?: never; @@ -956,11 +903,17 @@ export interface operations { perPage?: string; /** @description Page number */ page?: string; + /** @description Only include diffs that need review */ + needsReview?: boolean; }; header?: never; path: { - /** @description A unique identifier for the build */ - buildId: components["schemas"]["BuildId"]; + /** @description The account slug (owner) */ + owner: string; + /** @description The project name */ + project: string; + /** @description The build number */ + buildNumber: number; }; cookie?: never; }; @@ -1054,7 +1007,7 @@ export interface operations { }; }; }; - getAuthProjectBuilds: { + getProjectBuilds: { parameters: { query?: { /** @description Number of items per page (max 100) */ @@ -1067,7 +1020,12 @@ export interface operations { distinctName?: string; }; header?: never; - path?: never; + path: { + /** @description The account slug (owner) */ + owner: string; + /** @description The project name */ + project: string; + }; cookie?: never; }; requestBody?: never; @@ -1113,11 +1071,15 @@ export interface operations { }; }; }; - getAuthBuildByNumber: { + getBuildByNumber: { parameters: { query?: never; header?: never; path: { + /** @description The account slug (owner) */ + owner: string; + /** @description The project name */ + project: string; /** @description The build number */ buildNumber: number; }; diff --git a/packages/cli/e2e/builds.js b/packages/cli/e2e/build.js similarity index 76% rename from packages/cli/e2e/builds.js rename to packages/cli/e2e/build.js index 434bbea2..1086705e 100644 --- a/packages/cli/e2e/builds.js +++ b/packages/cli/e2e/build.js @@ -1,11 +1,11 @@ /** - * E2E tests for `argos builds` commands. + * E2E tests for `argos build` commands. * Requires ARGOS_TOKEN env var. * Optional: ARGOS_API_BASE_URL env var. * * Usage: - * ARGOS_TOKEN=xxx node e2e/builds.js - * ARGOS_TOKEN=xxx ARGOS_API_BASE_URL=https://api.argos-ci.dev:4001/v2 NODE_OPTIONS=--use-system-ca pnpm -C packages/cli exec node e2e/builds.js + * ARGOS_TOKEN=xxx node e2e/build.js + * ARGOS_TOKEN=xxx ARGOS_API_BASE_URL=https://api.argos-ci.dev:4001/v2 NODE_OPTIONS=--use-system-ca pnpm -C packages/cli exec node e2e/build.js */ import { assert, run } from "./utils.js"; @@ -16,7 +16,7 @@ const buildNumber = process.env.ARGOS_BUILD_NUMBER || "28022"; if (!token) { console.error( - "Usage: ARGOS_TOKEN=xxx [ARGOS_API_BASE_URL=] node e2e/builds.js", + "Usage: ARGOS_TOKEN=xxx [ARGOS_API_BASE_URL=] node e2e/build.js", ); process.exit(1); } @@ -29,10 +29,10 @@ const baseEnv = apiBaseURL ? envWith({ ARGOS_API_BASE_URL: apiBaseURL }) : process.env; -console.log("\n`builds get` failing commands:"); +console.log("\n`build get` failing commands:"); try { - run(["builds", "get", "1"], { ...baseEnv, ARGOS_TOKEN: "" }); + run(["build", "get", "1"], { ...baseEnv, ARGOS_TOKEN: "" }); assert(false, "Missing token with build number should exit with code 1"); } catch (err) { assert(err.status !== 0, "Exit code 1 when no token for build number"); @@ -45,7 +45,7 @@ try { try { run( [ - "builds", + "build", "get", "https://app.argos-ci.com/argos-ci/argos-javascript/builds/1", ], @@ -61,7 +61,7 @@ try { } try { - run(["builds", "get", "999999"], { + run(["build", "get", "999999"], { ...baseEnv, ARGOS_TOKEN: token, }); @@ -75,7 +75,7 @@ try { } try { - run(["builds", "get", "not-a-number"], { + run(["build", "get", "not-a-number"], { ...baseEnv, ARGOS_TOKEN: token, }); @@ -88,15 +88,15 @@ try { ); } -console.log("\n`builds get` successful commands:"); -const buildByNumberJsonOutput = run(["builds", "get", buildNumber, "--json"], { +console.log("\n`build get` successful commands:"); +const buildByNumberJsonOutput = run(["build", "get", buildNumber, "--json"], { ...baseEnv, ARGOS_TOKEN: token, }); const buildByNumberJson = JSON.parse(buildByNumberJsonOutput.stdout); const buildUrl = buildByNumberJson.url; -const buildByNumberHumanOutput = run(["builds", "get", buildNumber], { +const buildByNumberHumanOutput = run(["build", "get", buildNumber], { ...baseEnv, ARGOS_TOKEN: token, }); @@ -119,7 +119,7 @@ assert( "Returns the requested build number", ); -const buildByUrlJsonOutput = run(["builds", "get", "--json", buildUrl], { +const buildByUrlJsonOutput = run(["build", "get", "--json", buildUrl], { ...baseEnv, ARGOS_TOKEN: token, }); @@ -129,10 +129,10 @@ assert( "accepts an Argos build URL", ); -console.log("\n`builds snapshots` failing commands:"); +console.log("\n`build snapshots` failing commands:"); try { - run(["builds", "snapshots", "1"], { ...baseEnv, ARGOS_TOKEN: "" }); + run(["build", "snapshots", "1"], { ...baseEnv, ARGOS_TOKEN: "" }); assert( false, "Missing token for snapshots with build number should exit with code 1", @@ -148,8 +148,8 @@ try { ); } -console.log("\n`builds snapshots` successful commands:"); -const buildSnapshots = run(["builds", "snapshots", buildNumber], { +console.log("\n`build snapshots` successful commands:"); +const buildSnapshots = run(["build", "snapshots", buildNumber], { ...baseEnv, ARGOS_TOKEN: token, }); @@ -160,7 +160,7 @@ assert( assert(buildSnapshots.stdout.includes("Summary:"), "Prints the build Summary"); const buildSnapshotsJsonOutput = run( - ["builds", "snapshots", buildNumber, "--json"], + ["build", "snapshots", buildNumber, "--json"], { ...baseEnv, ARGOS_TOKEN: token, @@ -168,13 +168,10 @@ const buildSnapshotsJsonOutput = run( ); const buildSnapshotsJson = JSON.parse(buildSnapshotsJsonOutput.stdout); assert(Array.isArray(buildSnapshotsJson), "Returns an array in JSON mode"); -assert( - Boolean(buildSnapshotsJson[0].base.id), - "Returns structured snapshot data", -); +assert(Boolean(buildSnapshotsJson[0].id), "Returns structured snapshot data"); const snapshotsNeedsReviewJsonOutput = run( - ["builds", "snapshots", buildNumber, "--needs-review", "--json"], + ["build", "snapshots", buildNumber, "--needs-review", "--json"], { ...baseEnv, ARGOS_TOKEN: token, @@ -183,8 +180,7 @@ const snapshotsNeedsReviewJsonOutput = run( const snapshotsNeedingReview = JSON.parse( snapshotsNeedsReviewJsonOutput.stdout, ); -assert(Array.isArray(snapshotsNeedingReview), "Returns an array in JSON mode"); assert( - snapshotsNeedingReview.length === 0, - "Returns an empty array when there are no snapshots to review", + Array.isArray(snapshotsNeedingReview), + "Returns an array in JSON mode for needs-review filter", ); diff --git a/packages/cli/package.json b/packages/cli/package.json index a40ec671..a701c113 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -42,7 +42,7 @@ }, "scripts": { "build": "tsdown", - "e2e": "node e2e/upload.js && node e2e/skip.js && node e2e/builds.js", + "e2e": "node e2e/upload.js && node e2e/skip.js && node e2e/build.js", "check-types": "tsc", "check-format": "prettier --check --ignore-unknown --ignore-path=../../.gitignore --ignore-path=../../.prettierignore .", "lint": "eslint ." diff --git a/packages/cli/src/commands/builds.ts b/packages/cli/src/commands/build.ts similarity index 58% rename from packages/cli/src/commands/builds.ts rename to packages/cli/src/commands/build.ts index caec7366..38033abe 100644 --- a/packages/cli/src/commands/builds.ts +++ b/packages/cli/src/commands/build.ts @@ -5,11 +5,10 @@ import type { ArgosAPISchema } from "@argos-ci/api-client"; import { tokenOption, type TokenOption } from "../options"; type Build = ArgosAPISchema.components["schemas"]["Build"]; +type Project = ArgosAPISchema.components["schemas"]["Project"]; type SnapshotDiff = ArgosAPISchema.components["schemas"]["SnapshotDiff"]; type SnapshotDiffStatus = SnapshotDiff["status"]; -type SimplifiedBuildStatus = "failure" | "success" | "pending"; - function getToken(options: TokenOption): string { const token = options.token ?? process.env["ARGOS_TOKEN"]; if (!token) { @@ -32,26 +31,8 @@ function createBuildsClient(options: TokenOption) { }); } -function getSimplifiedStatus(build: Build): SimplifiedBuildStatus { - switch (build.status) { - case "accepted": - case "no-changes": - return "success"; - - case "rejected": - case "changes-detected": - case "expired": - case "error": - case "aborted": - return "failure"; - - default: - return "pending"; - } -} - function isBuildPending(build: Build): boolean { - return getSimplifiedStatus(build) === "pending"; + return build.status === "pending" || build.status === "progress"; } function parseBuildReferenceOrExit(buildReference: string): number { @@ -63,87 +44,19 @@ function parseBuildReferenceOrExit(buildReference: string): number { return parsedBuildNumber; } - const urlMatch = buildReference.match(/\/builds\/(\d+)(?:\/?$|[?#])/); + const urlMatch = buildReference.match( + /^https:\/\/app\.argos-ci\.(?:com|dev(?::\d+)?)\/.+\/builds\/(\d+)(?:\/?$|[?#])/, + ); if (urlMatch) { return Number(urlMatch[1]); } console.error( - `Error: Build reference must be a valid build number or Argos build URL, got "${buildReference}".`, + `Error: Build reference must be a valid build number or Argos build URL (https://app.argos-ci.com/.../builds/), got "${buildReference}".`, ); process.exit(1); } -function getBuildJSON(build: Build) { - return { - id: build.id, - number: build.number, - status: getSimplifiedStatus(build), - rawStatus: build.status, - conclusion: build.conclusion, - branch: build.head?.branch ?? null, - commit: build.head?.sha ?? null, - baseBranch: build.base?.branch ?? null, - baseCommit: build.base?.sha ?? null, - url: build.url, - stats: build.stats, - testReport: build.metadata?.testReport ?? null, - notification: build.notification, - }; -} - -function getSnapshotJSON(diff: SnapshotDiff, build: Build) { - return { - id: diff.id, - name: diff.name, - status: diff.status, - score: diff.score, - buildUrl: build.url, - reviewUrl: `${build.url}/${diff.id}`, - diffImageUrl: diff.url, - group: diff.group, - parentName: diff.parentName, - base: diff.base - ? { - id: diff.base.id, - name: diff.base.name, - imageUrl: diff.base.url, - contentType: diff.base.contentType, - width: diff.base.width, - height: diff.base.height, - pageUrl: diff.base.metadata?.url ?? null, - previewUrl: diff.base.metadata?.previewUrl ?? null, - viewport: diff.base.metadata?.viewport ?? null, - browser: diff.base.metadata?.browser ?? null, - automationLibrary: diff.base.metadata?.automationLibrary ?? null, - sdk: diff.base.metadata?.sdk ?? null, - test: diff.base.metadata?.test ?? null, - story: diff.base.metadata?.story ?? null, - tags: diff.base.metadata?.tags ?? null, - } - : null, - head: diff.head - ? { - id: diff.head.id, - name: diff.head.name, - imageUrl: diff.head.url, - contentType: diff.head.contentType, - width: diff.head.width, - height: diff.head.height, - pageUrl: diff.head.metadata?.url ?? null, - previewUrl: diff.head.metadata?.previewUrl ?? null, - viewport: diff.head.metadata?.viewport ?? null, - browser: diff.head.metadata?.browser ?? null, - automationLibrary: diff.head.metadata?.automationLibrary ?? null, - sdk: diff.head.metadata?.sdk ?? null, - test: diff.head.metadata?.test ?? null, - story: diff.head.metadata?.story ?? null, - tags: diff.head.metadata?.tags ?? null, - } - : null, - }; -} - function formatValue(value: string | number | null | undefined): string { if (value === null || value === undefined || value === "") { return "-"; @@ -192,17 +105,16 @@ function formatSnapshotSummary(diffs: SnapshotDiff[]): string { } function printBuild(build: Build) { - const buildJSON = getBuildJSON(build); const lines = [ - `Build #${buildJSON.number}`, - `Status: ${buildJSON.status} (${buildJSON.rawStatus})`, - `Snapshots: ${formatStats(buildJSON.stats)}`, - `Conclusion: ${formatValue(buildJSON.conclusion)}`, - `Branch: ${formatValue(buildJSON.branch)}`, - `Commit: ${formatValue(buildJSON.commit)}`, - `Base branch: ${formatValue(buildJSON.baseBranch)}`, - `Base commit: ${formatValue(buildJSON.baseCommit)}`, - `URL: ${buildJSON.url}`, + `Build #${build.number}`, + `Status: ${build.status}`, + `Snapshots: ${formatStats(build.stats)}`, + `Conclusion: ${formatValue(build.conclusion)}`, + `Branch: ${formatValue(build.head?.branch)}`, + `Commit: ${formatValue(build.head?.sha)}`, + `Base branch: ${formatValue(build.base?.branch)}`, + `Base commit: ${formatValue(build.base?.sha)}`, + `URL: ${build.url}`, ]; console.log(lines.join("\n")); @@ -220,15 +132,14 @@ function printSnapshots(diffs: SnapshotDiff[], build: Build) { `Summary: ${formatSnapshotSummary(diffs)}`, "", ...diffs.flatMap((diff) => { - const snapshot = getSnapshotJSON(diff, build); return [ - `${snapshot.name} [${snapshot.status}]`, - ` Review: ${snapshot.reviewUrl}`, - ` Diff image: ${formatValue(snapshot.diffImageUrl)}`, - ` Base image: ${formatValue(snapshot.base?.imageUrl)}`, - ` Head image: ${formatValue(snapshot.head?.imageUrl)}`, - ` Score: ${formatValue(snapshot.score)}`, - ` Group: ${formatValue(snapshot.group)}`, + `${diff.name} [${diff.status}]`, + ` Review: ${build.url}/${diff.id}`, + ` Mask: ${formatValue(diff.url)}`, + ` Base file: ${formatValue(diff.base?.url)}`, + ` Head file: ${formatValue(diff.head?.url)}`, + ` Score: ${formatValue(diff.score)}`, + ` Group: ${formatValue(diff.group)}`, "", ]; }), @@ -239,7 +150,8 @@ function printSnapshots(diffs: SnapshotDiff[], build: Build) { async function fetchAllDiffs( client: ReturnType, - buildId: string, + project: Project, + buildNumber: number, options?: { needsReview?: boolean }, ): Promise { const results: SnapshotDiff[] = []; @@ -252,12 +164,19 @@ async function fetchAllDiffs( perPage: String(perPage), ...(options?.needsReview ? ({ needsReview: true } as const) : {}), }; - const { data, error } = await client.GET("/builds/{buildId}/diffs", { - params: { - path: { buildId }, - query: query as never, + const { data, error } = await client.GET( + "/projects/{owner}/{project}/builds/{buildNumber}/diffs", + { + params: { + path: { + owner: project.account.slug, + project: project.name, + buildNumber, + }, + query: query as never, + }, }, - }); + ); if (error || !data) { if (error) { @@ -277,14 +196,40 @@ async function fetchAllDiffs( return results; } +async function fetchProject( + client: ReturnType, +): Promise { + const { data, error } = await client.GET("/project"); + + if (error) { + throwAPIError(error); + } + + if (!data) { + console.error("Error: Unexpected empty response from API."); + process.exit(1); + } + + return data; +} + async function fetchBuildByNumber( client: ReturnType, + project: Project, buildNumber: number, errorLabel: string, ): Promise { const { data, error, response } = await client.GET( - "/project/builds/{buildNumber}", - { params: { path: { buildNumber } } }, + "/projects/{owner}/{project}/builds/{buildNumber}", + { + params: { + path: { + owner: project.account.slug, + project: project.name, + buildNumber, + }, + }, + }, ); if (error) { @@ -303,15 +248,15 @@ async function fetchBuildByNumber( return data; } -export function buildsCommand(program: Command) { - const builds = program.command("builds").description("Manage Argos builds"); +export function buildCommand(program: Command) { + const build = program.command("build").description("Manage Argos builds"); const createJsonOption = () => new Option( "--json", "Output machine-readable JSON instead of human-readable text", ); - builds + build .command("get") .description("Fetch build metadata") .argument("", "Build number or Argos build URL") @@ -324,20 +269,22 @@ export function buildsCommand(program: Command) { ) => { const buildNumber = parseBuildReferenceOrExit(buildReference); const client = createBuildsClient(options); + const project = await fetchProject(client); const build = await fetchBuildByNumber( client, + project, buildNumber, buildReference, ); if (options.json) { - console.log(JSON.stringify(getBuildJSON(build), null, 2)); + console.log(JSON.stringify(build, null, 2)); return; } printBuild(build); }, ); - builds + build .command("snapshots") .description("Fetch snapshot diffs for a build") .argument("", "Build number or Argos build URL") @@ -351,8 +298,10 @@ export function buildsCommand(program: Command) { ) => { const buildNumber = parseBuildReferenceOrExit(buildReference); const client = createBuildsClient(options); + const project = await fetchProject(client); const build = await fetchBuildByNumber( client, + project, buildNumber, buildReference, ); @@ -371,18 +320,12 @@ export function buildsCommand(program: Command) { return; } - const diffs = await fetchAllDiffs(client, build.id, { + const diffs = await fetchAllDiffs(client, project, build.number, { needsReview: Boolean(options.needsReview), }); if (options.json) { - console.log( - JSON.stringify( - diffs.map((diff) => getSnapshotJSON(diff, build)), - null, - 2, - ), - ); + console.log(JSON.stringify(diffs, null, 2)); return; } diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index c615733e..88f2e741 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -5,7 +5,7 @@ import { program } from "commander"; import { uploadCommand } from "./commands/upload"; import { finalizeCommand } from "./commands/finalize"; import { skipCommand } from "./commands/skip"; -import { buildsCommand } from "./commands/builds"; +import { buildCommand } from "./commands/build"; const __dirname = fileURLToPath(new URL(".", import.meta.url)); @@ -22,7 +22,7 @@ program uploadCommand(program); skipCommand(program); finalizeCommand(program); -buildsCommand(program); +buildCommand(program); if (!process.argv.slice(2).length) { program.outputHelp(); diff --git a/packages/gitlab/src/statuses.ts b/packages/gitlab/src/statuses.ts index d55b19d9..2be60481 100644 --- a/packages/gitlab/src/statuses.ts +++ b/packages/gitlab/src/statuses.ts @@ -16,14 +16,26 @@ async function run(input: { }) { const { argosClient, gitlabClient, gitlabProjectId, pullInterval, commit } = input; - const { data, error } = await argosClient.GET("/project/builds", { - params: { - query: { - distinctName: "true", - headSha: commit, + + const { data: project, error: projectError } = + await argosClient.GET("/project"); + if (projectError) { + console.error(projectError); + throw new Error(projectError.error); + } + + const { data, error } = await argosClient.GET( + "/projects/{owner}/{project}/builds", + { + params: { + path: { owner: project.account.slug, project: project.name }, + query: { + distinctName: "true", + headSha: commit, + }, }, }, - }); + ); if (error) { console.error(error); diff --git a/skills/argos-cli/SKILL.md b/skills/argos-cli/SKILL.md index d9c0b5eb..e8be1dca 100644 --- a/skills/argos-cli/SKILL.md +++ b/skills/argos-cli/SKILL.md @@ -35,13 +35,13 @@ Auth resolves: `--token` flag > `ARGOS_TOKEN` env var. ## Available Commands -| Command | What it does | -| ------------------------ | -------------------------------- | -| `builds get ` | Fetch build metadata | -| `builds snapshots ` | Fetch snapshot diffs for a build | -| `upload ` | Upload screenshots to Argos | -| `finalize` | Finalize a parallel build | -| `skip` | Mark a build as skipped | +| Command | What it does | +| ----------------------- | -------------------------------- | +| `build get ` | Fetch build metadata | +| `build snapshots ` | Fetch snapshot diffs for a build | +| `upload ` | Upload screenshots to Argos | +| `finalize` | Finalize a parallel build | +| `skip` | Mark a build as skipped | Read the matching reference file for detailed flags and output shapes. @@ -50,10 +50,10 @@ Read the matching reference file for detailed flags and output shapes. **Review a build (fetch metadata first, then diffs that need review):** ```bash -argos builds get 72652 -argos builds snapshots 72652 --needs-review -argos builds get 72652 --json -argos builds snapshots 72652 --needs-review --json +argos build get 72652 +argos build snapshots 72652 --needs-review +argos build get 72652 --json +argos build snapshots 72652 --needs-review --json ``` **Upload screenshots in CI:** @@ -71,6 +71,6 @@ argos finalize --parallel-nonce $CI_PIPELINE_ID ## When to Load References -- **Fetching build data or reviewing snapshots** → [references/builds.md](references/builds.md) +- **Fetching build data or reviewing snapshots** → [references/build.md](references/build.md) - **Uploading screenshots or finalizing parallel builds** → [references/upload.md](references/upload.md) - **Skipping a build** → [references/skip.md](references/skip.md) diff --git a/skills/argos-cli/references/builds.md b/skills/argos-cli/references/build.md similarity index 67% rename from skills/argos-cli/references/builds.md rename to skills/argos-cli/references/build.md index d5e6b231..8beeb4c8 100644 --- a/skills/argos-cli/references/builds.md +++ b/skills/argos-cli/references/build.md @@ -1,10 +1,10 @@ -# builds +# build -Detailed flag specifications for `argos builds` commands. +Detailed flag specifications for `argos build` commands. --- -## builds get +## build get Fetch build metadata. Use `--json` for machine-readable output. @@ -21,9 +21,9 @@ Example: ```text Build #72652 -Status: failure (changes-detected) +Status: changes-detected Snapshots: total 42, changed 3, added 1, removed 0, unchanged 38 -Conclusion: rejected +Conclusion: changes-detected Branch: main Commit: abc123 Base branch: main @@ -33,35 +33,31 @@ URL: https://app.argos-ci.com/... Use `--json` whenever another tool needs to parse stdout. -**`--json` output:** +**`--json` output:** The full Build object from the OpenAPI spec: ```json { "id": "", "number": 72652, - "status": "success" | "failure" | "pending", - "rawStatus": "accepted" | "no-changes" | "rejected" | "changes-detected" | "expired" | "error" | "aborted" | "pending" | "progress", - "conclusion": "accepted" | "rejected" | null, - "branch": "main", - "commit": "", - "baseBranch": "main", - "baseCommit": "", + "head": { "sha": "", "branch": "main" }, + "base": { "sha": "", "branch": "main" }, + "status": "accepted" | "no-changes" | "rejected" | "changes-detected" | "expired" | "error" | "aborted" | "pending" | "progress", + "conclusion": "no-changes" | "changes-detected" | null, + "stats": { "total": 42, "changed": 3, "added": 1, "removed": 0, "unchanged": 38, "ignored": 0, "failure": 0, "retryFailure": 0 }, + "metadata": { "testReport": null }, "url": "https://app.argos-ci.com/...", - "stats": { "total": 42, "changed": 3, "added": 1, "removed": 0, "unchanged": 38 }, - "testReport": null, "notification": null } ``` -**Status mapping:** +**Status values:** -- `success` → `accepted` or `no-changes` -- `failure` → `rejected`, `changes-detected`, `expired`, `error`, or `aborted` -- `pending` → all other statuses +- Terminal statuses: `accepted`, `no-changes`, `rejected`, `changes-detected`, `expired`, `error`, `aborted` +- Pending statuses: `pending`, `progress` --- -## builds snapshots +## build snapshots Fetch snapshot diffs for a build. Use `--json` for machine-readable output. @@ -86,7 +82,9 @@ Summary: changed 2, added 1 homepage / desktop [changed] Review: https://app.argos-ci.com/.../ - Diff image: https://cdn.argos-ci.com/... + Mask: https://cdn.argos-ci.com/... + Base file: https://cdn.argos-ci.com/... + Head file: https://cdn.argos-ci.com/... Score: 0.042 Group: homepage ``` @@ -95,7 +93,7 @@ Use `--json` whenever another tool needs to parse stdout. If `--needs-review` is passed, the API returns only diffs that need review. -**`--json` output:** Array of snapshot diff objects: +**`--json` output:** Raw array of SnapshotDiff objects from the OpenAPI spec: ```json [ @@ -104,27 +102,17 @@ If `--needs-review` is passed, the API returns only diffs that need review. "name": "homepage / desktop", "status": "changed" | "added" | "removed" | "unchanged" | "pending" | "failure" | "ignored" | "retryFailure", "score": 0.042, - "buildUrl": "https://app.argos-ci.com/...", - "reviewUrl": "https://app.argos-ci.com/.../", - "diffImageUrl": "https://cdn.argos-ci.com/...", "group": "homepage", "parentName": null, + "url": "https://cdn.argos-ci.com/...", "base": { "id": "", "name": "homepage / desktop", - "imageUrl": "https://cdn.argos-ci.com/...", - "contentType": "image/png", + "metadata": { ... }, "width": 1280, "height": 800, - "pageUrl": "https://example.com", - "previewUrl": null, - "viewport": { "width": 1280, "height": 800 }, - "browser": { "name": "chromium", "version": "120.0" }, - "automationLibrary": null, - "sdk": null, - "test": null, - "story": null, - "tags": null + "url": "https://cdn.argos-ci.com/...", + "contentType": "image/png" }, "head": { ... } } @@ -133,7 +121,8 @@ If `--needs-review` is passed, the API returns only diffs that need review. **Notes:** -- `diffImageUrl` is the visual diff overlay — the most efficient signal for automated review. +- `url` on the diff is the mask (visual diff overlay) — the most efficient signal for automated review. +- `base.url` / `head.url` are the snapshot file URLs (can be images or YAML for Aria snapshots). - `score` ranges from `0` (identical) to `1` (completely different). `null` for added/removed snapshots. - `base` is `null` for `added` snapshots; `head` is `null` for `removed` snapshots. - Paginates automatically — all results are returned in a single call.