Skip to content

Commit

Permalink
Versions rollback (#5373)
Browse files Browse the repository at this point in the history
  • Loading branch information
RamIdeas committed Mar 26, 2024
1 parent e11e169 commit 5bd8db8
Show file tree
Hide file tree
Showing 9 changed files with 465 additions and 95 deletions.
7 changes: 7 additions & 0 deletions .changeset/big-hounds-breathe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"wrangler": minor
---

feature: Implement versioned rollbacks via `wrangler rollback [version-id] --experimental-versions`.

Please note, the `experimental-versions` flag is required to use the new behaviour. The original `wrangler rollback` command is unchanged if run without this flag.
2 changes: 1 addition & 1 deletion packages/cli/interactive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,7 @@ export const spinner = (
}
clearLoop();
} else {
if (msg) {
if (msg !== undefined) {
logUpdate(`\n${grayBar} ${msg}`);
}
newline();
Expand Down
273 changes: 253 additions & 20 deletions packages/wrangler/e2e/versions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ function matchWhoamiEmail(stdout: string): string {
function matchVersionId(stdout: string): string {
return stdout.match(/Version ID:\s+([a-f\d-]+)/)?.[1] as string;
}
function matchDeploymentId(stdout: string): string {
return stdout.match(/Deployment ID:\s+([a-f\d-]+)/)?.[1] as string;
}
function countOccurences(stdout: string, substring: string) {
return stdout.split(substring).length - 1;
}

describe("versions deploy", () => {
let root: string;
Expand All @@ -24,6 +30,7 @@ describe("versions deploy", () => {
let runInRoot: typeof shellac;
let runInWorker: typeof shellac;
let normalize: (str: string) => string;
let versionId0: string;
let versionId1: string;
let versionId2: string;

Expand All @@ -46,17 +53,19 @@ describe("versions deploy", () => {

it("init worker", async () => {
const init =
await runInRoot`$ ${WRANGLER} init --yes --no-delegate-c3 ${workerName}`;
await runInRoot`$ ${WRANGLER} init ${workerName} --yes --no-delegate-c3`;

expect(normalize(init.stdout)).toContain(
"To publish your Worker to the Internet, run `npm run deploy`"
);

// TEMP: wrangler deploy needed for the first time to *create* the worker (will create 1 extra version + deployment in snapshots below)
await runInWorker`$ ${WRANGLER} deploy`;
// TEMP: regular deploy needed for the first time to *create* the worker (will create 1 extra version + deployment in snapshots below)
const deploy = await runInWorker`$ ${WRANGLER} deploy`;

versionId0 = matchDeploymentId(deploy.stdout);
});

it("upload worker version (with message and tag)", async () => {
it("upload 1st worker version", async () => {
const upload =
await runInWorker`$ ${WRANGLER} versions upload --message "Upload via e2e test" --tag "e2e-upload" --x-versions`;

Expand Down Expand Up @@ -91,7 +100,7 @@ describe("versions deploy", () => {
expect(list.stdout).toMatch(/Tag:\s+e2e-upload/);
});

it("deploy worker version", async () => {
it("deploy 1st worker version", async () => {
const deploy =
await runInWorker`$ ${WRANGLER} versions deploy ${versionId1}@100% --message "Deploy via e2e test" --yes --x-versions`;

Expand Down Expand Up @@ -157,7 +166,7 @@ describe("versions deploy", () => {
expect(list.stdout).toContain(versionId1);
});

it("modify & upload worker version", async () => {
it("modify & upload 2nd worker version", async () => {
await seed(workerPath, {
"src/index.ts": dedent`
export default {
Expand All @@ -177,12 +186,11 @@ describe("versions deploy", () => {
Worker Version ID: 00000000-0000-0000-0000-000000000000
Uploaded tmp-e2e-wrangler (TIMINGS)"
`);
});

it("list 2 versions", async () => {
const list = await runInWorker`$ ${WRANGLER} versions list --x-versions`;
const versionsList =
await runInWorker`$ ${WRANGLER} versions list --x-versions`;

expect(normalize(list.stdout)).toMatchInlineSnapshot(`
expect(normalize(versionsList.stdout)).toMatchInlineSnapshot(`
"Version ID: 00000000-0000-0000-0000-000000000000
Created: TIMESTAMP
Author: person@example.com
Expand All @@ -203,14 +211,17 @@ describe("versions deploy", () => {
Message: -"
`);

expect(list.stdout).toMatch(/Message:\s+Upload AGAIN via e2e test/);
expect(list.stdout).toMatch(/Tag:\s+e2e-upload-AGAIN/);
expect(versionsList.stdout).toMatch(/Message:\s+Upload AGAIN via e2e test/);
expect(versionsList.stdout).toMatch(/Tag:\s+e2e-upload-AGAIN/);
});

it("deploy worker version", async () => {
it("deploy 2nd worker version", async () => {
const deploy =
await runInWorker`$ ${WRANGLER} versions deploy ${versionId2}@100% --message "Deploy AGAIN via e2e test" --yes --x-versions`;

const deploymentsList =
await runInWorker`$ ${WRANGLER} deployments list --x-versions`;

expect(normalize(deploy.stdout)).toMatchInlineSnapshot(`
"╭ Deploy Worker Versions by splitting traffic between multiple versions
Expand Down Expand Up @@ -244,16 +255,124 @@ describe("versions deploy", () => {
╰ SUCCESS Deployed tmp-e2e-wrangler version 00000000-0000-0000-0000-000000000000 at 100% (TIMINGS)"
`);

// list 2 deployments (+ old deployment)
expect(normalize(deploymentsList.stdout)).toMatchInlineSnapshot(`
"Created: TIMESTAMP
Author: person@example.com
Source: Unknown (deployment)
Message: Deploy AGAIN via e2e test
Version(s): (100%) 00000000-0000-0000-0000-000000000000
Created: TIMESTAMP
Tag: e2e-upload-AGAIN
Message: Upload AGAIN via e2e test
Created: TIMESTAMP
Author: person@example.com
Source: Unknown (deployment)
Message: Deploy via e2e test
Version(s): (100%) 00000000-0000-0000-0000-000000000000
Created: TIMESTAMP
Tag: e2e-upload
Message: Upload via e2e test
Created: TIMESTAMP
Author: person@example.com
Source: Upload
Message: Automatic deployment on upload.
Version(s): (100%) 00000000-0000-0000-0000-000000000000
Created: TIMESTAMP
Tag: -
Message: -"
`);
expect(deploymentsList.stderr).toMatchInlineSnapshot('""');

expect(countOccurences(deploymentsList.stdout, versionId0)).toBe(1); // once for regular deploy, only
expect(countOccurences(deploymentsList.stdout, versionId1)).toBe(1); // once for versions deploy, only
expect(countOccurences(deploymentsList.stdout, versionId2)).toBe(1); // once for versions deploy, only
});

it("list 2 deployments", async () => {
const list =
it("rollback to implicit worker version (1st version)", async () => {
const rollback =
await runInWorker`$ ${WRANGLER} rollback --message "Rollback via e2e test" --yes --x-versions`;

const versionsList =
await runInWorker`$ ${WRANGLER} versions list --x-versions`;

const deploymentsList =
await runInWorker`$ ${WRANGLER} deployments list --x-versions`;

expect(normalize(list.stdout)).toMatchInlineSnapshot(`
expect(normalize(rollback.stdout)).toMatchInlineSnapshot(`
"├ Fetching latest deployment
├ Your current deployment has 1 version(s):
│ (100%) 00000000-0000-0000-0000-000000000000
│ Created: TIMESTAMP
│ Tag: e2e-upload-AGAIN
│ Message: Upload AGAIN via e2e test
├ Finding latest stable Worker Version to rollback to
├ Please provide a message for this rollback (120 characters max, optional)?
│ Message Rollback via e2e test
╰ WARNING You are about to rollback to Worker Version 00000000-0000-0000-0000-000000000000:
│ (100%) 00000000-0000-0000-0000-000000000000
│ Created: TIMESTAMP
│ Tag: e2e-upload
│ Message: Upload via e2e test
├ Are you sure you want to deploy this Worker Version to 100% of traffic?
│ yes Rollback
├ Performing rollback
╰ SUCCESS Worker Version 00000000-0000-0000-0000-000000000000 has been deployed to 100% of traffic."
`);

expect(rollback.stdout).toContain(
`Worker Version ${versionId1} has been deployed to 100% of traffic`
);

// list same versions as before (no new versions created)
expect(normalize(versionsList.stdout)).toMatchInlineSnapshot(`
"Version ID: 00000000-0000-0000-0000-000000000000
Created: TIMESTAMP
Author: person@example.com
Source: Unknown (version_upload)
Tag: e2e-upload-AGAIN
Message: Upload AGAIN via e2e test
Version ID: 00000000-0000-0000-0000-000000000000
Created: TIMESTAMP
Author: person@example.com
Source: Unknown (version_upload)
Tag: e2e-upload
Message: Upload via e2e test
Version ID: 00000000-0000-0000-0000-000000000000
Created: TIMESTAMP
Author: person@example.com
Source: Upload
Tag: -
Message: -"
`);

// list deployments with new rollback deployment of 1st version (1 new deployment created)
expect(normalize(deploymentsList.stdout)).toMatchInlineSnapshot(`
"Created: TIMESTAMP
Author: person@example.com
Source: Unknown (deployment)
Message: Rollback via e2e test
Version(s): (100%) 00000000-0000-0000-0000-000000000000
Created: TIMESTAMP
Tag: e2e-upload
Message: Upload via e2e test
Created: TIMESTAMP
Author: person@example.com
Source: Unknown (deployment)
Message: Deploy AGAIN via e2e test
Version(s): (100%) 00000000-0000-0000-0000-000000000000
Created: TIMESTAMP
Expand All @@ -276,13 +395,127 @@ describe("versions deploy", () => {
Tag: -
Message: -"
`);
expect(list.stderr).toMatchInlineSnapshot('""');

expect(list.stdout).toContain(versionId1);
expect(list.stdout).toContain(versionId2);
expect(countOccurences(deploymentsList.stdout, versionId0)).toBe(1); // once for regular deploy, only
expect(countOccurences(deploymentsList.stdout, versionId1)).toBe(2); // once for versions deploy, once for rollback
expect(countOccurences(deploymentsList.stdout, versionId2)).toBe(1); // once for versions deploy, only
});

// TODO: rollback, when supported for --x-versions
it("rollback to specific worker version (0th version)", async () => {
const rollback =
await runInWorker`$ ${WRANGLER} rollback ${versionId0} --message "Rollback to old version" --yes --x-versions`;

const versionsList =
await runInWorker`$ ${WRANGLER} versions list --x-versions`;

const deploymentsList =
await runInWorker`$ ${WRANGLER} deployments list --x-versions`;

expect(normalize(rollback.stdout)).toMatchInlineSnapshot(`
"├ Fetching latest deployment
├ Your current deployment has 1 version(s):
│ (100%) 00000000-0000-0000-0000-000000000000
│ Created: TIMESTAMP
│ Tag: e2e-upload
│ Message: Upload via e2e test
├ Please provide a message for this rollback (120 characters max, optional)?
│ Message Rollback to old version
╰ WARNING You are about to rollback to Worker Version 00000000-0000-0000-0000-000000000000:
│ (100%) 00000000-0000-0000-0000-000000000000
│ Created: TIMESTAMP
│ Tag: -
│ Message: -
├ Are you sure you want to deploy this Worker Version to 100% of traffic?
│ yes Rollback
├ Performing rollback
╰ SUCCESS Worker Version 00000000-0000-0000-0000-000000000000 has been deployed to 100% of traffic."
`);

expect(rollback.stdout).toContain(
`Worker Version ${versionId0} has been deployed to 100% of traffic`
);

// list same versions as before (no new versions created)
expect(normalize(versionsList.stdout)).toMatchInlineSnapshot(`
"Version ID: 00000000-0000-0000-0000-000000000000
Created: TIMESTAMP
Author: person@example.com
Source: Unknown (version_upload)
Tag: e2e-upload-AGAIN
Message: Upload AGAIN via e2e test
Version ID: 00000000-0000-0000-0000-000000000000
Created: TIMESTAMP
Author: person@example.com
Source: Unknown (version_upload)
Tag: e2e-upload
Message: Upload via e2e test
Version ID: 00000000-0000-0000-0000-000000000000
Created: TIMESTAMP
Author: person@example.com
Source: Upload
Tag: -
Message: -"
`);

// list deployments with new rollback deployment of 0th version (1 new deployment created)
expect(normalize(deploymentsList.stdout)).toMatchInlineSnapshot(`
"Created: TIMESTAMP
Author: person@example.com
Source: Unknown (deployment)
Message: Rollback to old version
Version(s): (100%) 00000000-0000-0000-0000-000000000000
Created: TIMESTAMP
Tag: -
Message: -
Created: TIMESTAMP
Author: person@example.com
Source: Unknown (deployment)
Message: Rollback via e2e test
Version(s): (100%) 00000000-0000-0000-0000-000000000000
Created: TIMESTAMP
Tag: e2e-upload
Message: Upload via e2e test
Created: TIMESTAMP
Author: person@example.com
Source: Unknown (deployment)
Message: Deploy AGAIN via e2e test
Version(s): (100%) 00000000-0000-0000-0000-000000000000
Created: TIMESTAMP
Tag: e2e-upload-AGAIN
Message: Upload AGAIN via e2e test
Created: TIMESTAMP
Author: person@example.com
Source: Unknown (deployment)
Message: Deploy via e2e test
Version(s): (100%) 00000000-0000-0000-0000-000000000000
Created: TIMESTAMP
Tag: e2e-upload
Message: Upload via e2e test
Created: TIMESTAMP
Author: person@example.com
Source: Upload
Message: Automatic deployment on upload.
Version(s): (100%) 00000000-0000-0000-0000-000000000000
Created: TIMESTAMP
Tag: -
Message: -"
`);

expect(countOccurences(deploymentsList.stdout, versionId0)).toBe(2); // once for regular deploy, once for rollback
expect(countOccurences(deploymentsList.stdout, versionId1)).toBe(2); // once for versions deploy, once for rollback
expect(countOccurences(deploymentsList.stdout, versionId2)).toBe(1); // once for versions deploy, only
});

it("delete worker", async () => {
const { stdout, stderr } = await runInWorker`$ ${WRANGLER} delete`;
Expand Down

0 comments on commit 5bd8db8

Please sign in to comment.