diff --git a/.changeset/angry-keys-dance.md b/.changeset/angry-keys-dance.md new file mode 100644 index 000000000000..2f9a87d36969 --- /dev/null +++ b/.changeset/angry-keys-dance.md @@ -0,0 +1,5 @@ +--- +"wrangler": minor +--- + +Added new [[pipelines]] bindings diff --git a/packages/wrangler/src/__tests__/configuration.test.ts b/packages/wrangler/src/__tests__/configuration.test.ts index 4762c4acac71..091f424f0c1d 100644 --- a/packages/wrangler/src/__tests__/configuration.test.ts +++ b/packages/wrangler/src/__tests__/configuration.test.ts @@ -130,6 +130,7 @@ describe("normalizeAndValidateConfig()", () => { upload_source_maps: undefined, placement: undefined, tail_consumers: undefined, + pipelines: [], }); expect(diagnostics.hasErrors()).toBe(false); expect(diagnostics.hasWarnings()).toBe(false); @@ -3181,6 +3182,110 @@ describe("normalizeAndValidateConfig()", () => { }); }); + describe("[pipelines]", () => { + it("should error if pipelines is an object", () => { + const { diagnostics } = normalizeAndValidateConfig( + { pipelines: {} } as unknown as RawConfig, + undefined, + { env: undefined } + ); + + expect(diagnostics.hasWarnings()).toBe(false); + expect(diagnostics.renderErrors()).toMatchInlineSnapshot(` + "Processing wrangler configuration: + - The field \\"pipelines\\" should be an array but got {}." + `); + }); + + it("should error if pipelines is a string", () => { + const { diagnostics } = normalizeAndValidateConfig( + { pipelines: "BAD" } as unknown as RawConfig, + undefined, + { env: undefined } + ); + + expect(diagnostics.hasWarnings()).toBe(false); + expect(diagnostics.renderErrors()).toMatchInlineSnapshot(` + "Processing wrangler configuration: + - The field \\"pipelines\\" should be an array but got \\"BAD\\"." + `); + }); + + it("should error if pipelines is a number", () => { + const { diagnostics } = normalizeAndValidateConfig( + { pipelines: 999 } as unknown as RawConfig, + undefined, + { env: undefined } + ); + + expect(diagnostics.hasWarnings()).toBe(false); + expect(diagnostics.renderErrors()).toMatchInlineSnapshot(` + "Processing wrangler configuration: + - The field \\"pipelines\\" should be an array but got 999." + `); + }); + + it("should error if pipelines is null", () => { + const { diagnostics } = normalizeAndValidateConfig( + { pipelines: null } as unknown as RawConfig, + undefined, + { env: undefined } + ); + + expect(diagnostics.hasWarnings()).toBe(false); + expect(diagnostics.renderErrors()).toMatchInlineSnapshot(` + "Processing wrangler configuration: + - The field \\"pipelines\\" should be an array but got null." + `); + }); + + it("should accept valid bindings", () => { + const { diagnostics } = normalizeAndValidateConfig( + { + pipelines: [ + { + binding: "VALID", + pipeline: "343cd4f1d58c42fbb5bd082592fd7143", + }, + ], + } as unknown as RawConfig, + undefined, + { env: undefined } + ); + + expect(diagnostics.hasErrors()).toBe(false); + }); + + it("should error if pipelines.bindings are not valid", () => { + const { diagnostics } = normalizeAndValidateConfig( + { + pipelines: [ + {}, + { + binding: "VALID", + pipeline: "343cd4f1d58c42fbb5bd082592fd7143", + }, + { binding: 2000, project: 2111 }, + ], + } as unknown as RawConfig, + undefined, + { env: undefined } + ); + expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(` + "Processing wrangler configuration: + - Unexpected fields found in pipelines[2] field: \\"project\\"" + `); + expect(diagnostics.hasWarnings()).toBe(true); + expect(diagnostics.renderErrors()).toMatchInlineSnapshot(` + "Processing wrangler configuration: + - \\"pipelines[0]\\" bindings should have a string \\"binding\\" field but got {}. + - \\"pipelines[0]\\" bindings must have a \\"pipeline\\" field but got {}. + - \\"pipelines[2]\\" bindings should have a string \\"binding\\" field but got {\\"binding\\":2000,\\"project\\":2111}. + - \\"pipelines[2]\\" bindings must have a \\"pipeline\\" field but got {\\"binding\\":2000,\\"project\\":2111}." + `); + }); + }); + describe("[unsafe.bindings]", () => { it("should error if unsafe is an array", () => { const { diagnostics } = normalizeAndValidateConfig( diff --git a/packages/wrangler/src/__tests__/deploy.test.ts b/packages/wrangler/src/__tests__/deploy.test.ts index 5fd80f7b13b0..f21f6ec2a085 100644 --- a/packages/wrangler/src/__tests__/deploy.test.ts +++ b/packages/wrangler/src/__tests__/deploy.test.ts @@ -10466,6 +10466,47 @@ export default{ }); }); + describe("pipelines", () => { + it("should upload pipelines bindings", async () => { + writeWranglerToml({ + pipelines: [ + { + binding: "MY_PIPELINE", + pipeline: "0123456789ABCDEF0123456789ABCDEF", + }, + ], + }); + await fs.promises.writeFile("index.js", `export default {};`); + mockSubDomainRequest(); + mockUploadWorkerRequest({ + expectedBindings: [ + { + type: "pipelines", + name: "MY_PIPELINE", + id: "0123456789ABCDEF0123456789ABCDEF", + }, + ], + }); + + await runWrangler("deploy index.js"); + expect(std.out).toMatchInlineSnapshot(` + "Total Upload: xx KiB / gzip: xx KiB + Worker Startup Time: 100 ms + Your worker has access to the following bindings: + - Pipelines: + - MY_PIPELINE: 0123456789ABCDEF0123456789ABCDEF + Uploaded test-name (TIMINGS) + Published test-name (TIMINGS) + https://test-name.test-sub-domain.workers.dev + Current Deployment ID: Galaxy-Class + Current Version ID: Galaxy-Class + + + Note: Deployment ID has been renamed to Version ID. Deployment ID is present to maintain compatibility with the previous behavior of this command. This output will change in a future version of Wrangler. To learn more visit: https://developers.cloudflare.com/workers/configuration/versions-and-deployments" + `); + }); + }); + describe("--keep-vars", () => { it("should send keepVars when keep-vars is passed in", async () => { vi.stubEnv("CLOUDFLARE_API_TOKEN", "hunter2"); diff --git a/packages/wrangler/src/__tests__/type-generation.test.ts b/packages/wrangler/src/__tests__/type-generation.test.ts index df1f416f997c..88fc1cdbfd00 100644 --- a/packages/wrangler/src/__tests__/type-generation.test.ts +++ b/packages/wrangler/src/__tests__/type-generation.test.ts @@ -217,6 +217,7 @@ const bindingsConfigMock: Omit< }, { type: "CompiledWasm", globs: ["**/*.wasm"], fallthrough: true }, ], + pipelines: [], }; describe("generateTypes()", () => { diff --git a/packages/wrangler/src/api/pages/create-worker-bundle-contents.ts b/packages/wrangler/src/api/pages/create-worker-bundle-contents.ts index b35e1ee482fe..eed323ff419b 100644 --- a/packages/wrangler/src/api/pages/create-worker-bundle-contents.ts +++ b/packages/wrangler/src/api/pages/create-worker-bundle-contents.ts @@ -66,6 +66,7 @@ function createWorkerBundleFormData( text_blobs: undefined, data_blobs: undefined, dispatch_namespaces: undefined, + pipelines: undefined, logfwdr: undefined, unsafe: undefined, experimental_assets: undefined, diff --git a/packages/wrangler/src/api/startDevWorker/utils.ts b/packages/wrangler/src/api/startDevWorker/utils.ts index b237ede5814f..acd608ae1eaa 100644 --- a/packages/wrangler/src/api/startDevWorker/utils.ts +++ b/packages/wrangler/src/api/startDevWorker/utils.ts @@ -245,7 +245,7 @@ export function convertCfWorkerInitBindingstoBindings( break; } default: { - assertNever(type); + assertNever(type as never); } } } @@ -270,6 +270,7 @@ export async function convertBindingsToCfWorkerInitBindings( version_metadata: undefined, data_blobs: undefined, durable_objects: undefined, + pipelines: undefined, queues: undefined, r2_buckets: undefined, d1_databases: undefined, diff --git a/packages/wrangler/src/config/config.ts b/packages/wrangler/src/config/config.ts index 640743c0de33..80dc47c02c73 100644 --- a/packages/wrangler/src/config/config.ts +++ b/packages/wrangler/src/config/config.ts @@ -402,4 +402,5 @@ export const defaultWranglerConfig: Config = { }, mtls_certificates: [], tail_consumers: undefined, + pipelines: [], }; diff --git a/packages/wrangler/src/config/environment.ts b/packages/wrangler/src/config/environment.ts index 262f21ae1b9f..d3171e21e9df 100644 --- a/packages/wrangler/src/config/environment.ts +++ b/packages/wrangler/src/config/environment.ts @@ -754,6 +754,23 @@ export interface EnvironmentNonInheritable { /** Details about the outbound Worker which will handle outbound requests from your namespace */ outbound?: DispatchNamespaceOutbound; }[]; + + /** + * Specifies list of Pipelines bound to this Worker environment + * + * NOTE: This field is not automatically inherited from the top level environment, + * and so must be specified in every named environment. + * + * @default `[]` + * @nonInheritable + */ + pipelines: { + /** The binding name used to refer to the bound service. */ + binding: string; + + /** Name of the Pipeline to bind */ + pipeline: string; + }[]; } /** diff --git a/packages/wrangler/src/config/index.ts b/packages/wrangler/src/config/index.ts index 6d1c400d5141..5dc0e95a2636 100644 --- a/packages/wrangler/src/config/index.ts +++ b/packages/wrangler/src/config/index.ts @@ -232,6 +232,7 @@ export function printBindings(bindings: CfWorkerInit["bindings"]) { wasm_modules, dispatch_namespaces, mtls_certificates, + pipelines, } = bindings; if (data_blobs !== undefined && Object.keys(data_blobs).length > 0) { @@ -443,6 +444,16 @@ export function printBindings(bindings: CfWorkerInit["bindings"]) { }); } + if (pipelines != undefined && pipelines.length > 0) { + output.push({ + type: "Pipelines", + entries: pipelines.map(({ binding, pipeline }) => ({ + key: binding, + value: pipeline, + })), + }); + } + if (version_metadata !== undefined) { output.push({ type: "Worker Version Metadata", diff --git a/packages/wrangler/src/config/validation.ts b/packages/wrangler/src/config/validation.ts index 1917e80c8380..fbe321e37e15 100644 --- a/packages/wrangler/src/config/validation.ts +++ b/packages/wrangler/src/config/validation.ts @@ -1444,6 +1444,16 @@ function normalizeAndValidateEnvironment( validateAIBinding(envName), undefined ), + pipelines: notInheritable( + diagnostics, + topLevelEnv, + rawConfig, + rawEnv, + envName, + "pipelines", + validateBindingArray(envName, validatePipelineBinding), + [] + ), version_metadata: notInheritable( diagnostics, topLevelEnv, @@ -2201,6 +2211,7 @@ const validateUnsafeBinding: ValidatorFn = (diagnostics, field, value) => { "service", "logfwdr", "mtls_certificate", + "pipeline", ]; if (safeBindings.includes(value.type)) { @@ -3103,6 +3114,40 @@ const validateConsumer: ValidatorFn = (diagnostics, field, value, _config) => { return isValid; }; +const validatePipelineBinding: ValidatorFn = (diagnostics, field, value) => { + if (typeof value !== "object" || value === null) { + diagnostics.errors.push( + `"pipeline" bindings should be objects, but got ${JSON.stringify(value)}` + ); + return false; + } + let isValid = true; + // Pipelin bindings must have a binding and a pipeline. + if (!isRequiredProperty(value, "binding", "string")) { + diagnostics.errors.push( + `"${field}" bindings should have a string "binding" field but got ${JSON.stringify( + value + )}.` + ); + isValid = false; + } + if (!isRequiredProperty(value, "pipeline", "string")) { + diagnostics.errors.push( + `"${field}" bindings must have a "pipeline" field but got ${JSON.stringify( + value + )}.` + ); + isValid = false; + } + + validateAdditionalProperties(diagnostics, field, Object.keys(value), [ + "binding", + "pipeline", + ]); + + return isValid; +}; + function normalizeAndValidateLimits( diagnostics: Diagnostics, topLevelEnv: Environment | undefined, diff --git a/packages/wrangler/src/deploy/deploy.ts b/packages/wrangler/src/deploy/deploy.ts index 04c81a5c4547..b0b9d3b504bb 100644 --- a/packages/wrangler/src/deploy/deploy.ts +++ b/packages/wrangler/src/deploy/deploy.ts @@ -651,6 +651,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m analytics_engine_datasets: config.analytics_engine_datasets, dispatch_namespaces: config.dispatch_namespaces, mtls_certificates: config.mtls_certificates, + pipelines: config.pipelines, logfwdr: config.logfwdr, experimental_assets: config.experimental_assets?.binding ? { binding: config.experimental_assets.binding } diff --git a/packages/wrangler/src/deployment-bundle/create-worker-upload-form.ts b/packages/wrangler/src/deployment-bundle/create-worker-upload-form.ts index 08b4886cd458..c3a48dc42f37 100644 --- a/packages/wrangler/src/deployment-bundle/create-worker-upload-form.ts +++ b/packages/wrangler/src/deployment-bundle/create-worker-upload-form.ts @@ -110,6 +110,7 @@ export type WorkerMetadataBinding = }; } | { type: "mtls_certificate"; name: string; certificate_id: string } + | { type: "pipelines"; name: string; id: string } | { type: "logfwdr"; name: string; @@ -316,6 +317,14 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData { }); }); + bindings.pipelines?.forEach(({ binding, pipeline }) => { + metadataBindings.push({ + name: binding, + type: "pipelines", + id: pipeline, + }); + }); + bindings.logfwdr?.bindings.forEach(({ name, destination }) => { metadataBindings.push({ name: name, diff --git a/packages/wrangler/src/deployment-bundle/worker.ts b/packages/wrangler/src/deployment-bundle/worker.ts index 37e6478f6579..c2d2d962e45d 100644 --- a/packages/wrangler/src/deployment-bundle/worker.ts +++ b/packages/wrangler/src/deployment-bundle/worker.ts @@ -226,6 +226,11 @@ export interface CfExperimentalAssetBinding { binding: string; } +export interface CfPipeline { + binding: string; + pipeline: string; +} + export interface CfUnsafeBinding { name: string; type: string; @@ -326,6 +331,7 @@ export interface CfWorkerInit { dispatch_namespaces: CfDispatchNamespace[] | undefined; mtls_certificates: CfMTlsCertificate[] | undefined; logfwdr: CfLogfwdr | undefined; + pipelines: CfPipeline[] | undefined; unsafe: CfUnsafe | undefined; experimental_assets: CfExperimentalAssetBinding | undefined; }; diff --git a/packages/wrangler/src/dev.tsx b/packages/wrangler/src/dev.tsx index 1f87ccf410f6..e6423913924f 100644 --- a/packages/wrangler/src/dev.tsx +++ b/packages/wrangler/src/dev.tsx @@ -725,6 +725,7 @@ export async function startDev(args: StartDevOptions) { analytics_engine_datasets: undefined, dispatch_namespaces: undefined, mtls_certificates: undefined, + pipelines: undefined, logfwdr: undefined, unsafe: undefined, experimental_assets: undefined, @@ -1599,6 +1600,7 @@ export function getBindings( experimental_assets: configParam.experimental_assets?.binding ? { binding: configParam.experimental_assets?.binding } : undefined, + pipelines: configParam.pipelines, }; return bindings; diff --git a/packages/wrangler/src/secret/index.ts b/packages/wrangler/src/secret/index.ts index 0a2c62f620c6..23579f036093 100644 --- a/packages/wrangler/src/secret/index.ts +++ b/packages/wrangler/src/secret/index.ts @@ -92,6 +92,7 @@ async function createDraftWorker({ data_blobs: {}, dispatch_namespaces: [], mtls_certificates: [], + pipelines: [], logfwdr: { bindings: [] }, experimental_assets: undefined, unsafe: { diff --git a/packages/wrangler/src/versions/upload.ts b/packages/wrangler/src/versions/upload.ts index c638adf522ed..cb1b2b0207c6 100644 --- a/packages/wrangler/src/versions/upload.ts +++ b/packages/wrangler/src/versions/upload.ts @@ -376,6 +376,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m text_blobs: config.text_blobs, data_blobs: config.data_blobs, durable_objects: config.durable_objects, + pipelines: config.pipelines, queues: config.queues.producers?.map((producer) => { return { binding: producer.binding, queue_name: producer.queue }; }),