Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Workflows commands #7015

Merged
merged 17 commits into from
Oct 18, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .changeset/green-parrots-lick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
"wrangler": minor
---

Add support for Workflow bindings (in deployments, not yet in local dev)

To bind to a workflow, add a `workflows` section in your wrangler.toml:

```toml
[[workflows]]
binding = "WORKFLOW"
name = "my-workflow"
class_name = "MyDemoWorkflow"
```

and export an entrypoint (e.g. `MyDemoWorkflow`) in your script:

```typescript
import { WorkflowEntrypoint } from "cloudflare:workers";

export class MyDemoWorkflow extends WorkflowEntrypoint<{}, Params> {...}
```
5 changes: 5 additions & 0 deletions .changeset/moody-seals-smash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wrangler": minor
---

add `wrangler workflows ...` commands
7 changes: 7 additions & 0 deletions fixtures/workflow/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# worker-ts

## 0.0.1

### Patch Changes

- [#3588](https://github.com/cloudflare/workers-sdk/pull/3588) [`64631d8b`](https://github.com/cloudflare/workers-sdk/commit/64631d8b59572f49d65325d8f6fec098c5e912b9) Thanks [@penalosa](https://github.com/penalosa)! - fix: Preserve email handlers when applying middleware to user workers.
15 changes: 15 additions & 0 deletions fixtures/workflow/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "my-workflow",
"private": true,
"scripts": {
"deploy": "wrangler deploy",
"start": "wrangler dev --x-dev-env"
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20241011.0",
"wrangler": "workspace:*"
},
"volta": {
"extends": "../../package.json"
}
}
50 changes: 50 additions & 0 deletions fixtures/workflow/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import {
WorkerEntrypoint,
Workflow,
WorkflowEvent,
WorkflowStep,
} from "cloudflare:workers";

type Params = {
name: string;
};
export class Demo extends Workflow<{}, Params> {
async run(events: Array<WorkflowEvent<Params>>, step: WorkflowStep) {
const { timestamp, payload } = events[0];
const result = await step.do("First step", async function () {
return {
output: "First step result",
};
});

await step.sleep("Wait", "1 minute");

const result2 = await step.do("Second step", async function () {
return {
output: "Second step result",
};
});

return {
result,
result2,
timestamp,
payload,
};
}
}

type Env = {
WORKFLOW: {
create: (id: string) => {
pause: () => {};
};
};
};
export default class extends WorkerEntrypoint<Env> {
async fetch() {
const handle = await this.env.WORKFLOW.create(crypto.randomUUID());
await handle.pause();
return new Response();
}
}
13 changes: 13 additions & 0 deletions fixtures/workflow/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "es2021",
"lib": ["es2021"],
"module": "es2022",
"types": ["@cloudflare/workers-types/experimental"],
"noEmit": true,
"isolatedModules": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}
5 changes: 5 additions & 0 deletions fixtures/workflow/worker-configuration.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Generated by Wrangler by running `wrangler types`

interface Env {
WORKFLOW: Workflow;
}
9 changes: 9 additions & 0 deletions fixtures/workflow/wrangler.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#:schema node_modules/wrangler/config-schema.json
name = "my-workflow-demo"
main = "src/index.ts"
compatibility_date = "2024-10-11"

[[workflows]]
binding = "WORKFLOW"
name = "my-workflow"
class_name = "Demo"
41 changes: 41 additions & 0 deletions packages/wrangler/e2e/dev-with-resources.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,47 @@ describe.sequential.each(RUNTIMES)("Bindings: $flags", ({ runtime, flags }) => {
await worker.readUntil(/✉️/);
});

// TODO: enable for remove dev once realish preview supports it
// TODO: enable for local dev once implemented
it.skip("exposes Workflow bindings", async () => {
await helper.seed({
"wrangler.toml": dedent`
name = "my-workflow-demo"
main = "src/index.ts"
compatibility_date = "2024-10-11"

[[workflows]]
binding = "WORKFLOW"
name = "my-workflow"
class_name = "Demo"
`,
"src/index.ts": dedent`
import { WorkflowEntrypoint } from "cloudflare:workers";

export default {
async fetch(request, env, ctx) {
if (env.WORKFLOW === undefined) {
return new Response("env.WORKFLOW is undefined");
}

return new Response("env.WORKFLOW is available");
}
}

export class Demo extends WorkflowEntrypoint {
run() {
// blank
}
}
`,
});
const worker = helper.runLongLived(`wrangler dev ${flags}`);
const { url } = await worker.waitForReady();
const res = await fetch(url);

await expect(res.text()).resolves.toBe("env.WORKFLOW is available");
});

// TODO(soon): implement E2E tests for other bindings
it.todo("exposes hyperdrive bindings");
it.skipIf(isLocal).todo("exposes send email bindings");
Expand Down
2 changes: 2 additions & 0 deletions packages/wrangler/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@
"@esbuild-plugins/node-modules-polyfill": "^0.2.2",
"blake3-wasm": "^2.1.5",
"chokidar": "^3.5.3",
"date-fns": "^4.1.0",
"esbuild": "0.17.19",
"itty-time": "^1.0.6",
"miniflare": "workspace:*",
"nanoid": "^3.3.3",
"path-to-regexp": "^6.3.0",
Expand Down
1 change: 1 addition & 0 deletions packages/wrangler/src/__tests__/configuration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ describe("normalizeAndValidateConfig()", () => {
placement: undefined,
tail_consumers: undefined,
pipelines: [],
workflows: [],
});
expect(diagnostics.hasErrors()).toBe(false);
expect(diagnostics.hasWarnings()).toBe(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ describe("Command Registration", () => {
wrangler pubsub 📮 Manage Pub/Sub brokers [private beta]
wrangler dispatch-namespace 🏗️ Manage dispatch namespaces
wrangler ai 🤖 Manage AI models
wrangler workflows 🔁 Manage Workflows [open-beta]
wrangler login 🔓 Login to Cloudflare
wrangler logout 🚪 Logout from Cloudflare
wrangler whoami 🕵️ Retrieve your user information
Expand Down
2 changes: 2 additions & 0 deletions packages/wrangler/src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ describe("wrangler", () => {
wrangler pubsub 📮 Manage Pub/Sub brokers [private beta]
wrangler dispatch-namespace 🏗️ Manage dispatch namespaces
wrangler ai 🤖 Manage AI models
wrangler workflows 🔁 Manage Workflows [open-beta]
wrangler login 🔓 Login to Cloudflare
wrangler logout 🚪 Logout from Cloudflare
wrangler whoami 🕵️ Retrieve your user information
Expand Down Expand Up @@ -117,6 +118,7 @@ describe("wrangler", () => {
wrangler pubsub 📮 Manage Pub/Sub brokers [private beta]
wrangler dispatch-namespace 🏗️ Manage dispatch namespaces
wrangler ai 🤖 Manage AI models
wrangler workflows 🔁 Manage Workflows [open-beta]
wrangler login 🔓 Login to Cloudflare
wrangler logout 🚪 Logout from Cloudflare
wrangler whoami 🕵️ Retrieve your user information
Expand Down
2 changes: 2 additions & 0 deletions packages/wrangler/src/__tests__/navigator-user-agent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ describe("defineNavigatorUserAgent is respected", () => {
serveLegacyAssetsFromWorker: false,
mockAnalyticsEngineDatasets: [],
doBindings: [],
workflowBindings: [],
define: {},
alias: {},
checkFetch: false,
Expand Down Expand Up @@ -175,6 +176,7 @@ describe("defineNavigatorUserAgent is respected", () => {
moduleCollector: noopModuleCollector,
serveLegacyAssetsFromWorker: false,
doBindings: [],
workflowBindings: [],
define: {},
alias: {},
mockAnalyticsEngineDatasets: [],
Expand Down
1 change: 1 addition & 0 deletions packages/wrangler/src/__tests__/type-generation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ const bindingsConfigMock: Omit<
},
],
},
workflows: [],
r2_buckets: [
{
binding: "R2_BUCKET_BINDING",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ describe("versions --help", () => {
wrangler pubsub 📮 Manage Pub/Sub brokers [private beta]
wrangler dispatch-namespace 🏗️ Manage dispatch namespaces
wrangler ai 🤖 Manage AI models
wrangler workflows 🔁 Manage Workflows [open-beta]
wrangler login 🔓 Login to Cloudflare
wrangler logout 🚪 Logout from Cloudflare
wrangler whoami 🕵️ Retrieve your user information
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ function createWorkerBundleFormData(
ai: config?.ai,
version_metadata: config?.version_metadata,
durable_objects: config?.durable_objects,
workflows: config?.workflows,
queues: config?.queues.producers?.map((producer) => {
return { binding: producer.binding, queue_name: producer.queue };
}),
Expand Down
2 changes: 2 additions & 0 deletions packages/wrangler/src/api/startDevWorker/BundlerController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ export class BundlerController extends Controller<BundlerControllerEventMap> {
serveLegacyAssetsFromWorker: Boolean(
config.legacy?.legacyAssets && !config.dev?.remote
),
workflowBindings: bindings?.workflows ?? [],
doBindings: bindings?.durable_objects?.bindings ?? [],
jsxFactory: config.build.jsxFactory,
jsxFragment: config.build.jsxFactory,
Expand Down Expand Up @@ -238,6 +239,7 @@ export class BundlerController extends Controller<BundlerControllerEventMap> {
noBundle: !config.build?.bundle,
findAdditionalModules: config.build?.findAdditionalModules,
durableObjects: bindings?.durable_objects ?? { bindings: [] },
workflows: bindings?.workflows ?? [],
mockAnalyticsEngineDatasets: bindings.analytics_engine_datasets ?? [],
local: !config.dev?.remote,
// startDevWorker only applies to "dev"
Expand Down
2 changes: 2 additions & 0 deletions packages/wrangler/src/api/startDevWorker/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import type {
CfService,
CfUnsafe,
CfVectorize,
CfWorkflow,
} from "../../deployment-bundle/worker";
import type { WorkerRegistry } from "../../dev-registry";
import type { CfAccount } from "../../dev/create-worker-preview";
Expand Down Expand Up @@ -254,6 +255,7 @@ export type Binding =
| { type: "version_metadata" }
| { type: "data_blob"; source: BinaryFile }
| ({ type: "durable_object_namespace" } & BindingOmit<CfDurableObject>)
| ({ type: "workflow" } & BindingOmit<CfWorkflow>)
| ({ type: "queue" } & BindingOmit<CfQueue>)
| ({ type: "r2_bucket" } & BindingOmit<CfR2Bucket>)
| ({ type: "d1" } & Omit<CfD1Database, "binding">)
Expand Down
7 changes: 7 additions & 0 deletions packages/wrangler/src/api/startDevWorker/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,12 @@ export function convertCfWorkerInitBindingstoBindings(
}
break;
}
case "workflows": {
for (const { binding, ...x } of info) {
output[binding] = { type: "workflow", ...x };
}
break;
}
case "queues": {
for (const { binding, ...x } of info) {
output[binding] = { type: "queue", ...x };
Expand Down Expand Up @@ -278,6 +284,7 @@ export async function convertBindingsToCfWorkerInitBindings(
durable_objects: undefined,
queues: undefined,
r2_buckets: undefined,
workflows: undefined,
d1_databases: undefined,
vectorize: undefined,
hyperdrive: undefined,
Expand Down
1 change: 1 addition & 0 deletions packages/wrangler/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ export const defaultWranglerConfig: Config = {
d1_databases: [],
vectorize: [],
hyperdrive: [],
workflows: [],
services: [],
analytics_engine_datasets: [],
ai: undefined,
Expand Down
22 changes: 22 additions & 0 deletions packages/wrangler/src/config/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,17 @@ export type DurableObjectBindings = {
environment?: string;
}[];

export type WorkflowBinding = {
/** The name of the binding used to refer to the Workflow */
binding: string;
/** The name of the Workflow */
name: string;
/** The exported class name of the Workflow */
class_name: string;
/** The script where the Workflow is defined (if it's external to this Worker) */
script_name?: string;
};

/**
* The `EnvironmentNonInheritable` interface declares all the configuration fields for an environment
* that cannot be inherited from the top-level environment, and must be defined specifically.
Expand Down Expand Up @@ -417,6 +428,17 @@ export interface EnvironmentNonInheritable {
bindings: DurableObjectBindings;
};

/**
* A list of workflows that your Worker should be bound to.
*
* NOTE: This field is not automatically inherited from the top level environment,
* and so must be specified in every named environment.
*
* @default `[]`
* @nonInheritable
*/
workflows: WorkflowBinding[];

/**
* Cloudchamber configuration
*
Expand Down
18 changes: 18 additions & 0 deletions packages/wrangler/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ export function printBindings(
const {
data_blobs,
durable_objects,
workflows,
kv_namespaces,
send_email,
queues,
Expand Down Expand Up @@ -278,6 +279,23 @@ export function printBindings(
});
}

if (workflows !== undefined && workflows.length > 0) {
output.push({
type: "Workflows",
entries: workflows.map(({ class_name, script_name, binding }) => {
let value = class_name;
if (script_name) {
value += ` (defined in ${script_name})`;
}

return {
key: binding,
value,
};
}),
});
}

if (kv_namespaces !== undefined && kv_namespaces.length > 0) {
output.push({
type: "KV Namespaces",
Expand Down
Loading
Loading