From c414dc8215fc14b49ae0f0b79979344aa9f64208 Mon Sep 17 00:00:00 2001 From: Rahul Sethi <5822355+RamIdeas@users.noreply.github.com> Date: Fri, 8 Mar 2024 11:38:31 +0000 Subject: [PATCH 01/10] move versions subcommand registration into versions directory --- packages/wrangler/src/index.ts | 22 ++-------------------- packages/wrangler/src/versions/index.ts | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/packages/wrangler/src/index.ts b/packages/wrangler/src/index.ts index 098e29b19ee0..c5925c5e517f 100644 --- a/packages/wrangler/src/index.ts +++ b/packages/wrangler/src/index.ts @@ -66,11 +66,7 @@ import { validateScopeKeys, } from "./user"; import { vectorize } from "./vectorize/index"; -import { versionsUploadHandler, versionsUploadOptions } from "./versions"; -import { - versionsDeployHandler, - versionsDeployOptions, -} from "./versions/deploy"; +import registerVersionsSubcommands from "./versions"; import { whoami } from "./whoami"; import { asJson } from "./yargs-types"; import type { Config } from "./config"; @@ -710,21 +706,7 @@ export function createCLIParser(argv: string[]) { "--experimental-gradual-rollouts" ); if (experimentalGradualRollouts) { - wrangler.command("versions", false, (versionYargs) => { - return versionYargs - .command( - "upload", - "Uploads your Worker code and config as a new version [beta]", - versionsUploadOptions, - versionsUploadHandler - ) - .command( - "deploy [version-specs..]", - "Safely roll out new versions of your Worker by splitting traffic between multiple versions [beta]", - versionsDeployOptions, - versionsDeployHandler - ); - }); + wrangler.command("versions", false, registerVersionsSubcommands); } wrangler.exitProcess(false); diff --git a/packages/wrangler/src/versions/index.ts b/packages/wrangler/src/versions/index.ts index d4867a92f9d0..21e1c820982b 100644 --- a/packages/wrangler/src/versions/index.ts +++ b/packages/wrangler/src/versions/index.ts @@ -11,6 +11,7 @@ import { logger } from "../logger"; import * as metrics from "../metrics"; import { requireAuth } from "../user"; import { collectKeyValues } from "../utils/collectKeyValues"; +import { versionsDeployHandler, versionsDeployOptions } from "./deploy"; import versionsUpload from "./upload"; import type { Config } from "../config"; import type { @@ -223,3 +224,21 @@ export async function versionsUploadHandler( message: args.message, }); } + +export default function registerVersionsSubcommands( + versionYargs: CommonYargsArgv +) { + versionYargs + .command( + "upload", + "Uploads your Worker code and config as a new version [beta]", + versionsUploadOptions, + versionsUploadHandler + ) + .command( + "deploy [version-specs..]", + "Safely roll out new versions of your Worker by splitting traffic between multiple versions [beta]", + versionsDeployOptions, + versionsDeployHandler + ); +} From d9cc4383288ac67b7d963759091cf1feef893754 Mon Sep 17 00:00:00 2001 From: Rahul Sethi <5822355+RamIdeas@users.noreply.github.com> Date: Fri, 8 Mar 2024 12:13:00 +0000 Subject: [PATCH 02/10] implement versions list command --- packages/wrangler/src/metrics/send-event.ts | 3 +- packages/wrangler/src/versions/index.ts | 11 +- packages/wrangler/src/versions/list.ts | 108 ++++++++++++++++++++ 3 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 packages/wrangler/src/versions/list.ts diff --git a/packages/wrangler/src/metrics/send-event.ts b/packages/wrangler/src/metrics/send-event.ts index f885efedc7d2..6e903459bc09 100644 --- a/packages/wrangler/src/metrics/send-event.ts +++ b/packages/wrangler/src/metrics/send-event.ts @@ -66,7 +66,8 @@ export type EventNames = | "view deployments" | "rollback deployments" | "upload worker version" - | "deploy worker versions"; + | "deploy worker versions" + | "list worker versions"; /** * Send a metrics event, with no extra properties, to Cloudflare, if usage tracking is enabled. diff --git a/packages/wrangler/src/versions/index.ts b/packages/wrangler/src/versions/index.ts index 21e1c820982b..7f19f5721237 100644 --- a/packages/wrangler/src/versions/index.ts +++ b/packages/wrangler/src/versions/index.ts @@ -12,6 +12,7 @@ import * as metrics from "../metrics"; import { requireAuth } from "../user"; import { collectKeyValues } from "../utils/collectKeyValues"; import { versionsDeployHandler, versionsDeployOptions } from "./deploy"; +import { versionsListHandler, versionsListOptions } from "./list"; import versionsUpload from "./upload"; import type { Config } from "../config"; import type { @@ -229,15 +230,21 @@ export default function registerVersionsSubcommands( versionYargs: CommonYargsArgv ) { versionYargs + .command( + "list", + "List the 10 most recent Versions of your Worker [beta]", + versionsListOptions, + versionsListHandler + ) .command( "upload", - "Uploads your Worker code and config as a new version [beta]", + "Uploads your Worker code and config as a new Version [beta]", versionsUploadOptions, versionsUploadHandler ) .command( "deploy [version-specs..]", - "Safely roll out new versions of your Worker by splitting traffic between multiple versions [beta]", + "Safely roll out new Versions of your Worker by splitting traffic between multiple Versions [beta]", versionsDeployOptions, versionsDeployHandler ); diff --git a/packages/wrangler/src/versions/list.ts b/packages/wrangler/src/versions/list.ts new file mode 100644 index 000000000000..7a4469c312e2 --- /dev/null +++ b/packages/wrangler/src/versions/list.ts @@ -0,0 +1,108 @@ +import path from "path"; +import * as cli from "@cloudflare/cli"; +import { fetchResult } from "../cfetch"; +import { findWranglerToml, readConfig } from "../config"; +import { UserError } from "../errors"; +import * as metrics from "../metrics"; +import { printWranglerBanner } from "../update-check"; +import { requireAuth } from "../user"; +import type { + CommonYargsArgv, + StrictYargsOptionsToInterface, +} from "../yargs-types"; + +const BLANK_INPUT = "-"; // To be used where optional user-input is displayed and the value is nullish + +export type VersionsListArgs = StrictYargsOptionsToInterface< + typeof versionsListOptions +>; + +type UUID = string; +type VersionId = UUID; +type ApiVersion = { + id: VersionId; + number: number; + metadata: { + created_on: string; + modified_on: string; + source: "api" | string; + author_id: string; + author_email: string; + }; + annotations?: Record & { + "workers/triggered_by"?: "upload" | string; + "workers/message"?: string; + "workers/tag"?: string; + }; + // other properties not typed as not used +}; + +export function versionsListOptions(yargs: CommonYargsArgv) { + return yargs.option("name", { + describe: "Name of the worker", + type: "string", + requiresArg: true, + }); +} + +export async function versionsListHandler(args: VersionsListArgs) { + await printWranglerBanner(); + + const config = getConfig(args); + await metrics.sendMetricsEvent( + "list worker versions", + {}, + { + sendMetrics: config.send_metrics, + } + ); + + const accountId = await requireAuth(config); + const workerName = args.name ?? config.name; + + if (workerName === undefined) { + throw new UserError( + 'You need to provide a name when deploying a worker. Either pass it as a cli arg with `--name ` or in your config file as `name = ""`' + ); + } + + const { items: versions } = await fetchResult<{ items: ApiVersion[] }>( + `/accounts/${accountId}/workers/scripts/${workerName}/versions` + ); + + for (const version of versions) { + const view = { + "Version ID": version.id, + Created: new Date(version.metadata["created_on"]).toLocaleString(), + Author: version.metadata.author_email, + Source: version.metadata.source, + Tag: version.annotations?.["workers/tag"] ?? BLANK_INPUT, + Message: version.annotations?.["workers/message"] ?? BLANK_INPUT, + }; + + const alignment = 17; + for (const [label, value] of Object.entries(view)) { + const paddedLabel = `${label}:`.padEnd(alignment); + const alignedValue = value + ?.split("\n") + .map((line, lineNo) => + lineNo === 0 ? line : " ".repeat(alignment) + line + ) + .join("\n"); + + cli.logRaw(paddedLabel + alignedValue); + } + + cli.logRaw(``); + } +} + +function getConfig( + args: Pick +) { + const configPath = + args.config || (args.name && findWranglerToml(path.dirname(args.name))); + const config = readConfig(configPath, args); + + return config; +} From 60523e187b23d29c304f61be268a2bb273b41f17 Mon Sep 17 00:00:00 2001 From: Rahul Sethi <5822355+RamIdeas@users.noreply.github.com> Date: Fri, 8 Mar 2024 12:55:46 +0000 Subject: [PATCH 03/10] refactor to use shared util --- .../src/utils/render-labelled-values.ts | 22 +++++++++++++ packages/wrangler/src/versions/list.ts | 32 ++++++------------- 2 files changed, 31 insertions(+), 23 deletions(-) create mode 100644 packages/wrangler/src/utils/render-labelled-values.ts diff --git a/packages/wrangler/src/utils/render-labelled-values.ts b/packages/wrangler/src/utils/render-labelled-values.ts new file mode 100644 index 000000000000..68900e4a0b61 --- /dev/null +++ b/packages/wrangler/src/utils/render-labelled-values.ts @@ -0,0 +1,22 @@ +import { logger } from "../logger"; + +export default function renderLabelledValues( + view: Record, + { print = logger.log as (line: string) => unknown, spacerCount = 2 } = {} +) { + const alignment = + Math.max(...Object.keys(view).map((key) => key.length)) + spacerCount; + for (const [label, value] of Object.entries(view)) { + const paddedLabel = label.padEnd(alignment); + const alignedValue = value + ?.split("\n") + .map((line, lineNo) => + lineNo === 0 ? line : " ".repeat(alignment) + line + ) + .join("\n"); + + print(paddedLabel + alignedValue); + } + + print(""); +} diff --git a/packages/wrangler/src/versions/list.ts b/packages/wrangler/src/versions/list.ts index 7a4469c312e2..99d8e0241809 100644 --- a/packages/wrangler/src/versions/list.ts +++ b/packages/wrangler/src/versions/list.ts @@ -6,6 +6,7 @@ import { UserError } from "../errors"; import * as metrics from "../metrics"; import { printWranglerBanner } from "../update-check"; import { requireAuth } from "../user"; +import renderLabelledValues from "../utils/render-labelled-values"; import type { CommonYargsArgv, StrictYargsOptionsToInterface, @@ -71,29 +72,14 @@ export async function versionsListHandler(args: VersionsListArgs) { ); for (const version of versions) { - const view = { - "Version ID": version.id, - Created: new Date(version.metadata["created_on"]).toLocaleString(), - Author: version.metadata.author_email, - Source: version.metadata.source, - Tag: version.annotations?.["workers/tag"] ?? BLANK_INPUT, - Message: version.annotations?.["workers/message"] ?? BLANK_INPUT, - }; - - const alignment = 17; - for (const [label, value] of Object.entries(view)) { - const paddedLabel = `${label}:`.padEnd(alignment); - const alignedValue = value - ?.split("\n") - .map((line, lineNo) => - lineNo === 0 ? line : " ".repeat(alignment) + line - ) - .join("\n"); - - cli.logRaw(paddedLabel + alignedValue); - } - - cli.logRaw(``); + renderLabelledValues({ + "Version ID:": version.id, + "Created:": new Date(version.metadata["created_on"]).toLocaleString(), + "Author:": version.metadata.author_email, + "Source:": version.metadata.source, + "Tag:": version.annotations?.["workers/tag"] ?? BLANK_INPUT, + "Message:": version.annotations?.["workers/message"] ?? BLANK_INPUT, + }); } } From 043c1a1854bf12a7f4cc25f08cf6e6d05536d908 Mon Sep 17 00:00:00 2001 From: Rahul Sethi <5822355+RamIdeas@users.noreply.github.com> Date: Fri, 8 Mar 2024 14:34:22 +0000 Subject: [PATCH 04/10] implement versions view command --- packages/wrangler/src/metrics/send-event.ts | 1 + packages/wrangler/src/versions/index.ts | 7 ++ packages/wrangler/src/versions/view.ts | 98 +++++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 packages/wrangler/src/versions/view.ts diff --git a/packages/wrangler/src/metrics/send-event.ts b/packages/wrangler/src/metrics/send-event.ts index 6e903459bc09..315be70d9a42 100644 --- a/packages/wrangler/src/metrics/send-event.ts +++ b/packages/wrangler/src/metrics/send-event.ts @@ -67,6 +67,7 @@ export type EventNames = | "rollback deployments" | "upload worker version" | "deploy worker versions" + | "view worker version" | "list worker versions"; /** diff --git a/packages/wrangler/src/versions/index.ts b/packages/wrangler/src/versions/index.ts index 7f19f5721237..ddb45f5ec5ef 100644 --- a/packages/wrangler/src/versions/index.ts +++ b/packages/wrangler/src/versions/index.ts @@ -14,6 +14,7 @@ import { collectKeyValues } from "../utils/collectKeyValues"; import { versionsDeployHandler, versionsDeployOptions } from "./deploy"; import { versionsListHandler, versionsListOptions } from "./list"; import versionsUpload from "./upload"; +import { versionsViewHandler, versionsViewOptions } from "./view"; import type { Config } from "../config"; import type { CommonYargsArgv, @@ -230,6 +231,12 @@ export default function registerVersionsSubcommands( versionYargs: CommonYargsArgv ) { versionYargs + .command( + "view ", + "View the details of a specific version of your Worker [beta]", + versionsViewOptions, + versionsViewHandler + ) .command( "list", "List the 10 most recent Versions of your Worker [beta]", diff --git a/packages/wrangler/src/versions/view.ts b/packages/wrangler/src/versions/view.ts new file mode 100644 index 000000000000..1e0f5ac2e711 --- /dev/null +++ b/packages/wrangler/src/versions/view.ts @@ -0,0 +1,98 @@ +import path from "path"; +import * as cli from "@cloudflare/cli"; +import { fetchResult } from "../cfetch"; +import { findWranglerToml, readConfig } from "../config"; +import { UserError } from "../errors"; +import * as metrics from "../metrics"; +import { printWranglerBanner } from "../update-check"; +import { requireAuth } from "../user"; +import renderLabelledValues from "../utils/render-labelled-values"; +import type { + CommonYargsArgv, + StrictYargsOptionsToInterface, +} from "../yargs-types"; + +const BLANK_INPUT = "-"; // To be used where optional user-input is displayed and the value is nullish + +export type VersionsViewArgs = StrictYargsOptionsToInterface< + typeof versionsViewOptions +>; + +type UUID = string; +type VersionId = UUID; +type ApiVersion = { + id: VersionId; + number: number; + metadata: { + created_on: string; + modified_on: string; + source: "api" | string; + author_id: string; + author_email: string; + }; + annotations?: Record & { + "workers/triggered_by"?: "upload" | string; + "workers/message"?: string; + "workers/tag"?: string; + }; + // other properties not typed as not used +}; + +export function versionsViewOptions(yargs: CommonYargsArgv) { + return yargs + .positional("version-id", { + describe: "The Worker Version ID to view", + type: "string", + requiresArg: true, + }) + .option("name", { + describe: "Name of the worker", + type: "string", + requiresArg: true, + }); +} + +export async function versionsViewHandler(args: VersionsViewArgs) { + await printWranglerBanner(); + + const config = getConfig(args); + await metrics.sendMetricsEvent( + "view worker version", + {}, + { + sendMetrics: config.send_metrics, + } + ); + + const accountId = await requireAuth(config); + const workerName = args.name ?? config.name; + + if (workerName === undefined) { + throw new UserError( + 'You need to provide a name when deploying a worker. Either pass it as a cli arg with `--name ` or in your config file as `name = ""`' + ); + } + + const version = await fetchResult( + `/accounts/${accountId}/workers/scripts/${workerName}/versions/${args.versionId}` + ); + + renderLabelledValues({ + "Version ID:": version.id, + "Created:": new Date(version.metadata["created_on"]).toLocaleString(), + "Author:": version.metadata.author_email, + "Source:": version.metadata.source, + "Tag:": version.annotations?.["workers/tag"] ?? BLANK_INPUT, + "Message:": version.annotations?.["workers/message"] ?? BLANK_INPUT, + }); +} + +function getConfig( + args: Pick +) { + const configPath = + args.config || (args.name && findWranglerToml(path.dirname(args.name))); + const config = readConfig(configPath, args); + + return config; +} From 8553916d05c08439f27724176e015937cea2d3a0 Mon Sep 17 00:00:00 2001 From: Rahul Sethi <5822355+RamIdeas@users.noreply.github.com> Date: Fri, 8 Mar 2024 18:27:19 +0000 Subject: [PATCH 05/10] add `versions view` tests --- .../versions/versions.deploy.test.ts | 4 +- .../__tests__/versions/versions.view.test.ts | 183 ++++++++++++++++++ packages/wrangler/src/versions/view.ts | 1 + 3 files changed, 186 insertions(+), 2 deletions(-) create mode 100644 packages/wrangler/src/__tests__/versions/versions.view.test.ts diff --git a/packages/wrangler/src/__tests__/versions/versions.deploy.test.ts b/packages/wrangler/src/__tests__/versions/versions.deploy.test.ts index 572aec4d3e62..276ba0ba17b2 100644 --- a/packages/wrangler/src/__tests__/versions/versions.deploy.test.ts +++ b/packages/wrangler/src/__tests__/versions/versions.deploy.test.ts @@ -21,7 +21,7 @@ import { runWrangler } from "../helpers/run-wrangler"; import writeWranglerToml from "../helpers/write-wrangler-toml"; import type { VersionsDeployArgs } from "../../versions/deploy"; -function mockProcessOutput() { +function collectStreamOutput() { const std = { out: "", err: "" }; const onStdOutData = (chunk: Buffer) => (std.out += chunk.toString()); const onStdErrData = (chunk: Buffer) => (std.err += chunk.toString()); @@ -45,7 +45,7 @@ describe("versions deploy", () => { mockAccountId(); mockApiToken(); runInTempDir(); - const std = mockProcessOutput(); + const std = collectStreamOutput(); beforeEach(() => { msw.use( diff --git a/packages/wrangler/src/__tests__/versions/versions.view.test.ts b/packages/wrangler/src/__tests__/versions/versions.view.test.ts new file mode 100644 index 000000000000..097fef5b8cd8 --- /dev/null +++ b/packages/wrangler/src/__tests__/versions/versions.view.test.ts @@ -0,0 +1,183 @@ +import { normalizeOutput } from "../../../e2e/helpers/normalize"; +import { mockAccountId, mockApiToken } from "../helpers/mock-account-id"; +import { mockConsoleMethods } from "../helpers/mock-console"; +import { msw, mswGetVersion } from "../helpers/msw"; +import { runInTempDir } from "../helpers/run-in-tmp"; +import { runWrangler } from "../helpers/run-wrangler"; +import writeWranglerToml from "../helpers/write-wrangler-toml"; + +describe("versions view", () => { + mockAccountId(); + mockApiToken(); + runInTempDir(); + const std = mockConsoleMethods(); + + beforeEach(() => { + msw.use(mswGetVersion); + }); + + describe("without wrangler.toml", () => { + test("fails with no args", async () => { + const result = runWrangler( + "versions view --experimental-gradual-rollouts" + ); + + await expect(result).rejects.toMatchInlineSnapshot( + `[Error: Not enough non-option arguments: got 0, need at least 1]` + ); + + expect(normalizeOutput(std.out)).toMatchInlineSnapshot(` + "wrangler versions view + View the details of a specific version of your Worker [beta] + Positionals: + version-id The Worker Version ID to view [string] [required] + Flags: + -j, --experimental-json-config Experimental: Support wrangler.json [boolean] + -c, --config Path to .toml configuration file [string] + -e, --env Environment to use for operations and .env files [string] + -h, --help Show help [boolean] + -v, --version Show version number [boolean] + Options: + --name Name of the worker [string]" + `); + + expect(normalizeOutput(std.err)).toMatchInlineSnapshot( + `"X [ERROR] Not enough non-option arguments: got 0, need at least 1"` + ); + }); + + test("fails with --name arg only", async () => { + const result = runWrangler( + "versions view --name test-name --experimental-gradual-rollouts" + ); + + await expect(result).rejects.toMatchInlineSnapshot( + `[Error: Not enough non-option arguments: got 0, need at least 1]` + ); + + expect(normalizeOutput(std.out)).toMatchInlineSnapshot(` + "wrangler versions view + View the details of a specific version of your Worker [beta] + Positionals: + version-id The Worker Version ID to view [string] [required] + Flags: + -j, --experimental-json-config Experimental: Support wrangler.json [boolean] + -c, --config Path to .toml configuration file [string] + -e, --env Environment to use for operations and .env files [string] + -h, --help Show help [boolean] + -v, --version Show version number [boolean] + Options: + --name Name of the worker [string]" + `); + + expect(normalizeOutput(std.err)).toMatchInlineSnapshot( + `"X [ERROR] Not enough non-option arguments: got 0, need at least 1"` + ); + }); + + test("fails with positional version-id arg only", async () => { + const result = runWrangler( + "versions view 10000000-0000-0000-0000-000000000000 --experimental-gradual-rollouts" + ); + + await expect(result).rejects.toMatchInlineSnapshot( + `[Error: You need to provide a name when deploying a worker. Either pass it as a cli arg with \`--name \` or in your config file as \`name = ""\`]` + ); + + expect(normalizeOutput(std.out)).toMatchInlineSnapshot(`""`); + + expect(normalizeOutput(std.err)).toMatchInlineSnapshot( + `"X [ERROR] You need to provide a name when deploying a worker. Either pass it as a cli arg with \`--name \` or in your config file as \`name = \\"\\"\`"` + ); + }); + + test("succeeds with positional version-id arg and --name arg", async () => { + const result = runWrangler( + "versions view 10000000-0000-0000-0000-000000000000 --name test-name --experimental-gradual-rollouts" + ); + + await expect(result).resolves.toBeUndefined(); + + expect(normalizeOutput(std.out)).toMatchInlineSnapshot(` + "Version ID: 00000000-0000-0000-0000-000000000000 + Created: 1/1/2021, TIMESTAMP AM + Author: Jean-Luc-Picard@federation.org + Source: wrangler + Tag: - + Message: -" + `); + + expect(normalizeOutput(std.err)).toMatchInlineSnapshot(`""`); + }); + }); + + describe("with wrangler.toml", () => { + beforeEach(writeWranglerToml); + + test("fails with no args", async () => { + const result = runWrangler( + "versions view --experimental-gradual-rollouts" + ); + + await expect(result).rejects.toMatchInlineSnapshot( + `[Error: Not enough non-option arguments: got 0, need at least 1]` + ); + + expect(normalizeOutput(std.out)).toMatchInlineSnapshot(` + "wrangler versions view + View the details of a specific version of your Worker [beta] + Positionals: + version-id The Worker Version ID to view [string] [required] + Flags: + -j, --experimental-json-config Experimental: Support wrangler.json [boolean] + -c, --config Path to .toml configuration file [string] + -e, --env Environment to use for operations and .env files [string] + -h, --help Show help [boolean] + -v, --version Show version number [boolean] + Options: + --name Name of the worker [string]" + `); + + expect(normalizeOutput(std.err)).toMatchInlineSnapshot( + `"X [ERROR] Not enough non-option arguments: got 0, need at least 1"` + ); + }); + + test("succeeds with positional version-id arg only", async () => { + const result = runWrangler( + "versions view 10000000-0000-0000-0000-000000000000 --experimental-gradual-rollouts" + ); + + await expect(result).resolves.toBeUndefined(); + + expect(normalizeOutput(std.out)).toMatchInlineSnapshot(` + "Version ID: 00000000-0000-0000-0000-000000000000 + Created: 1/1/2021, TIMESTAMP AM + Author: Jean-Luc-Picard@federation.org + Source: wrangler + Tag: - + Message: -" + `); + + expect(normalizeOutput(std.err)).toMatchInlineSnapshot(`""`); + }); + + test("fails with non-existent version-id", async () => { + const result = runWrangler( + "versions view ffffffff-ffff-ffff-ffff-ffffffffffff --experimental-gradual-rollouts" + ); + + await expect(result).rejects.toMatchInlineSnapshot( + `[APIError: A request to the Cloudflare API (/accounts/some-account-id/workers/scripts/test-name/versions/ffffffff-ffff-ffff-ffff-ffffffffffff) failed.]` + ); + + expect(normalizeOutput(std.out)).toMatchInlineSnapshot(` + "X [ERROR] A request to the Cloudflare API (/accounts/some-account-id/workers/scripts/test-name/versions/00000000-0000-0000-0000-000000000000) failed. + If you think this is a bug, please open an issue at: + https://github.com/cloudflare/workers-sdk/issues/new/choose" + `); + + expect(normalizeOutput(std.err)).toMatchInlineSnapshot(`""`); + }); + }); +}); diff --git a/packages/wrangler/src/versions/view.ts b/packages/wrangler/src/versions/view.ts index 1e0f5ac2e711..3132a7fa88e3 100644 --- a/packages/wrangler/src/versions/view.ts +++ b/packages/wrangler/src/versions/view.ts @@ -44,6 +44,7 @@ export function versionsViewOptions(yargs: CommonYargsArgv) { describe: "The Worker Version ID to view", type: "string", requiresArg: true, + demandOption: true, }) .option("name", { describe: "Name of the worker", From cb33b10f597e10e089558766f37a22c657f61e55 Mon Sep 17 00:00:00 2001 From: Rahul Sethi <5822355+RamIdeas@users.noreply.github.com> Date: Fri, 8 Mar 2024 18:27:43 +0000 Subject: [PATCH 06/10] add `versions list` tests --- .../__tests__/versions/versions.list.test.ts | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 packages/wrangler/src/__tests__/versions/versions.list.test.ts diff --git a/packages/wrangler/src/__tests__/versions/versions.list.test.ts b/packages/wrangler/src/__tests__/versions/versions.list.test.ts new file mode 100644 index 000000000000..9f16c9c9ce32 --- /dev/null +++ b/packages/wrangler/src/__tests__/versions/versions.list.test.ts @@ -0,0 +1,123 @@ +import { mockAccountId, mockApiToken } from "../helpers/mock-account-id"; +import { mockConsoleMethods } from "../helpers/mock-console"; +import { msw, mswListVersions } from "../helpers/msw"; +import { runInTempDir } from "../helpers/run-in-tmp"; +import { runWrangler } from "../helpers/run-wrangler"; +import writeWranglerToml from "../helpers/write-wrangler-toml"; + +describe("versions list", () => { + mockAccountId(); + mockApiToken(); + runInTempDir(); + const std = mockConsoleMethods(); + + beforeEach(() => { + msw.use(mswListVersions); + }); + + describe("without wrangler.toml", () => { + test("fails with no args", async () => { + const result = runWrangler( + "versions list --experimental-gradual-rollouts" + ); + + await expect(result).rejects.toMatchInlineSnapshot( + `[Error: You need to provide a name when deploying a worker. Either pass it as a cli arg with \`--name \` or in your config file as \`name = ""\`]` + ); + + expect(std.out).toMatchInlineSnapshot(`""`); + + expect(std.err).toMatchInlineSnapshot(` + "X [ERROR] You need to provide a name when deploying a worker. Either pass it as a cli arg with \`--name \` or in your config file as \`name = \\"\\"\` + + " + `); + }); + + test("prints versions to stdout", async () => { + const result = runWrangler( + "versions list --name test-name --experimental-gradual-rollouts" + ); + + await expect(result).resolves.toBeUndefined(); + + expect(std.out).toMatchInlineSnapshot(` + "Version ID: 40000000-0000-0000-0000-000000000000 + Created: 1/1/2021, 12:00:00 AM + Author: Jean-Luc-Picard@federation.org + Source: wrangler + Tag: - + Message: - + + Version ID: 30000000-0000-0000-0000-000000000000 + Created: 2/2/2021, 12:00:00 AM + Author: Kathryn-Janeway@federation.org + Source: wrangler + Tag: - + Message: Rolled back for this version + + Version ID: 20000000-0000-0000-0000-000000000000 + Created: 2/3/2021, 12:00:00 AM + Author: Kathryn-Janeway@federation.org + Source: wrangler + Tag: - + Message: - + + Version ID: 10000000-0000-0000-0000-000000000000 + Created: 1/4/2021, 12:00:00 AM + Author: Jean-Luc-Picard@federation.org + Source: wrangler + Tag: - + Message: - + " + `); + + expect(std.err).toMatchInlineSnapshot(`""`); + }); + }); + + describe("with wrangler.toml", () => { + beforeEach(writeWranglerToml); + + test("prints versions to stdout", async () => { + const result = runWrangler( + "versions list --experimental-gradual-rollouts" + ); + + await expect(result).resolves.toBeUndefined(); + + expect(std.out).toMatchInlineSnapshot(` + "Version ID: 40000000-0000-0000-0000-000000000000 + Created: 1/1/2021, 12:00:00 AM + Author: Jean-Luc-Picard@federation.org + Source: wrangler + Tag: - + Message: - + + Version ID: 30000000-0000-0000-0000-000000000000 + Created: 2/2/2021, 12:00:00 AM + Author: Kathryn-Janeway@federation.org + Source: wrangler + Tag: - + Message: Rolled back for this version + + Version ID: 20000000-0000-0000-0000-000000000000 + Created: 2/3/2021, 12:00:00 AM + Author: Kathryn-Janeway@federation.org + Source: wrangler + Tag: - + Message: - + + Version ID: 10000000-0000-0000-0000-000000000000 + Created: 1/4/2021, 12:00:00 AM + Author: Jean-Luc-Picard@federation.org + Source: wrangler + Tag: - + Message: - + " + `); + + expect(std.err).toMatchInlineSnapshot(`""`); + }); + }); +}); From 887f47ae809e8b6a30ca8daa009f563809ca069d Mon Sep 17 00:00:00 2001 From: Rahul Sethi <5822355+RamIdeas@users.noreply.github.com> Date: Fri, 8 Mar 2024 19:07:11 +0000 Subject: [PATCH 07/10] remove unused imports --- packages/wrangler/src/versions/list.ts | 1 - packages/wrangler/src/versions/view.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/wrangler/src/versions/list.ts b/packages/wrangler/src/versions/list.ts index 99d8e0241809..1ff5cafe2f3c 100644 --- a/packages/wrangler/src/versions/list.ts +++ b/packages/wrangler/src/versions/list.ts @@ -1,5 +1,4 @@ import path from "path"; -import * as cli from "@cloudflare/cli"; import { fetchResult } from "../cfetch"; import { findWranglerToml, readConfig } from "../config"; import { UserError } from "../errors"; diff --git a/packages/wrangler/src/versions/view.ts b/packages/wrangler/src/versions/view.ts index 3132a7fa88e3..263956de5ba9 100644 --- a/packages/wrangler/src/versions/view.ts +++ b/packages/wrangler/src/versions/view.ts @@ -1,5 +1,4 @@ import path from "path"; -import * as cli from "@cloudflare/cli"; import { fetchResult } from "../cfetch"; import { findWranglerToml, readConfig } from "../config"; import { UserError } from "../errors"; From e6d37d5f2e2d9973edcfb39f6da8b32eb17ebf58 Mon Sep 17 00:00:00 2001 From: Rahul Sethi <5822355+RamIdeas@users.noreply.github.com> Date: Fri, 8 Mar 2024 19:32:54 +0000 Subject: [PATCH 08/10] only normalizeOutput in snapshot tests if necessary --- .../__tests__/versions/versions.list.test.ts | 9 ++-- .../__tests__/versions/versions.view.test.ts | 47 +++++++++++++------ 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/packages/wrangler/src/__tests__/versions/versions.list.test.ts b/packages/wrangler/src/__tests__/versions/versions.list.test.ts index 9f16c9c9ce32..83f9fbeac006 100644 --- a/packages/wrangler/src/__tests__/versions/versions.list.test.ts +++ b/packages/wrangler/src/__tests__/versions/versions.list.test.ts @@ -1,3 +1,4 @@ +import { normalizeOutput } from "../../../e2e/helpers/normalize"; import { mockAccountId, mockApiToken } from "../helpers/mock-account-id"; import { mockConsoleMethods } from "../helpers/mock-console"; import { msw, mswListVersions } from "../helpers/msw"; @@ -27,11 +28,9 @@ describe("versions list", () => { expect(std.out).toMatchInlineSnapshot(`""`); - expect(std.err).toMatchInlineSnapshot(` - "X [ERROR] You need to provide a name when deploying a worker. Either pass it as a cli arg with \`--name \` or in your config file as \`name = \\"\\"\` - - " - `); + expect(normalizeOutput(std.err)).toMatchInlineSnapshot( + `"X [ERROR] You need to provide a name when deploying a worker. Either pass it as a cli arg with \`--name \` or in your config file as \`name = \\"\\"\`"` + ); }); test("prints versions to stdout", async () => { diff --git a/packages/wrangler/src/__tests__/versions/versions.view.test.ts b/packages/wrangler/src/__tests__/versions/versions.view.test.ts index 097fef5b8cd8..7c390bf54200 100644 --- a/packages/wrangler/src/__tests__/versions/versions.view.test.ts +++ b/packages/wrangler/src/__tests__/versions/versions.view.test.ts @@ -26,17 +26,22 @@ describe("versions view", () => { `[Error: Not enough non-option arguments: got 0, need at least 1]` ); - expect(normalizeOutput(std.out)).toMatchInlineSnapshot(` - "wrangler versions view + expect(std.out).toMatchInlineSnapshot(` + " + wrangler versions view + View the details of a specific version of your Worker [beta] + Positionals: version-id The Worker Version ID to view [string] [required] + Flags: -j, --experimental-json-config Experimental: Support wrangler.json [boolean] -c, --config Path to .toml configuration file [string] -e, --env Environment to use for operations and .env files [string] -h, --help Show help [boolean] -v, --version Show version number [boolean] + Options: --name Name of the worker [string]" `); @@ -55,17 +60,22 @@ describe("versions view", () => { `[Error: Not enough non-option arguments: got 0, need at least 1]` ); - expect(normalizeOutput(std.out)).toMatchInlineSnapshot(` - "wrangler versions view + expect(std.out).toMatchInlineSnapshot(` + " + wrangler versions view + View the details of a specific version of your Worker [beta] + Positionals: version-id The Worker Version ID to view [string] [required] + Flags: -j, --experimental-json-config Experimental: Support wrangler.json [boolean] -c, --config Path to .toml configuration file [string] -e, --env Environment to use for operations and .env files [string] -h, --help Show help [boolean] -v, --version Show version number [boolean] + Options: --name Name of the worker [string]" `); @@ -84,7 +94,7 @@ describe("versions view", () => { `[Error: You need to provide a name when deploying a worker. Either pass it as a cli arg with \`--name \` or in your config file as \`name = ""\`]` ); - expect(normalizeOutput(std.out)).toMatchInlineSnapshot(`""`); + expect(std.out).toMatchInlineSnapshot(`""`); expect(normalizeOutput(std.err)).toMatchInlineSnapshot( `"X [ERROR] You need to provide a name when deploying a worker. Either pass it as a cli arg with \`--name \` or in your config file as \`name = \\"\\"\`"` @@ -98,13 +108,14 @@ describe("versions view", () => { await expect(result).resolves.toBeUndefined(); - expect(normalizeOutput(std.out)).toMatchInlineSnapshot(` - "Version ID: 00000000-0000-0000-0000-000000000000 - Created: 1/1/2021, TIMESTAMP AM + expect(std.out).toMatchInlineSnapshot(` + "Version ID: 10000000-0000-0000-0000-000000000000 + Created: 1/1/2021, 12:00:00 AM Author: Jean-Luc-Picard@federation.org Source: wrangler Tag: - - Message: -" + Message: - + " `); expect(normalizeOutput(std.err)).toMatchInlineSnapshot(`""`); @@ -123,17 +134,22 @@ describe("versions view", () => { `[Error: Not enough non-option arguments: got 0, need at least 1]` ); - expect(normalizeOutput(std.out)).toMatchInlineSnapshot(` - "wrangler versions view + expect(std.out).toMatchInlineSnapshot(` + " + wrangler versions view + View the details of a specific version of your Worker [beta] + Positionals: version-id The Worker Version ID to view [string] [required] + Flags: -j, --experimental-json-config Experimental: Support wrangler.json [boolean] -c, --config Path to .toml configuration file [string] -e, --env Environment to use for operations and .env files [string] -h, --help Show help [boolean] -v, --version Show version number [boolean] + Options: --name Name of the worker [string]" `); @@ -150,13 +166,14 @@ describe("versions view", () => { await expect(result).resolves.toBeUndefined(); - expect(normalizeOutput(std.out)).toMatchInlineSnapshot(` - "Version ID: 00000000-0000-0000-0000-000000000000 - Created: 1/1/2021, TIMESTAMP AM + expect(std.out).toMatchInlineSnapshot(` + "Version ID: 10000000-0000-0000-0000-000000000000 + Created: 1/1/2021, 12:00:00 AM Author: Jean-Luc-Picard@federation.org Source: wrangler Tag: - - Message: -" + Message: - + " `); expect(normalizeOutput(std.err)).toMatchInlineSnapshot(`""`); From 3c3d744891069b29cf5a22b0830f6a9c7b391953 Mon Sep 17 00:00:00 2001 From: Rahul Sethi <5822355+RamIdeas@users.noreply.github.com> Date: Fri, 8 Mar 2024 23:13:24 +0000 Subject: [PATCH 09/10] port Source display logic from legacy deployments command for versions view/list --- .../__tests__/versions/versions.list.test.ts | 16 +++---- .../__tests__/versions/versions.view.test.ts | 4 +- packages/wrangler/src/versions/list.ts | 47 +++++++++++++++++-- packages/wrangler/src/versions/view.ts | 15 +----- 4 files changed, 56 insertions(+), 26 deletions(-) diff --git a/packages/wrangler/src/__tests__/versions/versions.list.test.ts b/packages/wrangler/src/__tests__/versions/versions.list.test.ts index 83f9fbeac006..c9dd7250637f 100644 --- a/packages/wrangler/src/__tests__/versions/versions.list.test.ts +++ b/packages/wrangler/src/__tests__/versions/versions.list.test.ts @@ -44,28 +44,28 @@ describe("versions list", () => { "Version ID: 40000000-0000-0000-0000-000000000000 Created: 1/1/2021, 12:00:00 AM Author: Jean-Luc-Picard@federation.org - Source: wrangler + Source: Upload Tag: - Message: - Version ID: 30000000-0000-0000-0000-000000000000 Created: 2/2/2021, 12:00:00 AM Author: Kathryn-Janeway@federation.org - Source: wrangler + Source: Rollback Tag: - Message: Rolled back for this version Version ID: 20000000-0000-0000-0000-000000000000 Created: 2/3/2021, 12:00:00 AM Author: Kathryn-Janeway@federation.org - Source: wrangler + Source: Wrangler 🤠 Tag: - Message: - Version ID: 10000000-0000-0000-0000-000000000000 Created: 1/4/2021, 12:00:00 AM Author: Jean-Luc-Picard@federation.org - Source: wrangler + Source: Rollback Tag: - Message: - " @@ -89,28 +89,28 @@ describe("versions list", () => { "Version ID: 40000000-0000-0000-0000-000000000000 Created: 1/1/2021, 12:00:00 AM Author: Jean-Luc-Picard@federation.org - Source: wrangler + Source: Upload Tag: - Message: - Version ID: 30000000-0000-0000-0000-000000000000 Created: 2/2/2021, 12:00:00 AM Author: Kathryn-Janeway@federation.org - Source: wrangler + Source: Rollback Tag: - Message: Rolled back for this version Version ID: 20000000-0000-0000-0000-000000000000 Created: 2/3/2021, 12:00:00 AM Author: Kathryn-Janeway@federation.org - Source: wrangler + Source: Wrangler 🤠 Tag: - Message: - Version ID: 10000000-0000-0000-0000-000000000000 Created: 1/4/2021, 12:00:00 AM Author: Jean-Luc-Picard@federation.org - Source: wrangler + Source: Rollback Tag: - Message: - " diff --git a/packages/wrangler/src/__tests__/versions/versions.view.test.ts b/packages/wrangler/src/__tests__/versions/versions.view.test.ts index 7c390bf54200..05bb97cf2c99 100644 --- a/packages/wrangler/src/__tests__/versions/versions.view.test.ts +++ b/packages/wrangler/src/__tests__/versions/versions.view.test.ts @@ -112,7 +112,7 @@ describe("versions view", () => { "Version ID: 10000000-0000-0000-0000-000000000000 Created: 1/1/2021, 12:00:00 AM Author: Jean-Luc-Picard@federation.org - Source: wrangler + Source: Upload Tag: - Message: - " @@ -170,7 +170,7 @@ describe("versions view", () => { "Version ID: 10000000-0000-0000-0000-000000000000 Created: 1/1/2021, 12:00:00 AM Author: Jean-Luc-Picard@federation.org - Source: wrangler + Source: Upload Tag: - Message: - " diff --git a/packages/wrangler/src/versions/list.ts b/packages/wrangler/src/versions/list.ts index 1ff5cafe2f3c..e512cb89048f 100644 --- a/packages/wrangler/src/versions/list.ts +++ b/packages/wrangler/src/versions/list.ts @@ -29,7 +29,7 @@ type ApiVersion = { author_id: string; author_email: string; }; - annotations?: Record & { + annotations?: { "workers/triggered_by"?: "upload" | string; "workers/message"?: string; "workers/tag"?: string; @@ -75,14 +75,14 @@ export async function versionsListHandler(args: VersionsListArgs) { "Version ID:": version.id, "Created:": new Date(version.metadata["created_on"]).toLocaleString(), "Author:": version.metadata.author_email, - "Source:": version.metadata.source, + "Source:": getSource(version), "Tag:": version.annotations?.["workers/tag"] ?? BLANK_INPUT, "Message:": version.annotations?.["workers/message"] ?? BLANK_INPUT, }); } } -function getConfig( +export function getConfig( args: Pick ) { const configPath = @@ -91,3 +91,44 @@ function getConfig( return config; } + +export function getSource(version: { + metadata: Pick; + annotations?: Pick< + NonNullable, + "workers/triggered_by" + >; +}) { + return version.annotations?.["workers/triggered_by"] === undefined + ? formatSource(version.metadata.source) + : formatTrigger(version.annotations["workers/triggered_by"]); +} + +export function formatSource(source: string): string { + switch (source) { + case "api": + return "API 📡"; + case "dash": + return "Dashboard 🖥️"; + case "wrangler": + return "Wrangler 🤠"; + case "terraform": + return "Terraform 🏗️"; + default: + return "Other"; + } +} +export function formatTrigger(trigger: string): string { + switch (trigger) { + case "upload": + return "Upload"; + case "secret": + return "Secret Change"; + case "rollback": + return "Rollback"; + case "promotion": + return "Promotion"; + default: + return "Unknown"; + } +} diff --git a/packages/wrangler/src/versions/view.ts b/packages/wrangler/src/versions/view.ts index 263956de5ba9..19e6597294dc 100644 --- a/packages/wrangler/src/versions/view.ts +++ b/packages/wrangler/src/versions/view.ts @@ -1,11 +1,10 @@ -import path from "path"; import { fetchResult } from "../cfetch"; -import { findWranglerToml, readConfig } from "../config"; import { UserError } from "../errors"; import * as metrics from "../metrics"; import { printWranglerBanner } from "../update-check"; import { requireAuth } from "../user"; import renderLabelledValues from "../utils/render-labelled-values"; +import { getConfig, getSource } from "./list"; import type { CommonYargsArgv, StrictYargsOptionsToInterface, @@ -81,18 +80,8 @@ export async function versionsViewHandler(args: VersionsViewArgs) { "Version ID:": version.id, "Created:": new Date(version.metadata["created_on"]).toLocaleString(), "Author:": version.metadata.author_email, - "Source:": version.metadata.source, + "Source:": getSource(version), "Tag:": version.annotations?.["workers/tag"] ?? BLANK_INPUT, "Message:": version.annotations?.["workers/message"] ?? BLANK_INPUT, }); } - -function getConfig( - args: Pick -) { - const configPath = - args.config || (args.name && findWranglerToml(path.dirname(args.name))); - const config = readConfig(configPath, args); - - return config; -} From efd35146a1eed7bcd2c55cfb6a7b1203a8fcccbd Mon Sep 17 00:00:00 2001 From: Rahul Sethi <5822355+RamIdeas@users.noreply.github.com> Date: Tue, 12 Mar 2024 23:04:58 +0000 Subject: [PATCH 10/10] add changeset --- .changeset/spotty-vans-bow.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/spotty-vans-bow.md diff --git a/.changeset/spotty-vans-bow.md b/.changeset/spotty-vans-bow.md new file mode 100644 index 000000000000..10fc0b043789 --- /dev/null +++ b/.changeset/spotty-vans-bow.md @@ -0,0 +1,5 @@ +--- +"wrangler": minor +--- + +feature: Implement `wrangler versions list` and `wrangler versions view` commands behind the `--experimental-gradual-rollouts` flag.