Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement versions list + versions view commands #5208

Merged
merged 10 commits into from
Mar 13, 2024
5 changes: 5 additions & 0 deletions .changeset/spotty-vans-bow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wrangler": minor
---

feature: Implement `wrangler versions list` and `wrangler versions view` commands behind the `--experimental-gradual-rollouts` flag.
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand All @@ -45,7 +45,7 @@ describe("versions deploy", () => {
mockAccountId();
mockApiToken();
runInTempDir();
const std = mockProcessOutput();
const std = collectStreamOutput();

beforeEach(() => {
msw.use(
Expand Down
122 changes: 122 additions & 0 deletions packages/wrangler/src/__tests__/versions/versions.list.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
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";
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 <name>\` or in your config file as \`name = "<name>"\`]`
);

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 <name>\` or in your config file as \`name = \\"<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: Upload
Tag: -
Message: -

Version ID: 30000000-0000-0000-0000-000000000000
Created: 2/2/2021, 12:00:00 AM
Author: Kathryn-Janeway@federation.org
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 🤠
Tag: -
Message: -

Version ID: 10000000-0000-0000-0000-000000000000
Created: 1/4/2021, 12:00:00 AM
Author: Jean-Luc-Picard@federation.org
Source: Rollback
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: Upload
Tag: -
Message: -

Version ID: 30000000-0000-0000-0000-000000000000
Created: 2/2/2021, 12:00:00 AM
Author: Kathryn-Janeway@federation.org
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 🤠
Tag: -
Message: -

Version ID: 10000000-0000-0000-0000-000000000000
Created: 1/4/2021, 12:00:00 AM
Author: Jean-Luc-Picard@federation.org
Source: Rollback
Tag: -
Message: -
"
`);

expect(std.err).toMatchInlineSnapshot(`""`);
});
});
});
200 changes: 200 additions & 0 deletions packages/wrangler/src/__tests__/versions/versions.view.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
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(std.out).toMatchInlineSnapshot(`
"
wrangler versions view <version-id>

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(std.out).toMatchInlineSnapshot(`
"
wrangler versions view <version-id>

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 <name>\` or in your config file as \`name = "<name>"\`]`
);

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 <name>\` or in your config file as \`name = \\"<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(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: Upload
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(std.out).toMatchInlineSnapshot(`
"
wrangler versions view <version-id>

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(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: Upload
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(`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this not be logged to stderr?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. This is just the fetchResult call rejecting. Which should bubble up to the main() try..catch error handling. Does wrangler log errors to stdout??

"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(`""`);
});
});
});
22 changes: 2 additions & 20 deletions packages/wrangler/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 3 additions & 1 deletion packages/wrangler/src/metrics/send-event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ export type EventNames =
| "view deployments"
| "rollback deployments"
| "upload worker version"
| "deploy worker versions";
| "deploy worker versions"
| "view worker version"
| "list worker versions";

/**
* Send a metrics event, with no extra properties, to Cloudflare, if usage tracking is enabled.
Expand Down
Loading
Loading