wrangler 2.12.3 → 2.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/package.json +11 -7
  2. package/src/__tests__/api-devregistry.test.ts +121 -0
  3. package/src/__tests__/api.test.ts +27 -5
  4. package/src/__tests__/configuration.test.ts +59 -12
  5. package/src/__tests__/deployments.test.ts +335 -95
  6. package/src/__tests__/helpers/msw/handlers/deployments.ts +70 -3
  7. package/src/__tests__/helpers/msw/index.ts +4 -2
  8. package/src/__tests__/index.test.ts +10 -4
  9. package/src/__tests__/jest.setup.ts +4 -0
  10. package/src/__tests__/mtls-certificates.test.ts +5 -2
  11. package/src/__tests__/pages/publish.test.ts +67 -23
  12. package/src/__tests__/publish.test.ts +138 -59
  13. package/src/__tests__/queues.test.ts +5 -2
  14. package/src/__tests__/traverse-module-graph.test.ts +220 -0
  15. package/src/api/dev.ts +7 -18
  16. package/src/api/pages/create-worker-bundle-contents.ts +1 -0
  17. package/src/bundle.ts +19 -5
  18. package/src/config/environment.ts +27 -0
  19. package/src/config/index.ts +18 -0
  20. package/src/config/validation.ts +91 -1
  21. package/src/create-worker-upload-form.ts +18 -1
  22. package/src/d1/execute.tsx +1 -1
  23. package/src/d1/migrations/apply.tsx +2 -1
  24. package/src/deployments.ts +260 -8
  25. package/src/dev/start-server.ts +2 -8
  26. package/src/dev/use-esbuild.ts +2 -8
  27. package/src/dev-registry.ts +2 -1
  28. package/src/dev.tsx +1 -0
  29. package/src/entry.ts +18 -8
  30. package/src/index.ts +75 -22
  31. package/src/init.ts +144 -135
  32. package/src/metrics/send-event.ts +2 -1
  33. package/src/module-collection.ts +91 -25
  34. package/src/pages/functions/buildPlugin.ts +1 -0
  35. package/src/pages/functions/buildWorker.ts +2 -8
  36. package/src/publish/publish.ts +10 -26
  37. package/src/queues/cli/commands/consumer/add.ts +6 -0
  38. package/src/queues/client.ts +1 -0
  39. package/src/secret/index.ts +1 -0
  40. package/src/traverse-module-graph.ts +53 -0
  41. package/src/worker.ts +10 -0
  42. package/wrangler-dist/cli.d.ts +24 -0
  43. package/wrangler-dist/cli.js +19083 -18680
  44. package/src/__tests__/api-devregistry.test.js +0 -64
  45. package/src/__tests__/tsconfig.tsbuildinfo +0 -1
  46. package/src/miniflare-cli/tsconfig.tsbuildinfo +0 -1
  47. package/src/pages/functions/tsconfig.tsbuildinfo +0 -1
  48. package/templates/__tests__/tsconfig.tsbuildinfo +0 -1
  49. package/templates/tsconfig.tsbuildinfo +0 -1
@@ -1,9 +1,21 @@
1
1
  import { URLSearchParams } from "url";
2
+ import TOML from "@iarna/toml";
3
+ import chalk from "chalk";
4
+ import { FormData } from "undici";
2
5
  import { fetchResult } from "./cfetch";
6
+ import { readConfig } from "./config";
7
+ import { confirm, prompt } from "./dialogs";
8
+ import { mapBindings } from "./init";
3
9
  import { logger } from "./logger";
4
10
  import * as metrics from "./metrics";
11
+ import { requireAuth } from "./user";
12
+ import { getScriptName, printWranglerBanner } from ".";
13
+
5
14
  import type { Config } from "./config";
15
+ import type { WorkerMetadataBinding } from "./create-worker-upload-form";
6
16
  import type { ServiceMetadataRes } from "./init";
17
+ import type { CommonYargsOptions } from "./yargs-types";
18
+ import type { ArgumentsCamelCase } from "yargs";
7
19
 
8
20
  type DeploymentDetails = {
9
21
  id: string;
@@ -11,6 +23,7 @@ type DeploymentDetails = {
11
23
  annotations: {
12
24
  "workers/triggered_by": string;
13
25
  "workers/rollback_from": string;
26
+ "workers/message": string;
14
27
  };
15
28
  metadata: {
16
29
  author_id: string;
@@ -20,8 +33,15 @@ type DeploymentDetails = {
20
33
  modified_on: string;
21
34
  };
22
35
  resources: {
23
- script: string;
24
- bindings: unknown[];
36
+ script: {
37
+ handlers: string[];
38
+ };
39
+ bindings: WorkerMetadataBinding[];
40
+ script_runtime: {
41
+ compatibility_date: string | undefined;
42
+ compatibility_flags: string[] | undefined;
43
+ usage_model: string | undefined;
44
+ };
25
45
  };
26
46
  };
27
47
 
@@ -49,11 +69,12 @@ export async function deployments(
49
69
  }
50
70
  );
51
71
 
52
- const scriptMetadata = await fetchResult<ServiceMetadataRes>(
53
- `/accounts/${accountId}/workers/services/${scriptName}`
54
- );
72
+ const scriptTag = (
73
+ await fetchResult<ServiceMetadataRes>(
74
+ `/accounts/${accountId}/workers/services/${scriptName}`
75
+ )
76
+ ).default_environment.script.tag;
55
77
 
56
- const scriptTag = scriptMetadata.default_environment.script.tag;
57
78
  const params = new URLSearchParams({ order: "asc" });
58
79
  const { items: deploys } = await fetchResult<DeploymentListResult>(
59
80
  `/accounts/${accountId}/workers/deployments/by-script/${scriptTag}`,
@@ -72,12 +93,16 @@ export async function deployments(
72
93
  Deployment ID: ${versions.id}
73
94
  Created on: ${versions.metadata.created_on}
74
95
  Author: ${versions.metadata.author_email}
75
- Trigger: ${triggerStr}`;
96
+ Source: ${triggerStr}`;
76
97
 
77
98
  if (versions.annotations?.["workers/rollback_from"]) {
78
99
  version += `\nRollback from: ${versions.annotations["workers/rollback_from"]}`;
79
100
  }
80
101
 
102
+ if (versions.annotations?.["workers/message"]) {
103
+ version += `\nMessage: ${versions.annotations["workers/message"]}`;
104
+ }
105
+
81
106
  return version + `\n`;
82
107
  });
83
108
 
@@ -99,7 +124,6 @@ function formatSource(source: string): string {
99
124
  return "Other";
100
125
  }
101
126
  }
102
-
103
127
  function formatTrigger(trigger: string): string {
104
128
  switch (trigger) {
105
129
  case "upload":
@@ -114,3 +138,231 @@ function formatTrigger(trigger: string): string {
114
138
  return "Unknown";
115
139
  }
116
140
  }
141
+
142
+ export async function rollbackDeployment(
143
+ accountId: string,
144
+ scriptName: string | undefined,
145
+ { send_metrics: sendMetrics }: { send_metrics?: Config["send_metrics"] } = {},
146
+ deploymentId: string | undefined,
147
+ message: string | undefined
148
+ ) {
149
+ if (deploymentId === undefined) {
150
+ const scriptTag = (
151
+ await fetchResult<ServiceMetadataRes>(
152
+ `/accounts/${accountId}/workers/services/${scriptName}`
153
+ )
154
+ ).default_environment.script.tag;
155
+
156
+ const params = new URLSearchParams({ order: "asc" });
157
+ const { items: deploys } = await fetchResult<DeploymentListResult>(
158
+ `/accounts/${accountId}/workers/deployments/by-script/${scriptTag}`,
159
+ undefined,
160
+ params
161
+ );
162
+
163
+ if (deploys.length < 2) {
164
+ throw new Error(
165
+ "Cannot rollback to previous deployment since there are less than 2 deployemnts"
166
+ );
167
+ }
168
+
169
+ deploymentId = deploys.at(-2)?.id;
170
+ if (deploymentId === undefined) {
171
+ throw new Error("Cannot find previous deployment");
172
+ }
173
+ }
174
+
175
+ const firstHash = deploymentId.substring(0, deploymentId.indexOf("-"));
176
+
177
+ let rollbackMessage = "";
178
+ if (message !== undefined) {
179
+ rollbackMessage = message;
180
+ } else {
181
+ if (
182
+ !(await confirm(
183
+ `This deployment ${chalk.underline(
184
+ firstHash
185
+ )} will immediately replace the current deployment and become the active deployment across all your deployed routes and domains. However, your local development environment will not be affected by this rollback. ${chalk.blue.bold(
186
+ "Note:"
187
+ )} Rolling back to a previous deployment will not rollback any of the bound resources (Durable Object, R2, KV, etc.).`
188
+ ))
189
+ ) {
190
+ return;
191
+ }
192
+
193
+ rollbackMessage = await prompt(
194
+ "Please provide a message for this rollback (120 characters max)",
195
+ { defaultValue: "" }
196
+ );
197
+ }
198
+
199
+ let deployment_id = await rollbackRequest(
200
+ accountId,
201
+ scriptName,
202
+ deploymentId,
203
+ rollbackMessage
204
+ );
205
+
206
+ await metrics.sendMetricsEvent(
207
+ "rollback deployments",
208
+ { view: scriptName ? "single" : "all" },
209
+ {
210
+ sendMetrics,
211
+ }
212
+ );
213
+
214
+ deploymentId = addHyphens(deploymentId) ?? deploymentId;
215
+ deployment_id = addHyphens(deployment_id) ?? deployment_id;
216
+
217
+ logger.log(`\nSuccessfully rolled back to Deployment ID: ${deploymentId}`);
218
+ logger.log("Current Deployment ID:", deployment_id);
219
+ }
220
+
221
+ async function rollbackRequest(
222
+ accountId: string,
223
+ scriptName: string | undefined,
224
+ deploymentId: string,
225
+ rollbackReason: string
226
+ ): Promise<string | null> {
227
+ const body = new FormData();
228
+ body.set("message", rollbackReason);
229
+
230
+ const { deployment_id } = await fetchResult<{
231
+ deployment_id: string | null;
232
+ }>(
233
+ `/accounts/${accountId}/workers/scripts/${scriptName}?rollback_to=${deploymentId}`,
234
+ {
235
+ method: "PUT",
236
+ body,
237
+ }
238
+ );
239
+
240
+ return deployment_id;
241
+ }
242
+
243
+ export async function viewDeployment(
244
+ accountId: string,
245
+ scriptName: string | undefined,
246
+ { send_metrics: sendMetrics }: { send_metrics?: Config["send_metrics"] } = {},
247
+ deploymentId: string | undefined
248
+ ) {
249
+ await metrics.sendMetricsEvent(
250
+ "view deployments",
251
+ { view: scriptName ? "single" : "all" },
252
+ {
253
+ sendMetrics,
254
+ }
255
+ );
256
+
257
+ const scriptTag = (
258
+ await fetchResult<ServiceMetadataRes>(
259
+ `/accounts/${accountId}/workers/services/${scriptName}`
260
+ )
261
+ ).default_environment.script.tag;
262
+
263
+ if (deploymentId === undefined) {
264
+ const params = new URLSearchParams({ order: "asc" });
265
+ const { latest } = await fetchResult<DeploymentListResult>(
266
+ `/accounts/${accountId}/workers/deployments/by-script/${scriptTag}`,
267
+ undefined,
268
+ params
269
+ );
270
+
271
+ deploymentId = latest.id;
272
+ if (deploymentId === undefined) {
273
+ throw new Error("Cannot find previous deployment");
274
+ }
275
+ }
276
+
277
+ const deploymentDetails = await fetchResult<DeploymentListResult["latest"]>(
278
+ `/accounts/${accountId}/workers/deployments/by-script/${scriptTag}/detail/${deploymentId}`
279
+ );
280
+
281
+ const triggerStr = deploymentDetails.annotations?.["workers/triggered_by"]
282
+ ? `${formatTrigger(
283
+ deploymentDetails.annotations["workers/triggered_by"]
284
+ )} from ${formatSource(deploymentDetails.metadata.source)}`
285
+ : `${formatSource(deploymentDetails.metadata.source)}`;
286
+
287
+ const rollbackStr = deploymentDetails.annotations?.["workers/rollback_from"]
288
+ ? `\nRollback from: ${deploymentDetails.annotations["workers/rollback_from"]}`
289
+ : ``;
290
+
291
+ const reasonStr = deploymentDetails.annotations?.["workers/message"]
292
+ ? `\nMessage: ${deploymentDetails.annotations["workers/message"]}`
293
+ : ``;
294
+
295
+ const compatDateStr = deploymentDetails.resources.script_runtime
296
+ ?.compatibility_date
297
+ ? `\nCompatibility Date: ${deploymentDetails.resources.script_runtime?.compatibility_date}`
298
+ : ``;
299
+ const compatFlagsStr = deploymentDetails.resources.script_runtime
300
+ ?.compatibility_flags
301
+ ? `\nCompatibility Flags: ${deploymentDetails.resources.script_runtime?.compatibility_flags}`
302
+ : ``;
303
+
304
+ const bindings = deploymentDetails.resources.bindings;
305
+
306
+ const version = `
307
+ Deployment ID: ${deploymentDetails.id}
308
+ Created on: ${deploymentDetails.metadata.created_on}
309
+ Author: ${deploymentDetails.metadata.author_email}
310
+ Source: ${triggerStr}${rollbackStr}${reasonStr}
311
+ ------------------------------------------------------------
312
+ Author ID: ${deploymentDetails.metadata.author_id}
313
+ Usage Model: ${deploymentDetails.resources.script_runtime.usage_model}
314
+ Handlers: ${
315
+ deploymentDetails.resources.script.handlers
316
+ }${compatDateStr}${compatFlagsStr}
317
+ --------------------------bindings--------------------------
318
+ ${
319
+ bindings.length > 0
320
+ ? TOML.stringify(mapBindings(bindings) as TOML.JsonMap)
321
+ : `None`
322
+ }
323
+ `;
324
+
325
+ logger.log(version);
326
+
327
+ // early return to skip the deployments listings
328
+ return;
329
+ }
330
+
331
+ export async function commonDeploymentCMDSetup(
332
+ yargs: ArgumentsCamelCase<CommonYargsOptions>,
333
+ deploymentsWarning: string
334
+ ) {
335
+ await printWranglerBanner();
336
+ const config = readConfig(yargs.config, yargs);
337
+ const accountId = await requireAuth(config);
338
+ const scriptName = getScriptName(
339
+ { name: yargs.name as string, env: undefined },
340
+ config
341
+ );
342
+
343
+ logger.log(`${deploymentsWarning}\n`);
344
+
345
+ return { accountId, scriptName, config };
346
+ }
347
+
348
+ export function addHyphens(uuid: string | null): string | null {
349
+ if (uuid == null) {
350
+ return uuid;
351
+ }
352
+
353
+ if (uuid.length != 32) {
354
+ return null;
355
+ }
356
+
357
+ const uuid_parts: string[] = [];
358
+ uuid_parts.push(uuid.slice(0, 8));
359
+ uuid_parts.push(uuid.slice(8, 12));
360
+ uuid_parts.push(uuid.slice(12, 16));
361
+ uuid_parts.push(uuid.slice(16, 20));
362
+ uuid_parts.push(uuid.slice(20));
363
+
364
+ let hyphenated = "";
365
+ uuid_parts.forEach((part) => (hyphenated += part + "-"));
366
+
367
+ return hyphenated.slice(0, 36);
368
+ }
@@ -14,6 +14,7 @@ import {
14
14
  import { runCustomBuild } from "../entry";
15
15
  import { logger } from "../logger";
16
16
  import { waitForPortToBeAvailable } from "../proxy";
17
+ import traverseModuleGraph from "../traverse-module-graph";
17
18
  import {
18
19
  setupBindings,
19
20
  getMiniflare3,
@@ -253,14 +254,7 @@ async function runEsbuild({
253
254
  dependencies,
254
255
  sourceMapPath,
255
256
  }: Awaited<ReturnType<typeof bundleWorker>> = noBundle
256
- ? {
257
- modules: [],
258
- dependencies: {},
259
- resolvedEntryPointPath: entry.file,
260
- bundleType: entry.format === "modules" ? "esm" : "commonjs",
261
- stop: undefined,
262
- sourceMapPath: undefined,
263
- }
257
+ ? await traverseModuleGraph(entry, rules)
264
258
  : await bundleWorker(entry, destination, {
265
259
  serveAssetsFromWorker,
266
260
  jsxFactory,
@@ -4,6 +4,7 @@ import { useApp } from "ink";
4
4
  import { useState, useEffect } from "react";
5
5
  import { bundleWorker, rewriteNodeCompatBuildFailure } from "../bundle";
6
6
  import { logBuildFailure, logger } from "../logger";
7
+ import traverseModuleGraph from "../traverse-module-graph";
7
8
  import type { Config } from "../config";
8
9
  import type { WorkerRegistry } from "../dev-registry";
9
10
  import type { Entry } from "../entry";
@@ -107,14 +108,7 @@ export function useEsbuild({
107
108
  stop,
108
109
  sourceMapPath,
109
110
  }: Awaited<ReturnType<typeof bundleWorker>> = noBundle
110
- ? {
111
- modules: [],
112
- dependencies: {},
113
- resolvedEntryPointPath: entry.file,
114
- bundleType: entry.format === "modules" ? "esm" : "commonjs",
115
- stop: undefined,
116
- sourceMapPath: undefined,
117
- }
111
+ ? await traverseModuleGraph(entry, rules)
118
112
  : await bundleWorker(entry, destination, {
119
113
  serveAssetsFromWorker,
120
114
  jsxFactory,
@@ -13,7 +13,7 @@ import type { HttpTerminator } from "http-terminator";
13
13
  const DEV_REGISTRY_PORT = "6284";
14
14
  const DEV_REGISTRY_HOST = `http://localhost:${DEV_REGISTRY_PORT}`;
15
15
 
16
- let server: Server;
16
+ let server: Server | null;
17
17
  let terminator: HttpTerminator;
18
18
 
19
19
  export type WorkerRegistry = Record<string, WorkerDefinition>;
@@ -102,6 +102,7 @@ export async function startWorkerRegistry() {
102
102
  */
103
103
  export async function stopWorkerRegistry() {
104
104
  await terminator?.terminate();
105
+ server = null;
105
106
  }
106
107
 
107
108
  /**
package/src/dev.tsx CHANGED
@@ -852,6 +852,7 @@ function getBindings(
852
852
  ),
853
853
  ...(args.kv || []),
854
854
  ],
855
+ send_email: configParam.send_email,
855
856
  // Use a copy of combinedVars since we're modifying it later
856
857
  vars: {
857
858
  ...getVarsForDev(configParam, env),
package/src/entry.ts CHANGED
@@ -3,6 +3,7 @@ import { existsSync, statSync } from "node:fs";
3
3
  import path from "node:path";
4
4
  import * as esbuild from "esbuild";
5
5
  import { execaCommand } from "execa";
6
+ import { COMMON_ESBUILD_OPTIONS } from "./bundle";
6
7
  import { logger } from "./logger";
7
8
  import { getBasePath } from "./paths";
8
9
  import type { Config } from "./config";
@@ -15,7 +16,16 @@ import type { Metafile } from "esbuild";
15
16
  *
16
17
  * It consists not just of a `file`, but also of a `directory` that is used to resolve relative paths.
17
18
  */
18
- export type Entry = { file: string; directory: string; format: CfScriptFormat };
19
+ export type Entry = {
20
+ /** A worker's entrypoint */
21
+ file: string;
22
+ /** A worker's directory. Usually where the wrangler.toml file is located */
23
+ directory: string;
24
+ /** Is this a module worker or a service worker? */
25
+ format: CfScriptFormat;
26
+ /** The directory that contains all of a `--no-bundle` worker's modules. Usually `${directory}/src`. Defaults to path.dirname(file) */
27
+ moduleRoot: string;
28
+ };
19
29
 
20
30
  /**
21
31
  * Compute the entry-point for the Worker.
@@ -99,7 +109,12 @@ export async function getEntry(
99
109
  );
100
110
  }
101
111
 
102
- return { file, directory, format };
112
+ return {
113
+ file,
114
+ directory,
115
+ format,
116
+ moduleRoot: config.base_dir ?? path.dirname(file),
117
+ };
103
118
  }
104
119
 
105
120
  export async function runCustomBuild(
@@ -147,17 +162,12 @@ export default async function guessWorkerFormat(
147
162
  tsconfig?: string | undefined
148
163
  ): Promise<CfScriptFormat> {
149
164
  const result = await esbuild.build({
165
+ ...COMMON_ESBUILD_OPTIONS,
150
166
  entryPoints: [entryFile],
151
167
  absWorkingDir: entryWorkingDirectory,
152
168
  metafile: true,
153
169
  bundle: false,
154
- target: "es2022",
155
170
  write: false,
156
- loader: {
157
- ".js": "jsx",
158
- ".mjs": "jsx",
159
- ".cjs": "jsx",
160
- },
161
171
  ...(tsconfig && { tsconfig }),
162
172
  });
163
173
  // result.metafile is defined because of the `metafile: true` option above.
package/src/index.ts CHANGED
@@ -9,7 +9,12 @@ import { isBuildFailure } from "./bundle";
9
9
  import { loadDotEnv, readConfig } from "./config";
10
10
  import { d1 } from "./d1";
11
11
  import { deleteHandler, deleteOptions } from "./delete";
12
- import { deployments } from "./deployments";
12
+ import {
13
+ deployments,
14
+ commonDeploymentCMDSetup,
15
+ rollbackDeployment,
16
+ viewDeployment,
17
+ } from "./deployments";
13
18
  import {
14
19
  buildHandler,
15
20
  buildOptions,
@@ -41,13 +46,7 @@ import { secret, secretBulkHandler, secretBulkOptions } from "./secret";
41
46
  import { tailOptions, tailHandler } from "./tail";
42
47
  import { generateTypes } from "./type-generation";
43
48
  import { updateCheck } from "./update-check";
44
- import {
45
- listScopes,
46
- login,
47
- logout,
48
- requireAuth,
49
- validateScopeKeys,
50
- } from "./user";
49
+ import { listScopes, login, logout, validateScopeKeys } from "./user";
51
50
  import { whoami } from "./whoami";
52
51
 
53
52
  import type { Config } from "./config";
@@ -581,27 +580,81 @@ export function createCLIParser(argv: string[]) {
581
580
  "🚧`wrangler deployments` is a beta command. Please report any issues to https://github.com/cloudflare/workers-sdk/issues/new/choose";
582
581
  wrangler.command(
583
582
  "deployments",
584
- "🚢 Displays the 10 most recent deployments for a worker",
583
+ "🚢 List and view details for deployments",
585
584
  (yargs) =>
586
585
  yargs
587
586
  .option("name", {
588
587
  describe: "The name of your worker",
589
588
  type: "string",
590
589
  })
591
- .epilogue(deploymentsWarning),
592
- async (deploymentsYargs) => {
593
- await printWranglerBanner();
594
- const config = readConfig(deploymentsYargs.config, deploymentsYargs);
595
- const accountId = await requireAuth(config);
596
- const scriptName = getScriptName(
597
- { name: deploymentsYargs.name, env: undefined },
598
- config
599
- );
600
-
601
- logger.log(`${deploymentsWarning}\n`);
602
- await deployments(accountId, scriptName, config);
603
- }
590
+ .command(
591
+ "list",
592
+ "🚢 Displays the 10 most recent deployments for a worker",
593
+ async (listYargs) => listYargs,
594
+ async (listYargs) => {
595
+ const { accountId, scriptName, config } =
596
+ await commonDeploymentCMDSetup(listYargs, deploymentsWarning);
597
+ await deployments(accountId, scriptName, config);
598
+ }
599
+ )
600
+ .command(
601
+ "view [deployment-id]",
602
+ "🔍 View a deployment",
603
+ async (viewYargs) =>
604
+ viewYargs.positional("deployment-id", {
605
+ describe: "The ID of the deployment you want to inspect",
606
+ type: "string",
607
+ demandOption: false,
608
+ }),
609
+ async (viewYargs) => {
610
+ const { accountId, scriptName, config } =
611
+ await commonDeploymentCMDSetup(viewYargs, deploymentsWarning);
612
+
613
+ await viewDeployment(
614
+ accountId,
615
+ scriptName,
616
+ config,
617
+ viewYargs.deploymentId
618
+ );
619
+ }
620
+ )
621
+ .command(subHelp)
622
+ .epilogue(deploymentsWarning)
604
623
  );
624
+ const rollbackWarning =
625
+ "🚧`wrangler rollback` is a beta command. Please report any issues to https://github.com/cloudflare/workers-sdk/issues/new/choose";
626
+ wrangler
627
+ .command(
628
+ "rollback [deployment-id]",
629
+ "🔙 Rollback a deployment",
630
+ (rollbackYargs) =>
631
+ rollbackYargs
632
+ .positional("deployment-id", {
633
+ describe: "The ID of the deployment to rollback to",
634
+ type: "string",
635
+ demandOption: false,
636
+ })
637
+ .option("message", {
638
+ alias: "m",
639
+ describe:
640
+ "Skip confirmation and message prompts, uses provided argument as message",
641
+ type: "string",
642
+ default: undefined,
643
+ }),
644
+ async (rollbackYargs) => {
645
+ const { accountId, scriptName, config } =
646
+ await commonDeploymentCMDSetup(rollbackYargs, rollbackWarning);
647
+
648
+ await rollbackDeployment(
649
+ accountId,
650
+ scriptName,
651
+ config,
652
+ rollbackYargs.deploymentId,
653
+ rollbackYargs.message
654
+ );
655
+ }
656
+ )
657
+ .epilogue(rollbackWarning);
605
658
 
606
659
  // This set to false to allow overwrite of default behaviour
607
660
  wrangler.version(false);