Skip to content

Commit 824efda

Browse files
committed
[wrangler] Added pages deployment delete command
1 parent d04c69f commit 824efda

File tree

5 files changed

+153
-0
lines changed

5 files changed

+153
-0
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
@@ -215,6 +215,7 @@ tools/deployment-status.json
215215

216216
# IntelliJ
217217
.idea/
218+
*.iml
218219

219220
# VSCode Theme
220221
*.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+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { fetchResult } from "../cfetch";
2+
import { getConfigCache } from "../config-cache";
3+
import { FatalError } from "../errors";
4+
import isInteractive from "../is-interactive";
5+
import { logger } from "../logger";
6+
import { requireAuth } from "../user";
7+
import { PAGES_CONFIG_CACHE_FILENAME } from "./constants";
8+
import { promptSelectProject } from "./prompt-select-project";
9+
import type {
10+
CommonYargsArgv,
11+
StrictYargsOptionsToInterface,
12+
} from "../yargs-types";
13+
import type { PagesConfigCache } from "./types";
14+
15+
export function Options(yargs: CommonYargsArgv) {
16+
return yargs
17+
.positional("deployment", {
18+
type: "string",
19+
description: "The ID of the deployment you wish to delete",
20+
})
21+
.options({
22+
"project-name": {
23+
type: "string",
24+
description:
25+
"The name of the project you would like to delete the deployment from",
26+
},
27+
});
28+
}
29+
30+
export async function Handler({
31+
deployment,
32+
projectName,
33+
}: StrictYargsOptionsToInterface<typeof Options>) {
34+
const config = getConfigCache<PagesConfigCache>(PAGES_CONFIG_CACHE_FILENAME);
35+
const accountId = await requireAuth(config);
36+
37+
projectName ??= config.project_name;
38+
39+
if (!projectName) {
40+
if (isInteractive()) {
41+
projectName = await promptSelectProject({ accountId });
42+
} else {
43+
throw new FatalError(
44+
"Must specify a project name in non-interactive mode.",
45+
1
46+
);
47+
}
48+
}
49+
50+
if (!deployment || !projectName) {
51+
throw new FatalError("Must specify a project name and deployment.", 1);
52+
}
53+
54+
await fetchResult(
55+
`/accounts/${accountId}/pages/projects/${projectName}/deployments/${deployment}`,
56+
{ method: "DELETE" }
57+
);
58+
59+
logger.log(`Deployment ${deployment} was successfully deleted.`);
60+
}

packages/wrangler/src/pages/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import * as Build from "./build";
44
import * as BuildEnv from "./build-env";
5+
import * as Delete from "./delete";
56
import * as Deploy from "./deploy";
67
import * as DeploymentTails from "./deployment-tails";
78
import * as Deployments from "./deployments";
@@ -109,6 +110,12 @@ export function pages(yargs: CommonYargsArgv, subHelp: SubHelp) {
109110
DeploymentTails.Options,
110111
DeploymentTails.Handler
111112
)
113+
.command(
114+
"delete [deployment]",
115+
"Delete a deployment",
116+
Delete.Options,
117+
Delete.Handler
118+
)
112119
)
113120
.command(
114121
["deploy [directory]", "publish [directory]"],

0 commit comments

Comments
 (0)