Skip to content

Commit 308d699

Browse files
Enigopetebacondarwin
authored andcommitted
[wrangler] Added pages deployment delete command
1 parent a63b379 commit 308d699

File tree

6 files changed

+177
-4
lines changed

6 files changed

+177
-4
lines changed

.changeset/dry-spies-lay.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"wrangler": minor
3+
---
4+
5+
Added `pages deployment delete <deployment>` command

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ tools/deployment-status.json
208208

209209
# IntelliJ
210210
.idea/
211+
*.iml
211212

212213
# VSCode Theme
213214
*.vsix
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { http, HttpResponse } from "msw";
2+
import { endEventLoop } from "../helpers/end-event-loop";
3+
import { mockAccountId, mockApiToken } from "../helpers/mock-account-id";
4+
import { mockConsoleMethods } from "../helpers/mock-console";
5+
import { msw } from "../helpers/msw";
6+
import { runInTempDir } from "../helpers/run-in-tmp";
7+
import { runWrangler } from "../helpers/run-wrangler";
8+
9+
const PROJECT_NAME = "images";
10+
const DEPLOYMENT = "deployment";
11+
12+
describe("pages deployment delete", () => {
13+
runInTempDir();
14+
mockAccountId();
15+
mockApiToken();
16+
mockConsoleMethods();
17+
18+
afterEach(async () => {
19+
await endEventLoop();
20+
msw.resetHandlers();
21+
msw.restoreHandlers();
22+
});
23+
24+
it("should make request to delete deployment", async () => {
25+
const requests = mockDeploymentDeleteRequest(DEPLOYMENT);
26+
await runWrangler(
27+
`pages deployment delete ${DEPLOYMENT} --project-name=${PROJECT_NAME}`
28+
);
29+
expect(requests.count).toBe(1);
30+
});
31+
32+
it("should throw an error if deployment ID is missing", async () => {
33+
await expect(
34+
runWrangler(`pages deployment delete --project-name=${PROJECT_NAME}`)
35+
).rejects.toThrow("Must specify a project name and deployment.");
36+
});
37+
38+
it("should throw an error if project name is missing in non-interactive mode", async () => {
39+
await expect(
40+
runWrangler(`pages deployment delete ${DEPLOYMENT}`)
41+
).rejects.toThrow("Must specify a project name in non-interactive mode.");
42+
});
43+
});
44+
45+
/* -------------------------------------------------- */
46+
/* Helper Functions */
47+
/* -------------------------------------------------- */
48+
49+
type RequestLogger = {
50+
count: number;
51+
queryParams: [string, string][][];
52+
};
53+
54+
function mockDeploymentDeleteRequest(deployment: string): RequestLogger {
55+
const requests: RequestLogger = { count: 0, queryParams: [] };
56+
msw.use(
57+
http.delete(
58+
"*/accounts/:accountId/pages/projects/:project/deployments/:deployment",
59+
({ request, params }) => {
60+
requests.count++;
61+
const url = new URL(request.url);
62+
requests.queryParams.push(Array.from(url.searchParams.entries()));
63+
expect(params.project).toEqual(PROJECT_NAME);
64+
expect(params.accountId).toEqual("some-account-id");
65+
66+
return HttpResponse.json(
67+
{
68+
success: true,
69+
errors: [],
70+
messages: [],
71+
result: deployment,
72+
},
73+
{ status: 200 }
74+
);
75+
},
76+
{ once: true }
77+
)
78+
);
79+
return requests;
80+
}

packages/wrangler/src/__tests__/pages/pages.test.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,12 @@ describe("pages", () => {
119119
Interact with the deployments of a project
120120
121121
COMMANDS
122-
wrangler pages deployment list List deployments in your Cloudflare Pages project
123-
wrangler pages deployment create [directory] Deploy a directory of static assets as a Pages deployment
122+
wrangler pages deployment list List deployments in your Cloudflare Pages project
123+
wrangler pages deployment create [directory] Deploy a directory of static assets as a Pages deployment
124124
125-
Alias for \\"wrangler pages deploy\\".
126-
wrangler pages deployment tail [deployment] Start a tailing session for a project's deployment and livestream logs from your Functions
125+
Alias for \\"wrangler pages deploy\\".
126+
wrangler pages deployment delete [deployment] Delete a Pages deployment
127+
wrangler pages deployment tail [deployment] Start a tailing session for a project's deployment and livestream logs from your Functions
127128
128129
GLOBAL FLAGS
129130
--cwd Run as if Wrangler was started in the specified directory instead of the current working directory [string]

packages/wrangler/src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ import {
130130
} from "./pages";
131131
import { pagesFunctionsBuildCommand } from "./pages/build";
132132
import { pagesFunctionsBuildEnvCommand } from "./pages/build-env";
133+
import { pagesDeploymentDeleteCommand } from "./pages/delete-deployment";
133134
import {
134135
pagesDeployCommand,
135136
pagesDeploymentCreateCommand,
@@ -1153,6 +1154,10 @@ export function createCLIParser(argv: string[]) {
11531154
command: "wrangler pages deployment create",
11541155
definition: pagesDeploymentCreateCommand,
11551156
},
1157+
{
1158+
command: "wrangler pages deployment delete",
1159+
definition: pagesDeploymentDeleteCommand,
1160+
},
11561161
{
11571162
command: "wrangler pages deployment tail",
11581163
definition: pagesDeploymentTailCommand,
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { fetchResult } from "../cfetch";
2+
import { getConfigCache } from "../config-cache";
3+
import { createCommand } from "../core/create-command";
4+
import { COMPLIANCE_REGION_CONFIG_PUBLIC } from "../environment-variables/misc-variables";
5+
import { FatalError } from "../errors";
6+
import isInteractive from "../is-interactive";
7+
import { logger } from "../logger";
8+
import { requireAuth } from "../user";
9+
import { PAGES_CONFIG_CACHE_FILENAME } from "./constants";
10+
import { promptSelectProject } from "./prompt-select-project";
11+
import type { PagesConfigCache } from "./types";
12+
13+
export const pagesDeploymentDeleteCommand = createCommand({
14+
metadata: {
15+
description: "Delete a Pages deployment",
16+
status: "stable",
17+
owner: "Workers: Authoring and Testing",
18+
hideGlobalFlags: ["config", "env"],
19+
},
20+
behaviour: {
21+
provideConfig: false,
22+
},
23+
args: {
24+
deployment: {
25+
type: "string",
26+
description: "The ID of the deployment you wish to delete",
27+
},
28+
"project-name": {
29+
type: "string",
30+
description:
31+
"The name of the project you would like to delete the deployment from",
32+
},
33+
},
34+
positionalArgs: ["deployment"],
35+
async handler(args) {
36+
if (args.config) {
37+
throw new FatalError(
38+
"Pages does not support custom paths for the Wrangler configuration file",
39+
1
40+
);
41+
}
42+
43+
if (args.env) {
44+
throw new FatalError(
45+
"Pages does not support targeting an environment with the --env flag. Use the --branch flag to target your production or preview branch",
46+
1
47+
);
48+
}
49+
50+
const configCache = getConfigCache<PagesConfigCache>(
51+
PAGES_CONFIG_CACHE_FILENAME
52+
);
53+
54+
const accountId = await requireAuth(configCache);
55+
56+
let projectName = args.projectName;
57+
58+
if (!projectName) {
59+
if (isInteractive()) {
60+
projectName = await promptSelectProject({ accountId });
61+
} else {
62+
throw new FatalError(
63+
"Must specify a project name in non-interactive mode.",
64+
1
65+
);
66+
}
67+
}
68+
69+
if (!args.deployment || !projectName) {
70+
throw new FatalError("Must specify a project name and deployment.", 1);
71+
}
72+
73+
await fetchResult(
74+
COMPLIANCE_REGION_CONFIG_PUBLIC,
75+
`/accounts/${accountId}/pages/projects/${projectName}/deployments/${args.deployment}`,
76+
{ method: "DELETE" }
77+
);
78+
79+
logger.log(`Deployment ${args.deployment} was successfully deleted.`);
80+
},
81+
});

0 commit comments

Comments
 (0)