Skip to content

Commit c5b291d

Browse files
authored
feat: interactively handle wonky asset deploy commands (#10016)
* add interactive flow for wonky asset deploy commands * PR feedback
1 parent 823cba8 commit c5b291d

File tree

4 files changed

+394
-3
lines changed

4 files changed

+394
-3
lines changed

.changeset/deep-onions-move.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
"wrangler": minor
3+
---
4+
5+
Interactively handle `wrangler deploy`s that are probably assets-only, where there is no config file and flags are incorrect or missing.
6+
7+
For example:
8+
9+
`npx wrangler deploy ./public` will now ask if you meant to deploy a folder of assets only, ask for a name, set the compat date and then ask whether to write your choices out to `wrangler.json` for subsequent deployments.
10+
11+
`npx wrangler deploy --assets=./public` will now ask for a name, set the compat date and then ask whether to write your choices out to `wrangler.json` for subsequent deployments.
12+
13+
In non-interactive contexts, Wrangler will error as it currently does.

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

Lines changed: 264 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import * as esbuild from "esbuild";
1010
import { http, HttpResponse } from "msw";
1111
import dedent from "ts-dedent";
1212
import { vi } from "vitest";
13+
import { findWranglerConfig } from "../config/config-helpers";
1314
import {
1415
printBundleSize,
1516
printOffendingDependencies,
@@ -20,7 +21,7 @@ import { writeAuthConfigFile } from "../user";
2021
import { mockAccountId, mockApiToken } from "./helpers/mock-account-id";
2122
import { mockAuthDomain } from "./helpers/mock-auth-domain";
2223
import { mockConsoleMethods } from "./helpers/mock-console";
23-
import { clearDialogs, mockConfirm } from "./helpers/mock-dialogs";
24+
import { clearDialogs, mockConfirm, mockPrompt } from "./helpers/mock-dialogs";
2425
import { mockGetZoneFromHostRequest } from "./helpers/mock-get-zone-from-host";
2526
import { useMockIsTTY } from "./helpers/mock-istty";
2627
import { mockCollectKnownRoutesRequest } from "./helpers/mock-known-routes";
@@ -2720,6 +2721,266 @@ addEventListener('fetch', event => {});`
27202721
});
27212722
});
27222723
});
2724+
2725+
describe("should interactively handle misconfigured asset-only deployments", () => {
2726+
beforeEach(() => {
2727+
setIsTTY(true);
2728+
2729+
// Mock the date to ensure consistent compatibility_date
2730+
vi.setSystemTime(new Date("2024-01-01T00:00:00Z"));
2731+
2732+
// so that we can test that the name prompt defaults to the directory name
2733+
fs.mkdirSync("my-site");
2734+
process.chdir("my-site");
2735+
const assets = [
2736+
{ filePath: "index.html", content: "<html>test</html>" },
2737+
];
2738+
writeAssets(assets);
2739+
expect(findWranglerConfig().configPath).toBe(undefined);
2740+
mockSubDomainRequest();
2741+
mockUploadWorkerRequest({
2742+
expectedAssets: {
2743+
jwt: "<<aus-completion-token>>",
2744+
config: {},
2745+
},
2746+
expectedType: "none",
2747+
});
2748+
});
2749+
afterEach(() => {
2750+
setIsTTY(false);
2751+
vi.useRealTimers();
2752+
});
2753+
2754+
it("should handle `wrangler deploy <directory>`", async () => {
2755+
mockConfirm({
2756+
text: "It looks like you are trying to deploy a directory of static assets only. Is this correct?",
2757+
result: true,
2758+
});
2759+
mockPrompt({
2760+
text: "What do you want to name your project?",
2761+
options: { defaultValue: "my-site" },
2762+
result: "test-name",
2763+
});
2764+
mockConfirm({
2765+
text: "Do you want Wrangler to write a wrangler.json config file to store this configuration?\nThis will allow you to simply run `wrangler deploy` on future deployments.",
2766+
result: true,
2767+
});
2768+
2769+
const bodies: AssetManifest[] = [];
2770+
await mockAUSRequest(bodies);
2771+
2772+
await runWrangler("deploy ./assets");
2773+
expect(bodies.length).toBe(1);
2774+
expect(bodies[0]).toEqual({
2775+
manifest: {
2776+
"/index.html": {
2777+
hash: "8308ce789f3d08668ce87176838d59d0",
2778+
size: 17,
2779+
},
2780+
},
2781+
});
2782+
expect(fs.readFileSync("wrangler.jsonc", "utf-8"))
2783+
.toMatchInlineSnapshot(`
2784+
"{
2785+
\\"name\\": \\"test-name\\",
2786+
\\"compatibility_date\\": \\"2024-01-01\\",
2787+
\\"assets\\": {
2788+
\\"directory\\": \\"./assets\\"
2789+
}
2790+
}"
2791+
`);
2792+
expect(std.out).toMatchInlineSnapshot(`
2793+
"
2794+
2795+
2796+
No compatibility date found Defaulting to today: 2024-01-01
2797+
2798+
Wrote
2799+
{
2800+
\\"name\\": \\"test-name\\",
2801+
\\"compatibility_date\\": \\"2024-01-01\\",
2802+
\\"assets\\": {
2803+
\\"directory\\": \\"./assets\\"
2804+
}
2805+
}
2806+
to <cwd>/wrangler.jsonc.
2807+
Please run \`wrangler deploy\` instead of \`wrangler deploy ./assets\` next time. Wrangler will automatically use the configuration saved to wrangler.jsonc.
2808+
2809+
Proceeding with deployment...
2810+
2811+
Total Upload: xx KiB / gzip: xx KiB
2812+
Worker Startup Time: 100 ms
2813+
Uploaded test-name (TIMINGS)
2814+
Deployed test-name triggers (TIMINGS)
2815+
https://test-name.test-sub-domain.workers.dev
2816+
Current Version ID: Galaxy-Class"
2817+
`);
2818+
});
2819+
2820+
it("should handle `wrangler deploy --assets` without name or compat date", async () => {
2821+
// if the user has used --assets flag and args.script is not set, we just need to prompt for the name and add compat date
2822+
mockPrompt({
2823+
text: "What do you want to name your project?",
2824+
options: { defaultValue: "my-site" },
2825+
result: "test-name",
2826+
});
2827+
mockConfirm({
2828+
text: "Do you want Wrangler to write a wrangler.json config file to store this configuration?\nThis will allow you to simply run `wrangler deploy` on future deployments.",
2829+
result: true,
2830+
});
2831+
2832+
const bodies: AssetManifest[] = [];
2833+
await mockAUSRequest(bodies);
2834+
2835+
await runWrangler("deploy --assets ./assets");
2836+
expect(bodies.length).toBe(1);
2837+
expect(bodies[0]).toEqual({
2838+
manifest: {
2839+
"/index.html": {
2840+
hash: "8308ce789f3d08668ce87176838d59d0",
2841+
size: 17,
2842+
},
2843+
},
2844+
});
2845+
expect(fs.readFileSync("wrangler.jsonc", "utf-8"))
2846+
.toMatchInlineSnapshot(`
2847+
"{
2848+
\\"name\\": \\"test-name\\",
2849+
\\"compatibility_date\\": \\"2024-01-01\\",
2850+
\\"assets\\": {
2851+
\\"directory\\": \\"./assets\\"
2852+
}
2853+
}"
2854+
`);
2855+
expect(std.out).toMatchInlineSnapshot(`
2856+
"
2857+
2858+
No compatibility date found Defaulting to today: 2024-01-01
2859+
2860+
Wrote
2861+
{
2862+
\\"name\\": \\"test-name\\",
2863+
\\"compatibility_date\\": \\"2024-01-01\\",
2864+
\\"assets\\": {
2865+
\\"directory\\": \\"./assets\\"
2866+
}
2867+
}
2868+
to <cwd>/wrangler.jsonc.
2869+
Please run \`wrangler deploy\` instead of \`wrangler deploy ./assets\` next time. Wrangler will automatically use the configuration saved to wrangler.jsonc.
2870+
2871+
Proceeding with deployment...
2872+
2873+
Total Upload: xx KiB / gzip: xx KiB
2874+
Worker Startup Time: 100 ms
2875+
Uploaded test-name (TIMINGS)
2876+
Deployed test-name triggers (TIMINGS)
2877+
https://test-name.test-sub-domain.workers.dev
2878+
Current Version ID: Galaxy-Class"
2879+
`);
2880+
});
2881+
2882+
it("should suggest 'my-project' if the default name from the cwd is invalid", async () => {
2883+
process.chdir("../");
2884+
fs.renameSync("my-site", "[blah]");
2885+
process.chdir("[blah]");
2886+
// if the user has used --assets flag and args.script is not set, we just need to prompt for the name and add compat date
2887+
mockPrompt({
2888+
text: "What do you want to name your project?",
2889+
// not [blah] because it is an invalid worker name
2890+
options: { defaultValue: "my-project" },
2891+
result: "test-name",
2892+
});
2893+
mockConfirm({
2894+
text: "Do you want Wrangler to write a wrangler.json config file to store this configuration?\nThis will allow you to simply run `wrangler deploy` on future deployments.",
2895+
result: true,
2896+
});
2897+
2898+
const bodies: AssetManifest[] = [];
2899+
await mockAUSRequest(bodies);
2900+
2901+
await runWrangler("deploy --assets ./assets");
2902+
expect(bodies.length).toBe(1);
2903+
expect(bodies[0]).toEqual({
2904+
manifest: {
2905+
"/index.html": {
2906+
hash: "8308ce789f3d08668ce87176838d59d0",
2907+
size: 17,
2908+
},
2909+
},
2910+
});
2911+
expect(fs.readFileSync("wrangler.jsonc", "utf-8"))
2912+
.toMatchInlineSnapshot(`
2913+
"{
2914+
\\"name\\": \\"test-name\\",
2915+
\\"compatibility_date\\": \\"2024-01-01\\",
2916+
\\"assets\\": {
2917+
\\"directory\\": \\"./assets\\"
2918+
}
2919+
}"
2920+
`);
2921+
});
2922+
2923+
it("should bail if the user denies that they are trying to deploy a directory", async () => {
2924+
mockConfirm({
2925+
text: "It looks like you are trying to deploy a directory of static assets only. Is this correct?",
2926+
result: false,
2927+
});
2928+
2929+
await expect(runWrangler("deploy ./assets")).rejects
2930+
.toThrowErrorMatchingInlineSnapshot(`
2931+
[Error: The entry-point file at "assets" was not found.
2932+
The provided entry-point path, "assets", points to a directory, rather than a file.
2933+
2934+
If you want to deploy a directory of static assets, you can do so by using the \`--assets\` flag. For example:
2935+
2936+
wrangler deploy --assets=./assets
2937+
]
2938+
`);
2939+
});
2940+
2941+
it("does not write out a wrangler config file if the user says no", async () => {
2942+
mockPrompt({
2943+
text: "What do you want to name your project?",
2944+
options: { defaultValue: "my-site" },
2945+
result: "test-name",
2946+
});
2947+
mockConfirm({
2948+
text: "Do you want Wrangler to write a wrangler.json config file to store this configuration?\nThis will allow you to simply run `wrangler deploy` on future deployments.",
2949+
result: false,
2950+
});
2951+
2952+
const bodies: AssetManifest[] = [];
2953+
await mockAUSRequest(bodies);
2954+
2955+
await runWrangler("deploy --assets ./assets");
2956+
expect(bodies.length).toBe(1);
2957+
expect(bodies[0]).toEqual({
2958+
manifest: {
2959+
"/index.html": {
2960+
hash: "8308ce789f3d08668ce87176838d59d0",
2961+
size: 17,
2962+
},
2963+
},
2964+
});
2965+
expect(fs.existsSync("wrangler.jsonc")).toBe(false);
2966+
expect(std.out).toMatchInlineSnapshot(`
2967+
"
2968+
2969+
No compatibility date found Defaulting to today: 2024-01-01
2970+
2971+
You should run wrangler deploy --name test-name --compatibility-date 2024-01-01 --assets ./assets next time to deploy this Worker without going through this flow again.
2972+
2973+
Proceeding with deployment...
2974+
2975+
Total Upload: xx KiB / gzip: xx KiB
2976+
Worker Startup Time: 100 ms
2977+
Uploaded test-name (TIMINGS)
2978+
Deployed test-name triggers (TIMINGS)
2979+
https://test-name.test-sub-domain.workers.dev
2980+
Current Version ID: Galaxy-Class"
2981+
`);
2982+
});
2983+
});
27232984
});
27242985

27252986
describe("(legacy) asset upload", () => {
@@ -4346,7 +4607,8 @@ addEventListener('fetch', event => {});`
43464607
);
43474608
});
43484609

4349-
it("should error if directory specified by flag --assets does not exist", async () => {
4610+
it("should error if directory specified by flag --assets does not exist in non-interactive mode", async () => {
4611+
setIsTTY(false);
43504612
await expect(runWrangler("deploy --assets abc")).rejects.toThrow(
43514613
new RegExp(
43524614
'^The directory specified by the "--assets" command line argument does not exist:[Ss]*'

0 commit comments

Comments
 (0)