Skip to content

Commit

Permalink
feat(wrangler) - add assets binding to wrangler deploy (#6487)
Browse files Browse the repository at this point in the history
* add assets binding

* review feedback

* file path validation and add aus test helper

* fix windows path

* separate assets jwt from experimentalAssets config object

* remove assets from safeBindings list

* fixup versions upload
  • Loading branch information
emily-shen committed Aug 15, 2024
1 parent 00f340f commit 59556eb
Show file tree
Hide file tree
Showing 14 changed files with 152 additions and 147 deletions.
169 changes: 84 additions & 85 deletions packages/wrangler/src/__tests__/deploy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4345,23 +4345,7 @@ addEventListener('fetch', event => {});`
experimental_assets: { directory: "config-assets" },
});
const bodies: AssetManifest[] = [];
msw.use(
http.post<never, AssetManifest>(
`*/accounts/some-account-id/workers/scripts/test-name/assets-upload-session`,
async ({ request }) => {
bodies.push(await request.json());
return HttpResponse.json(
{
success: true,
errors: [],
messages: [],
result: { jwt: "<<aus-completion-token>>", buckets: [] },
},
{ status: 201 }
);
}
)
);
await mockAUSRequest(bodies);
mockSubDomainRequest();
mockUploadWorkerRequest({
expectedExperimentalAssets: true,
Expand Down Expand Up @@ -4431,6 +4415,45 @@ addEventListener('fetch', event => {});`
);
});

it("should encode file paths", async () => {
// NB windows will disallow these characters in file paths anyway < > : " / \ | ? *
const assets = [
{ filePath: "file-1.txt", content: "Content of file-1" },
{ filePath: "boop/file#1.txt", content: "Content of file-1" },
{ filePath: "béëp/boo^p.txt", content: "Content of file-1" },
];
writeAssets(assets);
writeWranglerToml({
experimental_assets: { directory: "assets" },
});

const bodies: AssetManifest[] = [];
await mockAUSRequest(bodies);
mockSubDomainRequest();
mockUploadWorkerRequest({
expectedExperimentalAssets: true,
expectedType: "none",
});
await runWrangler("deploy");
expect(bodies.length).toBe(1);
expect(bodies[0]).toEqual({
manifest: {
"/b%C3%A9%C3%ABp/boo%5Ep.txt": {
hash: "0de3dd5df907418e9730fd2bd747bd5e",
size: 17,
},
"/boop/file%231.txt": {
hash: "0de3dd5df907418e9730fd2bd747bd5e",
size: 17,
},
"/file-1.txt": {
hash: "0de3dd5df907418e9730fd2bd747bd5e",
size: 17,
},
},
});
});

it("should resolve assets directory relative to wrangler.toml if using config", async () => {
const assets = [{ filePath: "file-1.txt", content: "Content of file-1" }];
writeAssets(assets, "some/path/assets");
Expand All @@ -4441,23 +4464,7 @@ addEventListener('fetch', event => {});`
"some/path/wrangler.toml"
);
const bodies: AssetManifest[] = [];
msw.use(
http.post<never, AssetManifest>(
`*/accounts/some-account-id/workers/scripts/test-name/assets-upload-session`,
async ({ request }) => {
bodies.push(await request.json());
return HttpResponse.json(
{
success: true,
errors: [],
messages: [],
result: { jwt: "<<aus-completion-token>>", buckets: [[]] },
},
{ status: 201 }
);
}
)
);
await mockAUSRequest(bodies);
mockSubDomainRequest();
mockUploadWorkerRequest({
expectedExperimentalAssets: true,
Expand All @@ -4479,23 +4486,7 @@ addEventListener('fetch', event => {});`
const assets = [{ filePath: "file-1.txt", content: "Content of file-1" }];
writeAssets(assets, "some/path/assets");
const bodies: AssetManifest[] = [];
msw.use(
http.post<never, AssetManifest>(
`*/accounts/some-account-id/workers/scripts/test-name/assets-upload-session`,
async ({ request }) => {
bodies.push(await request.json());
return HttpResponse.json(
{
success: true,
errors: [],
messages: [],
result: { jwt: "<<aus-completion-token>>", buckets: [[]] },
},
{ status: 201 }
);
}
)
);
await mockAUSRequest(bodies);
mockSubDomainRequest();
mockUploadWorkerRequest({
expectedExperimentalAssets: true,
Expand Down Expand Up @@ -4523,23 +4514,7 @@ addEventListener('fetch', event => {});`
];
writeAssets(assets);
const bodies: AssetManifest[] = [];
msw.use(
http.post<never, AssetManifest>(
`*/accounts/some-account-id/workers/scripts/test-name/assets-upload-session`,
async ({ request }) => {
bodies.push(await request.json());
return HttpResponse.json(
{
success: true,
errors: [],
messages: [],
result: { jwt: "<<aus-completion-token>>", buckets: [] },
},
{ status: 201 }
);
}
)
);
await mockAUSRequest(bodies);
// skips asset uploading since empty buckets returned
mockSubDomainRequest();
mockUploadWorkerRequest({
Expand Down Expand Up @@ -4574,23 +4549,7 @@ addEventListener('fetch', event => {});`
experimental_assets: { directory: "assets" },
});
const bodies: AssetManifest[] = [];
msw.use(
http.post<never, AssetManifest>(
`*/accounts/some-account-id/workers/scripts/test-name/assets-upload-session`,
async ({ request }) => {
bodies.push(await request.json());
return HttpResponse.json(
{
success: true,
errors: [],
messages: [],
result: { jwt: "<<aus-completion-token>>", buckets: [] },
},
{ status: 201 }
);
}
)
);
await mockAUSRequest(bodies);
// skips asset uploading since empty buckets returned
mockSubDomainRequest();
mockUploadWorkerRequest({
Expand Down Expand Up @@ -4753,6 +4712,26 @@ addEventListener('fetch', event => {});`
])
);
});
it("should be able to upload a user worker with ASSETS binding", async () => {
const assets = [
{ filePath: "file-1.txt", content: "Content of file-1" },
{ filePath: "boop/file-2.txt", content: "Content of file-2" },
];
writeAssets(assets);
writeWorkerSource({ format: "js" });
writeWranglerToml({
main: "index.js",
experimental_assets: { directory: "assets", binding: "ASSETS" },
});
await mockAUSRequest();
mockSubDomainRequest();
mockUploadWorkerRequest({
expectedExperimentalAssets: true,
expectedBindings: [{ name: "ASSETS", type: "assets" }],
expectedMainModule: "index.js",
});
await runWrangler("deploy");
});
});

describe("workers_dev setting", () => {
Expand Down Expand Up @@ -11728,3 +11707,23 @@ function mockPostQueueHTTPConsumer(
);
return requests;
}

const mockAUSRequest = async (bodies?: AssetManifest[]) => {
msw.use(
http.post<never, AssetManifest>(
`*/accounts/some-account-id/workers/scripts/test-name/assets-upload-session`,
async ({ request }) => {
bodies?.push(await request.json());
return HttpResponse.json(
{
success: true,
errors: [],
messages: [],
result: { jwt: "<<aus-completion-token>>", buckets: [[]] },
},
{ status: 201 }
);
}
)
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ function createWorkerBundleFormData(
dispatch_namespaces: undefined,
logfwdr: undefined,
unsafe: undefined,
experimental_assets: undefined,
};

// The upload API only accepts an empty string or no specified placement for the "off" mode.
Expand All @@ -92,7 +93,7 @@ function createWorkerBundleFormData(
placement: placement,
tail_consumers: undefined,
limits: config?.limits,
experimental_assets: undefined,
experimental_assets_jwt: undefined,
};

return createWorkerUploadForm(worker);
Expand Down
5 changes: 5 additions & 0 deletions packages/wrangler/src/api/startDevWorker/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,10 @@ export function convertCfWorkerInitBindingstoBindings(
}
break;
}
case "experimental_assets": {
output[info["binding"]] = { type: "assets" };
break;
}
default: {
assertNever(type);
}
Expand Down Expand Up @@ -277,6 +281,7 @@ export async function convertBindingsToCfWorkerInitBindings(
mtls_certificates: undefined,
logfwdr: undefined,
unsafe: undefined,
experimental_assets: undefined,
};

const fetchers: Record<string, ServiceFetch> = {};
Expand Down
25 changes: 11 additions & 14 deletions packages/wrangler/src/deploy/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,7 @@ type Props = {
compatibilityDate: string | undefined;
compatibilityFlags: string[] | undefined;
legacyAssetPaths: LegacyAssetPaths | undefined;
experimentalAssets:
| (ExperimentalAssets & { staticAssetsOnly: boolean })
| undefined;
experimentalAssets: ExperimentalAssets | undefined;
vars: Record<string, string> | undefined;
defines: Record<string, string> | undefined;
alias: Record<string, string> | undefined;
Expand Down Expand Up @@ -600,17 +598,13 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
: undefined;

// Upload assets if experimental assets is being used
// temporarily gate this very explicitly just in case
const experimentalAssetsWorkerInfo: CfWorkerInit["experimental_assets"] =
const experimentalAssetsJwt: CfWorkerInit["experimental_assets_jwt"] =
props.experimentalAssets && !props.dryRun
? {
jwt: await syncExperimentalAssets(
accountId,
scriptName,
props.experimentalAssets.directory
),
staticAssetsOnly: props.experimentalAssets.staticAssetsOnly,
}
? await syncExperimentalAssets(
accountId,
scriptName,
props.experimentalAssets.directory
)
: undefined;

const legacyAssets = await syncLegacyAssets(
Expand Down Expand Up @@ -659,6 +653,9 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
dispatch_namespaces: config.dispatch_namespaces,
mtls_certificates: config.mtls_certificates,
logfwdr: config.logfwdr,
experimental_assets: config.experimental_assets?.binding
? { binding: config.experimental_assets.binding }
: undefined,
unsafe: {
bindings: config.unsafe.bindings,
metadata: config.unsafe.metadata,
Expand Down Expand Up @@ -703,7 +700,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
placement,
tail_consumers: config.tail_consumers,
limits: config.limits,
experimental_assets: experimentalAssetsWorkerInfo,
experimental_assets_jwt: experimentalAssetsJwt,
};

sourceMapSize = worker.sourceMaps?.reduce(
Expand Down
10 changes: 1 addition & 9 deletions packages/wrangler/src/deploy/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,14 +324,6 @@ export async function deployHandler(
await standardPricingWarning(config);
}

// Flag use of assets without user worker
const experimentalAssetsOptions = experimentalAssets
? {
...experimentalAssets,
staticAssetsOnly: !(args.script || config.main),
}
: undefined;

const beforeUpload = Date.now();
const name = getScriptName(args, config);
const { sourceMapSize, deploymentId, workerTag } = await deploy({
Expand All @@ -353,7 +345,7 @@ export async function deployHandler(
jsxFragment: args.jsxFragment,
tsconfig: args.tsconfig,
routes: args.routes,
experimentalAssets: experimentalAssetsOptions,
experimentalAssets,
legacyAssetPaths,
legacyEnv: isLegacyEnv(config),
minify: args.minify,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ export type WorkerMetadataBinding =
type: "logfwdr";
name: string;
destination: string;
};
}
| { type: "assets"; name: string };

// for PUT /accounts/:accountId/workers/scripts/:scriptName
export type WorkerMetadataPut = {
Expand Down Expand Up @@ -171,14 +172,14 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
tail_consumers,
limits,
annotations,
experimental_assets,
experimental_assets_jwt,
} = worker;

// short circuit if static assets upload only
if (experimental_assets?.staticAssetsOnly) {
if (main.name === "no-op-assets-worker.js" && experimental_assets_jwt) {
formData.set(
"metadata",
JSON.stringify({ assets: experimental_assets.jwt })
JSON.stringify({ assets: experimental_assets_jwt })
);
return formData;
}
Expand Down Expand Up @@ -364,6 +365,13 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
});
}

if (bindings.experimental_assets !== undefined) {
metadataBindings.push({
name: bindings.experimental_assets.binding,
type: "assets",
});
}

for (const [name, filePath] of Object.entries(bindings.text_blobs || {})) {
metadataBindings.push({
name,
Expand Down Expand Up @@ -546,6 +554,7 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
...(tail_consumers && { tail_consumers }),
...(limits && { limits }),
...(annotations && { annotations }),
...(experimental_assets_jwt && { assets: experimental_assets_jwt }),
};

if (bindings.unsafe?.metadata !== undefined) {
Expand Down
Loading

0 comments on commit 59556eb

Please sign in to comment.