chore: import upstream snapshot with attribution
Handle Changesets / Handle Changesets (push) Waiting to run
Semgrep OSS scan / semgrep-oss (push) Waiting to run
Deploy (to testing) and Test Playground Preview Worker / Deploy Playground Preview Worker (testing) (push) Has been skipped
Deploy Workers Shared Staging / Deploy Workers Shared Staging (push) Failing after 0s
Prerelease / build (push) Has been skipped

This commit is contained in:
wehub-resource-sync
2026-07-13 12:30:11 +08:00
commit 70bf21e064
5213 changed files with 731895 additions and 0 deletions
+127
View File
@@ -0,0 +1,127 @@
# Changesets
Every non-trivial change to the project - those that should appear in the changelog - must be captured in a "changeset".
We use the [`changesets`](https://github.com/changesets/changesets/blob/main/README.md) tool for creating changesets, publishing versions and updating the changelog.
## Creating a Changeset
```sh
pnpm changeset
```
1. Select which packages are affected by the change
2. Choose whether the version requires a major, minor, or patch release
3. Write a description of the change (see format below)
4. Include the generated changeset in your commit:
```sh
git add .changeset/*.md
```
## Version Types
- **patch**: Bug fixes, small improvements, documentation fixes
- **minor**: New features, new CLI commands, new configuration options, deprecations, and changes to experimental/beta/pre-1.0 features (including breaking changes to those features). When adding or changing experimental features, call this out explicitly in the changeset description.
- **major**: Breaking changes to stable features (when deprecations take effect, or functional breaking behavior is added). Note: breaking changes to experimental/beta features do NOT require a major version bump.
**Important restrictions:**
- Major versions for `wrangler` are currently **forbidden**. This rule will be removed when we are preparing for the next major release of `wrangler`.
- Major versions for other packages require strong justification
- If the change collects more analytics, it should be a minor even though there is no user-visible change.
## Changeset Message Format
```
<TITLE>
<BODY>
```
- **TITLE**: A single sentence with an imperative description of the change
- **BODY**: One or more paragraphs explaining the reason for the change and anything notable about the approach. Aim for more than one sentence but less than three paragraphs to keep it succinct and useful. Larger changes may warrant more detail.
### Good Examples
For a new feature (minor):
```markdown
---
"wrangler": minor
---
Add `wrangler d1 export` command for exporting D1 databases to SQL files
You can now export your D1 database to a local SQL file:
`wrangler d1 export my-database --output backup.sql`
This is useful for creating backups or migrating data between databases.
```
For a bug fix (patch):
```markdown
---
"wrangler": patch
---
Fix `wrangler dev` failing to start when `wrangler.toml` contains Unicode characters
Previously, projects with non-ASCII characters in configuration values would fail with "Invalid UTF-8 sequence". This is now handled correctly.
```
### Bad Examples (avoid these)
- "fix bug" - What bug? What was the symptom?
- "update dependency" - Which one? Why? Any user impact?
- "Add new feature" - What feature? How do you use it?
- "refactor" - Why does this warrant a release? What's the user impact?
## Formatting Rules
### Markdown Headers
Changeset descriptions must **NOT** use h1 (`#`), h2 (`##`), or h3 (`###`) headers.
The changelog uses h3 for section headers, so any headers in changeset content must be h4 (`####`) or smaller. This prevents formatting issues in the generated changelog.
### Code Examples
For new features or significant changes, consider including a brief usage example. Examples can be helpful for users to understand new functionality, but they are not mandatory—use your judgment based on how self-explanatory the change is.
When showing Wrangler configuration examples, use `wrangler.json` (with JSONC syntax for comments) rather than `wrangler.toml`.
## Multiple Changesets
If your PR makes multiple distinct user-facing changes, create separate changesets so each gets its own changelog entry. Don't lump unrelated changes together, and don't mix different types of changes (e.g., bug fix + new feature) in a single changeset.
## Package Coverage
Each changeset should reference all packages that have user-facing changes:
- If a change affects multiple packages, list them all in the changeset
- Alternatively, create separate changesets for each package if the changes warrant different descriptions
- You do NOT need to include packages that will only be released because they depend on a changed package - changesets handles this automatically
## When a Changeset is NOT Required
- Changes that are purely internal refactoring with no user-facing impact
- Changes only to devDependencies
- Documentation-only changes within a package
- Test-only changes
- CI/workflow changes that don't affect package behavior
## File Example
Here's a complete example of a patch changeset file:
```markdown
---
"wrangler": patch
---
Replace the word "publish" with "deploy" everywhere
We should be consistent with the word that describes how we get a worker to the edge. The command is `deploy`, so let's use that everywhere.
```
+7
View File
@@ -0,0 +1,7 @@
---
"miniflare": minor
---
Add `unsafeEvictDurableObject()` for targeted Durable Object eviction
This lets users verify how a Durable Object recovers after its instance is torn down.
+21
View File
@@ -0,0 +1,21 @@
{
"$schema": "https://unpkg.com/@changesets/config@1.6.2/schema.json",
"changelog": [
"@changesets/changelog-github",
{ "repo": "cloudflare/workers-sdk" }
],
"commit": false,
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"bumpVersionsWithWorkspaceProtocolOnly": true,
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
"onlyUpdatePeerDependentsWhenOutOfRange": true
},
"ignore": [],
"privatePackages": {
"tag": true,
"version": true
}
}
@@ -0,0 +1,9 @@
---
"@cloudflare/local-explorer-ui": patch
---
Fix D1 schema editor tab not tracking unsaved changes
The schema editor tab (edit table / create table) now correctly marks the tab as dirty when there are unsaved schema changes. This shows the unsaved changes indicator on the tab, triggers the browser's leave guard when navigating away, and prompts for confirmation when closing the tab.
Additionally, column and constraint deletions now properly mark the schema as dirty. Previously, removing a column or constraint would filter the entry out of the state array entirely, causing the dirty-state check to miss the change.
+7
View File
@@ -0,0 +1,7 @@
---
"create-cloudflare": patch
---
Pass `--no-install` to `@tanstack/cli create` during scaffolding
Previously, the TanStack Start template did not pass `--no-install` to the framework CLI, causing dependencies to be installed twice: once by `@tanstack/cli create` and again by C3's own install step. This aligns the TanStack Start template with other framework templates that already skip the framework CLI's install.
+5
View File
@@ -0,0 +1,5 @@
---
"miniflare": minor
---
Allow `listDurableObjectIds()` to accept Durable Object class names as well as binding names.
+12
View File
@@ -0,0 +1,12 @@
---
"wrangler": minor
---
Add Durable Object eviction support to `createTestHarness`
You can now gracefully evict a running Durable Object by class name or binding name to verify how it recovers after its instance is torn down:
```ts
const worker = server.getWorker();
await worker.evictDurableObject("Counter", { name: "user-123" });
```
+7
View File
@@ -0,0 +1,7 @@
---
"@cloudflare/vite-plugin": patch
---
Fix load time crash on Node.js versions earlier than 22.15
The plugin eagerly imported `registerHooks` from `node:module`, which only exists on Node.js v22.15.0+. `registerHooks` is now read lazily, meaning that missing support is only surfaced when using `experimental.newConfig`.
@@ -0,0 +1,7 @@
---
"wrangler": patch
---
Improve the agent-facing `--force` guidance for Pages-to-Workers delegation
When an AI agent opts out of the Pages-to-Workers delegation by passing `--force` to `wrangler pages deploy` or `wrangler pages project create`, Wrangler now prints a notice at the end of a successful command explaining that `--force` only needs to be passed once: the project then exists, so subsequent commands are no longer delegated and do not need the flag.
@@ -0,0 +1,9 @@
---
"wrangler": minor
---
Remove support for service environments and the `legacy_env` configuration field
Service environments have been removed. Wrangler now always deploys each environment as its own Worker named `<name>-<environment>`, which matches the behaviour of the previous default (`legacy_env = true`). The `--legacy-env` CLI flag has been removed, and the `legacy_env` configuration field is no longer supported — including it in your configuration file will now raise an error.
Because `legacy_env = true` was already the default, removing the field will not change how your Worker is deployed. If you were relying on service environments (`legacy_env = false`), each environment will now be deployed as a standalone Worker instead of as an environment of a single Worker. See https://developers.cloudflare.com/workers/wrangler/environments/ for more information.
@@ -0,0 +1,7 @@
---
"wrangler": patch
---
Suggest similar commands when a typo is detected
When an unknown command or subcommand is entered, wrangler now suggests the closest matching command if one exists within a reasonable edit distance. For example, running `wrangler whoamio` will display `Did you mean "wrangler whoami"?`, and running `wrangler kv namespase` will display `Did you mean "wrangler kv namespace"?`.
+81
View File
@@ -0,0 +1,81 @@
# ============================================================
# Codeowners Plus - Code Ownership Rules
# ============================================================
# This file defines code ownership for the workers-sdk monorepo,
# enforced by Codeowners Plus (https://github.com/multimediallc/codeowners-plus).
#
# See CODEOWNERS.md for full documentation on how ownership works.
#
# Syntax:
# (no prefix) = primary owner (highest-priority match wins)
# & prefix = AND rule (additional required reviewer)
# ? prefix = optional/CC reviewer (non-blocking)
# Multiple teams on one line = OR (either can satisfy)
#
# Rules are relative to this file's directory (repo root).
# Unlike GitHub CODEOWNERS, `*.js` only matches in this directory;
# use `**/*.js` for recursive matching.
#
# Paths not matching any specific rule fall through to the default
# primary owner (currently @cloudflare/wrangler).
# ============================================================
# Default owner - ANT/Wrangler team owns everything
* @cloudflare/wrangler
# ----------------------------------------------------------
# Versioning & release files (wrangler-only, no AND rules)
# ----------------------------------------------------------
# Only wrangler team approval is needed for releases.
# Product teams can request hold on release by reaching out.
**/CHANGELOG.md @cloudflare/wrangler
**/package.json @cloudflare/wrangler
# ----------------------------------------------------------
# D&C ownership (AND: requires wrangler + deploy-config)
# ----------------------------------------------------------
# */** excludes root-level files (CHANGELOG.md, package.json)
# so releases only need wrangler approval
& packages/workers-shared/*/** @cloudflare/deploy-config
# ----------------------------------------------------------
# D1 ownership (AND: requires wrangler + d1)
# ----------------------------------------------------------
& packages/wrangler/src/api/d1/** @cloudflare/d1
& packages/wrangler/src/d1/** @cloudflare/d1
& packages/wrangler/src/__tests__/d1/** @cloudflare/d1
# ----------------------------------------------------------
# Cloudchamber ownership (AND: requires wrangler + cloudchamber)
# ----------------------------------------------------------
& packages/wrangler/src/cloudchamber/** @cloudflare/cloudchamber
& packages/wrangler/src/containers/** @cloudflare/cloudchamber
& packages/wrangler/src/__tests__/containers/** @cloudflare/cloudchamber
# */** excludes root-level files (CHANGELOG.md, package.json)
& packages/containers-shared/*/** @cloudflare/cloudchamber
# ----------------------------------------------------------
# Workers KV ownership (AND: requires wrangler + workers-kv)
# ----------------------------------------------------------
& packages/wrangler/src/kv/** @cloudflare/workers-kv
& packages/wrangler/src/__tests__/kv/** @cloudflare/workers-kv
& packages/miniflare/src/workers/kv/** @cloudflare/workers-kv
& packages/miniflare/test/plugins/kv/** @cloudflare/workers-kv
# ----------------------------------------------------------
# Workflows ownership (AND: requires wrangler + workflows)
# ----------------------------------------------------------
# */** excludes root-level files (CHANGELOG.md, package.json)
& packages/workflows-shared/*/** @cloudflare/workflows
# ----------------------------------------------------------
# Adding a new product team
# ----------------------------------------------------------
# Copy this template and fill in <feature> and <team>:
#
# # Product: <Name> (AND: requires wrangler + <team>)
# & packages/wrangler/src/<feature>/** @cloudflare/<team>
# & packages/wrangler/src/__tests__/<feature>/** @cloudflare/<team>
# & packages/miniflare/src/plugins/<feature>/** @cloudflare/<team>
# & packages/miniflare/src/workers/<feature>/** @cloudflare/<team>
# & packages/miniflare/test/plugins/<feature>/** @cloudflare/<team>
+10
View File
@@ -0,0 +1,10 @@
# https://editorconfig.org
root = true
[*]
end_of_line = lf
indent_style = tab
[*.{yml,yaml}]
indent_style = space
indent_size = 2
+4
View File
@@ -0,0 +1,4 @@
# Refactor from spaces to tabs
e5a6aca696108cda8c3890b8ce2ec44c6cc09a0e
# Prettier 2 -> 3
f6300a71adb83e5f92cd9c9ddd56a85132e03f98
+3
View File
@@ -0,0 +1,3 @@
* text=auto eol=lf
api-extractor.json linguist-language=JSON-with-Comments
tsconfig.emit.json linguist-language=JSON-with-Comments
+4
View File
@@ -0,0 +1,4 @@
version: 2
secret:
ignored-paths:
- "packages/wrangler/src/__tests__/hyperdrive.test.ts"
+35
View File
@@ -0,0 +1,35 @@
name: "\U0001F41B Bug Report"
description: Report an issue or possible bug
type: Bug
body:
- type: markdown
attributes:
value: |
Thank you for taking the time to file a bug report! Please fill out this form as completely as possible.
- type: textarea
attributes:
label: What versions & operating system are you using?
description: "Make sure you've checked that you meet our system requirements as listed here: https://developers.cloudflare.com/workers/wrangler/install-and-update/. Please provide the output of running `npx envinfo --system --npmPackages '{wrangler,create-cloudflare,miniflare,@cloudflare/*}' --binaries`"
placeholder: e.g. Wrangler v4.6, Node v20, Windows 11 etc...
validations:
required: true
- type: input
attributes:
label: Please provide a link to a minimal reproduction
placeholder: https://github.com/foobar-user/minimal-repro
validations:
required: true
- type: textarea
attributes:
label: Describe the Bug
description: Please describe the bug in as much detail as possible, including steps to reproduce, the behaviour you're seeing, and the behaviour you expect.
validations:
required: true
- type: textarea
attributes:
label: Please provide any relevant error logs
description: Although not required, we often request logs to help troubleshoot the issue, so providing this up-front streamlines the process towards resolution. Please be careful to hide any sensitive information.
validations:
required: false
+8
View File
@@ -0,0 +1,8 @@
blank_issues_enabled: true
contact_links:
- name: Feature Request
url: https://github.com/cloudflare/workers-sdk/discussions/new?category=feature-requests
about: "Have a suggestion? Open a discussion to share your idea!"
- name: Support
url: https://discord.cloudflare.com
about: "Join us on Discord!"
@@ -0,0 +1,33 @@
name: "Check Remote Tests"
description: >
Determines whether E2E tests should be given Cloudflare API credentials.
Returns true for merge-queue runs, Version Packages PRs, or when the
"ci:run-remote-tests" label is present on a pull request.
outputs:
run-remote:
description: "'true' when remote tests should run, 'false' otherwise"
value: ${{ steps.decide.outputs.run_remote }}
runs:
using: "composite"
steps:
- name: Decide whether to run remote tests
id: decide
shell: bash
env:
GH_TOKEN: ${{ github.token }}
EVENT_NAME: ${{ github.event_name }}
HEAD_REF: ${{ github.head_ref }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO: ${{ github.repository }}
run: |
if [ "$EVENT_NAME" = "merge_group" ] || \
[ "$HEAD_REF" = "changeset-release/main" ]; then
echo "run_remote=true" >> "$GITHUB_OUTPUT"
elif [ "$EVENT_NAME" = "pull_request" ]; then
HAS_LABEL=$(gh pr view "$PR_NUMBER" \
--repo "$REPO" --json labels \
--jq '[.labels[].name] | any(. == "ci:run-remote-tests")')
echo "run_remote=${HAS_LABEL}" >> "$GITHUB_OUTPUT"
else
echo "run_remote=false" >> "$GITHUB_OUTPUT"
fi
@@ -0,0 +1,5 @@
name: "Expose Actions Variables"
description: "Expose ACTIONS_RUNTIME_TOKEN and ACTIONS_RESULTS_URL on GITHUB_ENV"
runs:
using: "node20"
main: "index.mjs"
@@ -0,0 +1,7 @@
import fs from "node:fs";
fs.appendFileSync(
process.env.GITHUB_ENV,
`GITHUB_ACTIONS_RUNTIME_TOKEN=${process.env.ACTIONS_RUNTIME_TOKEN}\n` +
`GITHUB_ACTIONS_RESULTS_URL=${process.env.ACTIONS_RESULTS_URL}\n`
);
@@ -0,0 +1,62 @@
name: "Install Dependencies"
description: "Install dependencies, fetching from cache when possible"
inputs:
# We run all jobs on Node.js 22 by default, as this is the most stable and supported version right now.
#
# Pinned to 22.23.1 (rather than floating `22`) until it's safe to float back:
# Node 22.23.0 regressed node-fetch@2 keep-alive responses with
# ERR_STREAM_PREMATURE_CLOSE (nodejs/node#63989, fixed in 22.23.1).
node-version:
description: the version of Node.js to install
default: 22.23.1
turbo-api:
description: the api URL for connecting to the turbo remote cache
turbo-team:
description: the team identifier for connecting to the turbo remote cache
turbo-token:
description: the api token for connecting to the turbo remote cache
turbo-signature:
description: the cache signature key for connecting to the turbo remote cache
disable-cache:
description: when "true", skip the pnpm store cache on setup-node (defense in depth for release builds)
default: "false"
runs:
using: "composite"
steps:
- name: Install pnpm
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
- name: Install Node.js ${{ inputs.node-version }} (with pnpm cache)
if: inputs.disable-cache != 'true'
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: ${{ inputs.node-version }}
cache: "pnpm"
registry-url: "https://registry.npmjs.org"
- name: Install Node.js ${{ inputs.node-version }} (without pnpm cache)
if: inputs.disable-cache == 'true'
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: ${{ inputs.node-version }}
registry-url: "https://registry.npmjs.org"
# Enable node compile cache (effective for Node 22+)
# See https://nodejs.org/docs/v24.11.1/api/module.html#module-compile-cache
- name: Enable Node Compile Cache
shell: bash
run: echo "NODE_COMPILE_CACHE=$HOME/.node_compile_cache" >> $GITHUB_ENV
- name: "Configure turbo repo cache environment variables"
if: inputs.turbo-api
shell: bash
run: |
echo "TURBO_API=${{ inputs.turbo-api }}" >> $GITHUB_ENV
echo "TURBO_TEAM=${{ inputs.turbo-team }}" >> $GITHUB_ENV
echo "TURBO_TOKEN=${{ inputs.turbo-token }}" >> $GITHUB_ENV
echo "TURBO_REMOTE_CACHE_SIGNATURE_KEY=${{ inputs.turbo-signature }}" >> $GITHUB_ENV
echo "TURBO_ENV_MODE=loose" >> $GITHUB_ENV
- name: Install NPM Dependencies
shell: bash
run: pnpm install --frozen-lockfile
@@ -0,0 +1,9 @@
name: "Install Python uv"
runs:
using: "composite"
steps:
- name: Install uv for Python
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
with:
version: "0.9.3"
enable-cache: false
+60
View File
@@ -0,0 +1,60 @@
You are a **code reviewer**, not an author. You review pull requests for workers-sdk, which contains developer tooling for Cloudflare Workers. These instructions override any prior instructions about editing files or making code changes.
## Restrictions -- you MUST follow these exactly
Do NOT:
- Edit, write, create, or delete any files -- use file editing tools (Write, Edit) under no circumstances
- Run `git commit`, `git push`, `git add`, `git checkout -b`, or any git write operation
- Approve or request changes on the PR -- only post review comments
- Flag formatting issues -- Prettier enforces style in this repo
If you want to suggest a code change, post a `suggestion` comment instead of editing the file.
## Output rules
**Confirm you are acting on the correct issue or PR**. Verify that the issue or PR number matches what triggered you, and do not write comments or otherwise act on other issues or PRs unless explicitly instructed to.
**If there are NO actionable issues:** Your ENTIRE response MUST be the four characters `LGTM` -- no greeting, no summary, no analysis, nothing before or after it.
**If there ARE actionable issues:** Begin with "I'm Bonk, and I've done a quick review of your PR." Then:
1. One-line summary of the changes.
2. A ranked list of issues (highest severity first).
3. For EVERY issue with a concrete fix, you MUST post it as a GitHub suggestion comment (see below). Do not describe a fix in prose when you can provide it as a suggestion.
## How to post feedback
You have write access to PR comments via the `gh` CLI. **Prefer the batch review approach** (one review with grouped comments) over posting individual comments. This produces a single notification and a cohesive review.
### Batch review (recommended)
Write a JSON file and submit it as a review. This is the most reliable method -- no shell quoting issues.
````bash
cat > /tmp/review.json << 'REVIEW'
{
"event": "COMMENT",
"body": "Review summary here.",
"comments": [
{
"path": "src/workerd/api/example.c++",
"line": 42,
"side": "RIGHT",
"body": "Ownership issue -- `kj::Own` moved but still referenced:\n```suggestion\nauto result = kj::mv(owned);\n```"
}
]
}
REVIEW
gh api repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/reviews --input /tmp/review.json
````
Each comment needs `path`, `line`, `side`, and `body`. Use `suggestion` fences in `body` for applicable changes.
- `side`: `"RIGHT"` for added or unchanged lines, `"LEFT"` for deleted lines
- For multi-line suggestions, add `start_line` and `start_side` to the comment object
- If `gh api` returns a 422 (wrong line number, stale commit), fall back to a top-level PR comment with `gh pr comment` instead of retrying
## What counts as actionable
Logic bugs, security issues (note a lot of this repo covers local development environments), backward compatibility violations, incorrect API behavior. Be pragmatic -- do not nitpick, do not flag subjective preferences.
+147
View File
@@ -0,0 +1,147 @@
const assert = require("node:assert");
const fs = require("node:fs");
const path = require("node:path");
const { execSync } = require("node:child_process");
// This script is used by the `release.yml` workflow to update the version of the packages being released.
// The standard step is only to run `changeset version` but this does not update the lockfile.
// So we also run `pnpm install`, which does this update.
// This is a workaround until this is handled automatically by `changeset version`.
// See https://github.com/changesets/changesets/issues/421.
function getPkg(filePath) {
return JSON.parse(fs.readFileSync(filePath, "utf8"));
}
function setPkg(filePath, newPkg) {
fs.writeFileSync(filePath, `${JSON.stringify(newPkg, null, "\t")}\n`);
}
function parseVersion(version) {
// Extract `<major>.<minor>.<patch>` from version (could be a constraint)
const match = /(\d+)\.(\d+)\.(\d+)/.exec(version);
assert(match !== null, `Expected ${version} to be <major>.<minor>.<patch>`);
return [parseInt(match[1]), parseInt(match[2]), parseInt(match[3])];
}
const rootPath = path.resolve(__dirname, "..");
const miniflarePath = path.join(rootPath, "packages/miniflare");
const miniflarePkgPath = path.join(miniflarePath, "package.json");
function getWorkerdVersion() {
const pnpmWorkspacePath = path.join(rootPath, "pnpm-workspace.yaml");
const match = /workerd: "(\d+\.\d+\.\d+)"/.exec(
fs.readFileSync(pnpmWorkspacePath, "utf8")
);
assert(match !== null, `Expected ${match[1]} to be <major>.<minor>.<patch>`);
return match[1];
}
/**
* Gets the correct version to bump `miniflare` to, ensuring the minor versions
* of `workerd` and `miniflare` match. Minor bumps in changesets will become
* patch bumps if the `workerd` version hasn't changed.
* See `changeset-version.test.js` for examples.
*
* @param workerdVersion `workerd` version constraint in `miniflare` package
* @param previousVersion `miniflare` version before running `changeset version`
* @param version `miniflare` version after running `changeset version`
*/
function getNextMiniflareVersion(workerdVersion, previousVersion, version) {
const [, workerdMinor] = parseVersion(workerdVersion);
const [, , previousPatch] = parseVersion(previousVersion);
const [major, minor] = parseVersion(version);
if (workerdMinor === minor) {
// If the minor versions match already, there's nothing we need to do
return version;
} else if (workerdMinor > minor) {
// If the workerd minor is greater than the miniflare minor,
// use the workerd minor and reset patch to 0
return `${major}.${workerdMinor}.0`;
} else {
// Otherwise, if the workerd minor is less than the miniflare minor,
// use the workerd minor and bump the patch instead
return `${major}.${workerdMinor}.${previousPatch + 1}`;
}
}
exports.getNextMiniflareVersion = getNextMiniflareVersion;
function main() {
// 1. Get `miniflare` version before applying changesets, so we know if the
// minor version was bumped
const previousMiniflarePkg = getPkg(miniflarePkgPath);
const previousMiniflareVersion = previousMiniflarePkg.version;
// 2. Run standard `changeset version` command to apply changesets, bump
// versions, and update changelogs
console.log("Applying changesets and updating versions...");
execSync("pnpm exec changeset version", { stdio: "inherit" });
// 3. Force `miniflare`'s minor version to be the same as `workerd`
console.log("Getting miniflare and workerd versions...");
const miniflarePkg = getPkg(miniflarePkgPath);
const miniflareVersion = miniflarePkg.version;
const workerdVersion = getWorkerdVersion();
const nextMiniflareVersion = getNextMiniflareVersion(
workerdVersion,
previousMiniflareVersion,
miniflareVersion
);
if (nextMiniflareVersion !== miniflareVersion) {
// If `changeset version` didn't produce the correct version on its own...
// ...update `miniflare`'s `package.json` version
console.log(`Updating miniflare version to ${nextMiniflareVersion}...`);
miniflarePkg.version = nextMiniflareVersion;
setPkg(miniflarePkgPath, miniflarePkg);
const changedPathsBuffer = execSync("git ls-files --modified", {
cwd: rootPath,
});
console.log("Checking modified files...");
const changedPaths = changedPathsBuffer.toString().trim().split("\n");
for (const relativeChangedPath of changedPaths) {
const changedPath = path.resolve(rootPath, relativeChangedPath);
const name = path.basename(changedPath);
if (name === "package.json") {
console.log("Updating dependencies in", changedPath);
// ...update `miniflare` version in dependencies of other packages
const pkg = getPkg(changedPath);
let changed = false;
for (const key of [
"dependencies",
"devDependencies",
"peerDependencies",
"optionalDependencies",
]) {
const constraint = pkg[key]?.["miniflare"];
if (constraint === undefined) continue;
// Don't update `workspace:`-style constraints
if (constraint.startsWith("workspace:")) continue;
pkg[key]["miniflare"] = nextMiniflareVersion;
changed = true;
}
if (changed) setPkg(changedPath, pkg);
} else if (name === "CHANGELOG.md") {
console.log("Updating changelog in", changedPath);
// ...update `CHANGELOG.md`s with correct version
let changelog = fs.readFileSync(changedPath, "utf8");
// Replace version header in `miniflare` `CHANGELOG.md`
changelog = changelog.replace(
`## ${miniflareVersion}`,
`## ${nextMiniflareVersion}`
);
// Replace `Updated dependencies` line in other `CHANGELOG.md`s
changelog = changelog.replace(
`- miniflare@${miniflareVersion}`,
`- miniflare@${nextMiniflareVersion}`
);
fs.writeFileSync(changedPath, changelog);
}
}
}
// 4. Update the lockfile
console.log("Updating lockfile...");
execSync("pnpm install --lockfile-only", { stdio: "inherit" });
console.log("Done.");
}
if (require.main === module) main();
+48
View File
@@ -0,0 +1,48 @@
version: 2
updates:
# Automatically check for updates in framework CLIs for C3
- package-ecosystem: "npm"
open-pull-requests-limit: 10
directory: "/packages/create-cloudflare/src/frameworks"
schedule:
interval: "weekly"
# Don't run these at midday on Mondays, when we are busy trying to land PRs
day: "sunday"
time: "06:00"
versioning-strategy: increase
# Match the 24h minimumReleaseAge enforced by pnpm in pnpm-workspace.yaml
# so Dependabot doesn't open PRs that CI will then reject.
cooldown:
default-days: 1
# the following is used to add the [C3] prefix to the PR title,
# we override the commit message but setting a prefix like this
# makes it so that also the PR title gets such prefix
commit-message:
prefix: "[C3] "
labels:
- "package:c3"
- "dependencies"
- "ci:skip-pr-description-validation"
# Check for workerd & workers-types updates for Miniflare
- package-ecosystem: "npm"
# If you restrict the update to a directory that is not the root
# then it will not update the pnpm-lock.yaml.
directory: "/"
groups:
# We want to keep workerd and workers-types updates in lock-step
workerd-and-workers-types:
patterns:
- "workerd"
- "@cloudflare/workers-types"
schedule:
interval: "daily"
time: "06:00"
versioning-strategy: increase
labels:
- "miniflare"
- "dependencies"
- "ci:skip-pr-description-validation"
allow:
- dependency-name: "workerd"
- dependency-name: "@cloudflare/workers-types"
+21
View File
@@ -0,0 +1,21 @@
// This file is not used directly.
// Instead its contents are used in `.github/workflows/write-prerelease-comment.yml`
// Any changes here should be copied into the CI step there.
const allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.payload.workflow_run.id,
});
for (const artifact of allArtifacts.data.artifacts) {
// Extract the PR number from the artifact name
const match = /^npm-package-(.+)-(\d+)$/.exec(artifact.name);
if (match) {
const packageName = match[1].toUpperCase();
require("fs").appendFileSync(
process.env.GITHUB_ENV,
`\nWORKFLOW_RUN_PR_FOR_${packageName}=${match[2]}` +
`\nWORKFLOW_RUN_ID_FOR_${packageName}=${context.payload.workflow_run.id}`
);
}
}
@@ -0,0 +1,26 @@
const { execSync } = require("child_process");
module.exports = function () {
const diff = execSync(
`git diff origin/${process.env.GITHUB_BASE_REF} -- packages/create-cloudflare/src/frameworks/package.json`
).toString();
const changedPackages = diff
.match(/-\s*".*?":\s".*?",?/g)
.map((match) => match.match(/-\s*"(.*)":/)?.[1])
.filter(Boolean);
if (changedPackages.length === 0) {
console.warn("No changes detected!");
return null;
} else if (changedPackages.length > 1) {
console.warn(
`More then one package has changed (${changedPackages.join(
", "
)}), that's not currently supported`
);
throw new Error("More than one package bump detected");
} else {
return changedPackages[0];
}
};
+9
View File
@@ -0,0 +1,9 @@
organization: cloudflare
stickers:
- id: cltg4w6r612040fl2r7vweuvv
alias: "ci:outstanding-contribution"
holobytes:
- evolvingStickerId: cltg4rlqc39020fl51scnguhb
alias: contribution
in: true
+26
View File
@@ -0,0 +1,26 @@
{
"$schema": "https://opencode.ai/config.json",
"disabled_providers": ["opencode"],
"enabled_providers": ["cloudflare-ai-gateway"],
"provider": {
"cloudflare-ai-gateway": {
"models": {
"anthropic/claude-opus-4-8": {},
"anthropic/claude-sonnet-4-5": {},
"workers-ai/@cf/moonshotai/kimi-k2.6": {}
}
}
},
"permission": {
"read": "allow",
"edit": "allow",
"glob": "allow",
"grep": "allow",
"bash": "deny",
"task": "allow",
"skill": "allow",
"todoread": "allow",
"todowrite": "allow",
"webfetch": "deny"
}
}
+50
View File
@@ -0,0 +1,50 @@
import { execSync, spawnSync } from "node:child_process";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const projectRoot = path.resolve(__dirname, "../..");
function getPackagePaths() {
const stdout = execSync(
'pnpm list --filter="./packages/*" --recursive --depth=-1 --parseable',
{ cwd: projectRoot, encoding: "utf8" }
);
return stdout.split("\n").filter((pkgPath) => path.isAbsolute(pkgPath));
}
function getPackage(pkgPath) {
const json = fs.readFileSync(path.join(pkgPath, "package.json"), "utf8");
return {
path: pkgPath,
json: JSON.parse(json),
};
}
function getPackages() {
return getPackagePaths().map(getPackage);
}
export function getPackagesForPrerelease() {
return getPackages().filter((pkg) => pkg.json["workers-sdk"]?.prerelease);
}
const pkgs = getPackagesForPrerelease();
spawnSync(
"pnpm",
[
"dlx",
"pkg-pr-new",
"publish",
"--pnpm",
"--compact",
"--no-template",
...pkgs.map((pkg) => pkg.path),
],
{
stdio: "inherit",
}
);
+25
View File
@@ -0,0 +1,25 @@
Fixes #[insert GH or internal issue link(s)].
_Describe your change..._
---
<!--
Please don't delete the checkboxes <3
The following selections do not need to be completed if this PR only contains changes to .md files
-->
- Tests
- [ ] Tests included/updated
- [ ] Automated tests not possible - manual testing has been completed as follows:
- [ ] Additional testing not necessary because:
- Public documentation
- [ ] Cloudflare docs PR(s): <!--e.g. <https://github.com/cloudflare/cloudflare-docs/pull/>...-->
- [ ] Documentation not necessary because:
*A picture of a cute animal (not mandatory, but encouraged)*
<!--
Have you read our [Contributing guide](https://github.com/cloudflare/workers-sdk/blob/main/CONTRIBUTING.md)?
In particular, for non-trivial changes, please always engage on the issue or create a discussion or feature request issue first before writing your code.
-->
+376
View File
@@ -0,0 +1,376 @@
---
name: github-issue-review
description: Triage a GitHub issue on cloudflare/workers-sdk. Assesses quality, identifies the component, checks for duplicates, and recommends next steps.
---
# GitHub Issue Triage Skill
This skill triages a GitHub issue to assess its quality, identify the affected component, and recommend next steps for the maintainers.
**Important:** This skill runs without bash or network access. All analysis is based on the pre-fetched issue data in `context.json`. Use only `read`, `glob`, `grep`, `edit`, and `write` tools.
## Input
Pre-fetched issue data at `data/<issue_number>/context.json` containing the full issue details from the GitHub API (title, body, comments, labels, author, dates).
## Triage Process
Perform the following steps in order. **Stop and skip to Output as soon as you have sufficient evidence for a recommendation.**
### Step 1: Load Issue Details
Read the pre-fetched `context.json` file using the `read` tool.
Extract and note:
- Title and description
- Issue type (bug report vs feature request vs question)
- Product/component affected
- Version reported against (if any)
- Operating system (if any)
- Reproduction steps or reproduction link (if any)
- Error messages (if any)
- Comment count and any relevant comment content
- Labels currently applied
- Issue age (created date) and last activity date
- `state_reason` if the issue is already closed
### Step 2: Check for Closeable Issues
Check each of the following categories in order. **STOP at the first match** and skip to Output.
> **Note:** The `state_reason` values in this section (`completed`, `not_planned`) are informational — they indicate the appropriate GitHub close reason for maintainer reference. They are not captured as a field in the output report.
#### 2a: Spam or Junk
Recommend **CLOSE** (state_reason: `not_planned`, no comment needed) if:
- The body contains random characters, test text, or nonsensical content (e.g. "bhhg", "ssssssssss", "aloll")
- The issue is clearly spam (e.g. loan advertisements, SEO spam, unrelated product promotions)
- The issue is a blank/empty test issue (e.g. title is "test" with no meaningful body)
- The body is entirely empty or contains only template headers with no content filled in
#### 2b: Reporter Confirmed Resolved
Recommend **CLOSE** (state_reason: `completed`) if:
- The reporter explicitly confirmed the issue is resolved in comments
- The reporter said they found a workaround and no longer need a fix
- A maintainer indicated it should be closed in comments
#### 2c: Already Fixed in a Prior Release
Recommend **CLOSE** (state_reason: `completed`) if, based on references within the issue comments or linked PRs:
- A comment references a PR or release that addresses this issue
- The issue describes a bug that was fixed in a version newer than the reporter's version
- The issue's symptoms match a fix described in a linked PR or comment
When closing, cite the specific PR number and release version if known.
**Template:**
> This issue has been fixed in PR #XXXX, which was released in **wrangler X.Y.Z** (or the relevant package version). Please update and let us know if you're still seeing the issue.
Or for older issues where the fix can't be pinpointed:
> This is a fairly old issue, and from testing with the latest version, it appears to have been resolved. I'm going to close it for now — if you're still running into problems on the latest version, feel free to open a new issue with more details and we can investigate further.
#### 2d: Duplicate
Recommend **CLOSE** (state_reason: `not_planned`) if a comment or linked reference in the issue identifies it as a duplicate of another issue — for example, a maintainer has already commented pointing to a canonical issue, or the reporter themselves links to an existing report.
When closing, link to the canonical issue.
**Template:**
> Closing as a duplicate of #XXXX. Please follow that issue for updates.
If the duplicate is in another repo:
> I'm closing this as a duplicate of <owner/repo>#XXXX, which is where this is being tracked.
#### 2e: Not workers-sdk / Wrong Repo
Recommend **CLOSE** (state_reason: `not_planned`) if:
- The issue describes Workers **runtime** behavior (fetch API quirks, V8 issues, compatibility flags, crypto APIs) — belongs in [cloudflare/workerd](https://github.com/cloudflare/workerd)
- The issue is about a third-party framework (Nuxt, SvelteKit, Remix, Hono, React Router) and the bug is in that framework's code, not in Wrangler or the Vite plugin
- The issue is about an upstream tool (esbuild, Bun, Vite core) rather than Cloudflare's integration
- The issue is an account/billing/abuse problem — belongs in Cloudflare Support
**Templates:**
For framework issues:
> It looks like this issue is coming from <framework>, rather than from workers-sdk. I'd recommend opening an issue on that project's repo: <link>. If it turns out to be a workers-sdk issue after all, feel free to open a new issue here with more details.
For runtime issues:
> This looks like a Workers runtime issue rather than a tooling issue. The right place to track this is [cloudflare/workerd](https://github.com/cloudflare/workerd) — could a maintainer transfer this issue there?
(Set Action field to: "Transfer to cloudflare/workerd, then post this comment.")
For account/support issues:
> Unfortunately, we're unable to provide support for account-level issues via GitHub. Please contact Cloudflare Support at https://dash.cloudflare.com/?to=/:account/support or the email address mentioned in the error message.
#### 2f: Stale / No Response
Recommend **CLOSE** (state_reason: `not_planned`) if:
- The issue has the `awaiting reporter response` or `needs reproduction` label AND the last activity was >30 days ago
- The issue is >12 months old with no activity in the last 6 months
- A maintainer asked for a reproduction or clarification and the reporter never responded
**Template:**
> We haven't heard from you in a while so I'm going to close this issue for now. If you're still running into problems, feel free to open a new issue with more details and we can investigate further.
For very old issues:
> This is a very old issue so I'm going to close it for now. If you're still running into problems on the latest version, feel free to comment with more details and we can investigate further.
#### 2g: Transient Platform Issue
Recommend **CLOSE** (state_reason: `completed`) if:
- The issue describes a one-time API error (500, 503, auth failures) that appears to have been a service incident
- Multiple users reported the same error around the same time, and it has since stopped
- The error is from Cloudflare's API (not from Wrangler itself) and there's no way to fix it in workers-sdk
**Template:**
> This was a transient API issue, and should be resolved now. If you're still seeing this error, please open a new issue with the latest details and we can investigate further.
Or for old transient issues:
> This appears to have been a transient server-side API issue. Similar reports were caused by Cloudflare API incidents that have since been resolved. I'm going to close this for now.
#### 2h: User Error / Misunderstanding
Recommend **CLOSE** (state_reason: `not_planned`) if:
- The issue describes expected behavior that the reporter misunderstands
- The issue is caused by incorrect configuration, wrong SQL syntax, or misuse of an API
- The fix is a documentation pointer rather than a code change
When closing, explain the correct approach and link to relevant documentation. Be helpful, not dismissive.
**Template:**
> This isn't a bug — <brief explanation of the correct behavior>. You can find more details in the docs: <link>. I'm going to close this for now, but feel free to ask on our [Discord](https://discord.cloudflare.com) if you have more questions.
#### 2i: Breaking Change
Recommend **KEEP OPEN** if:
- The requested change would be a breaking change in the current major version
The issue should remain open as a tracking item. Do not close it — the team hasn't decided against the change, it's deferred to a future major version.
**Suggested labels:** `breaking change`
**Template:**
> Changing this behavior at this stage would be a breaking change, so it will need to wait for the next major version. I've added the `breaking change` label to track this.
(Set Action field to: "Apply `breaking change` label, then post this comment.")
#### 2j: Won't Fix / By Design
Recommend **CLOSE** (state_reason: `not_planned`) if:
- The behavior is intentional and documented
- The team has explicitly decided not to implement this feature
- The cost/complexity of the change outweighs the benefit
**Templates:**
For design decisions:
> This is intentional behavior — <brief explanation>. I don't think we're likely to change this in the near future, but we appreciate the feedback.
For feature requests that won't happen:
> Thanks for the suggestion. We don't intend to implement this right now, but it may be revisited in future. <Optional: brief explanation of why, or pointer to an alternative approach.>
#### 2k: Feature Superseded / Deprecated
Recommend **CLOSE** (state_reason: `completed`) if:
- The requested feature has been implemented via a different mechanism than proposed
- The issue relates to a deprecated feature that has a replacement
- A newer feature or package addresses the underlying need
**Template:**
> This has been addressed by <feature/package>. <Brief explanation of how it solves the original request.> I'm going to close this for now.
### Step 3: Check for Insufficient Information
If the issue wasn't caught by Step 2, recommend **NEEDS MORE INFO** if:
- Bug report has no reproduction steps or link (the bug template requires one)
- Bug report is missing version information
- The description is too vague to act on (e.g. "it doesn't work" with no details)
- The error message is missing or truncated
**Template:**
> Thanks for reporting this. Could you provide <specific missing information>? In particular:
>
> - <specific question 1>
> - <specific question 2>
>
> A minimal reproduction (a GitHub repo or link we can clone and run) would also help us investigate. Without more details, we won't be able to look into this.
**Suggested label:** `awaiting reporter response` (and optionally `needs reproduction`)
**STOP HERE if** the issue clearly needs more info. Skip to Output.
### Step 4: Identify Component
Map the issue to a package based on labels, title, and body content:
| Signal | Package |
| ------------------------------------------------------------------------ | ----------------------------------------------- |
| `wrangler` label, wrangler CLI commands, `wrangler.toml`/`wrangler.json` | `packages/wrangler` |
| `miniflare` label, local dev simulation | `packages/miniflare` |
| `d1` label, D1 database, `d1 execute`, migrations | `packages/wrangler` (D1 code is in wrangler) |
| `vitest` label, worker tests, `vitest-pool-workers` | `packages/vitest-pool-workers` |
| `vite-plugin` label, vite dev, `@cloudflare/vite-plugin` | `packages/vite-plugin-cloudflare` |
| `c3` label, `create-cloudflare`, project scaffolding | `packages/create-cloudflare` |
| `pages` label, Pages deployment, `_routes.json`, `_headers` | `packages/wrangler` (Pages code is in wrangler) |
| `Workers + Assets` label, static asset serving | `packages/wrangler` |
| `containers` label, container registry | `packages/wrangler` |
| `workflows` label, Workflows API | `packages/wrangler` |
| `workers-builds` label | Workers Builds (may be internal) |
| `python` label, Python Workers | `packages/wrangler` |
| `workers for platforms` label, dispatch namespaces | `packages/wrangler` |
| `kv-asset-handler` label | `packages/kv-asset-handler` |
| `types` label, `wrangler types` command | `packages/wrangler` |
| R2, KV, Queues, Durable Objects, Vectorize bindings | `packages/wrangler` |
| `node compat`/`nodejs compat` label, Node.js APIs | May be workerd or wrangler depending on context |
| Workers runtime behavior (not tooling) | Likely belongs in cloudflare/workerd |
| Cloudflare dashboard, API behavior | Likely a platform issue, not workers-sdk |
### Step 5: Assess Reproducibility and Severity
For **bug reports**, evaluate:
- **Has reproduction?** Does the issue include a minimal repro link or clear steps?
- **Severity estimate:** Is this a crash, data loss, incorrect behavior, or cosmetic issue?
- **Scope:** Does it affect all users or a specific configuration?
- **Workaround available?** Did the reporter or comments mention one?
- **Version gap:** Is the reporter on an old version? Could updating fix this?
For **feature requests**, evaluate:
- **Clarity:** Is the proposed solution clearly described?
- **Use case:** Is the motivation explained?
- **Scope:** Small enhancement vs large new feature?
- **Existing alternatives:** Is there already a way to achieve this (even if less convenient)?
## Output Format
**How the output is used:** `report.md` is posted verbatim as a maintainer-facing triage comment on the issue (attributed to an automated bot), and the labels in `summary.json` are applied automatically. Because of this:
- Write `report.md` for maintainers, not the reporter. The **Suggested Comment** section is a _draft_ for a maintainer to send to the reporter — it is **not** posted automatically, so do not phrase the surrounding report as though a reply has already been made.
- Only put labels in the **Suggested Labels** fields that already exist in the repository. If unsure whether a label exists, omit it rather than guess — non-existent labels are dropped before applying, so inventing labels only weakens the report's accuracy.
### Report Directory Structure
```
./data/<issue_number>/
├── report.md # Full detailed report
└── summary.json # Structured JSON summary
```
### Output Step 1: Write Full Report
Write the full report to `./data/<issue_number>/report.md`:
```markdown
# Issue Triage: <owner/repo>#<number>
## Summary
<One-line summary of the issue>
## Classification
- **Type:** <Bug | Feature Request | Question | Discussion>
- **Component:** <package name or "unknown">
- **Severity:** <Critical | High | Medium | Low | N/A>
- **Has Reproduction:** <Yes (with link) | No | N/A>
- **Quality:** <Complete | Partial | Incomplete>
## Findings
- **Created:** <date>
- **Author:** <username>
- **Version:** <reported version, or "not specified">
- **Labels:** <labels, or "none">
- **Comments:** <count>
### Analysis
<2-5 bullet points covering what you found — component identification,
reproducibility assessment, severity reasoning, staleness indicators,
duplicate candidates. Only include what's relevant.>
## Recommendation
**Status:** <CLOSE | KEEP OPEN | NEEDS MORE INFO | NEEDS VERIFICATION>
**Reasoning:** <2-3 sentences explaining why>
**Action:** <What a maintainer should do next>
**Suggested Labels:** <labels to add, if any. Use existing repo labels only.>
### Suggested Comment
> <The exact comment to post on the issue. Follow the templates above for the
> matching closure category. Key principles:
>
> - Always offer an escape hatch ("feel free to open a new issue")
> - Link to specific PRs, releases, or docs when available
> - Be concise for straightforward closures, detailed for design decisions
> - Never be dismissive or snarky, even for spam (just close silently) or user error
> - For NEEDS MORE INFO, ask specific questions (not generic "please provide more details")
> Omit this section entirely for spam (close without comment) or if no comment is needed.>
```
### Output Step 2: Write Summary File
Write a machine-readable summary as **JSON** to `./data/<issue_number>/summary.json`. This file is parsed by the triage workflow (to build the dashboard payload and apply labels), so it must be valid JSON with exactly these keys:
```json
{
"issueNumber": <issue_number as a number>,
"title": "<issue title, with emoji/template prefixes like 'Bug:' removed>",
"githubUrl": "https://github.com/<owner>/<repo>/issues/<issue_number>",
"recommendation": "<CLOSE | KEEP OPEN | NEEDS MORE INFO | NEEDS VERIFICATION>",
"difficulty": "<easy | medium | hard | n/a>",
"reasoning": "<brief reasoning, 1-2 sentences>",
"suggestedAction": "<brief description of next steps>",
"hasSuggestedComment": <true if a Suggested Comment section is present in the report, false otherwise>,
"suggestedLabels": ["<label>", "..."]
}
```
**Key definitions:**
- **issueNumber**: The issue number as a JSON number (not a string).
- **title**: Issue title with any emoji prefixes (e.g. "🐛 Bug:") or template prefixes removed.
- **githubUrl**: Full URL to the issue.
- **recommendation**: One of `CLOSE`, `KEEP OPEN`, `NEEDS MORE INFO`, `NEEDS VERIFICATION`.
- **difficulty**: Estimated fix difficulty — `easy`, `medium`, `hard`, or `n/a` (for feature requests or closures).
- **reasoning**: Brief summary of why (1-2 sentences).
- **suggestedAction**: Brief description of next steps.
- **hasSuggestedComment**: Boolean — `true` if the report includes a Suggested Comment, `false` otherwise.
- **suggestedLabels**: JSON array of labels to apply, matching the **Suggested Labels** field in the report. Use existing repo labels only (see above). Use an empty array `[]` if there are no labels to apply.
**CRITICAL:** Write valid, parseable JSON only (no trailing commas, no comments, no surrounding markdown fences). Because JSON strings are escaped, `reasoning`/`suggestedAction` may safely contain any punctuation.
+35
View File
@@ -0,0 +1,35 @@
/**
* Update the package.json version property for the given package
*
* Usage:
*
* ```
* node ./.github/version-script.js <package-name>
* ```
*
* `<package-name>` defaults to `wrangler` if not provided.
*/
const { readFileSync, writeFileSync } = require("fs");
const { execSync } = require("child_process");
try {
const packageName = getArgs()[0] ?? "wrangler";
const packageJsonPath = `./packages/${packageName}/package.json`;
const pkg = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
const stdout = execSync("git rev-parse --short HEAD", { encoding: "utf8" });
pkg.version = "0.0.0-" + stdout.trim();
writeFileSync(packageJsonPath, JSON.stringify(pkg, null, "\t") + "\n");
} catch (error) {
console.error(error);
process.exit(1);
}
/**
* Get the command line args, stripping `node` and script filename, etc.
*/
function getArgs() {
const args = Array.from(process.argv);
while (args.shift() !== module.filename) {}
return args;
}
+151
View File
@@ -0,0 +1,151 @@
# GitHub Actions
See below for a summary of this repo's Actions
- _Actions marked with "⚠️" are expected to sometimes fail._
## Security auditing
We use [`zizmor`](https://docs.zizmor.sh/) to audit GitHub Actions workflow definitions and keep CI workflows as safe as possible. When changing files in this directory, run:
```sh
zizmor .github/workflows/*.yml
```
Workflow changes should avoid unsuppressed `zizmor` findings. In particular:
- Pin external actions to immutable commit SHAs, not tags.
- Use `actions/checkout` v6 or newer so persisted credentials are stored under `$RUNNER_TEMP`; set `persist-credentials: false` when a job does not need follow-up authenticated Git operations.
- Pass GitHub expression values into shell steps through `env` instead of expanding `${{ ... }}` directly inside `run` blocks.
- Treat privileged triggers such as `pull_request_target` and `workflow_run` as security-sensitive. If a privileged trigger is required, document the safety model and add a targeted `zizmor` ignore with a reason.
## PR related actions
### Tests + Checks (test-and-check.yml)
- Triggers
- Updates to PRs.
- PRs in the merge queue.
- Actions
- Builds all the packages.
- Runs formatting, linting and type checks.
- Runs fixture tests, Wrangler unit tests, C3 unit tests, Miniflare unit tests, and ESLint + Prettier checks.
- Adds the PR to a GitHub project
- Makes sure that Wrangler's warning for old Node.js versions works.
### Wrangler E2E tests (e2e-wrangler.yml)
- Triggers
- Updates to PRs on the Cloudflare fork.
- PRs in the merge queue.
- Actions
- Runs the E2E tests for Wrangler.
- Cloudflare API credentials are only passed on Version Packages PRs (`changeset-release/main`), in the merge queue, or when the `ci:run-remote-tests` label is applied. Other PRs run the E2E suite without remote tests.
### Vite Plugin E2E tests (e2e-vite.yml)
- Triggers
- Updates to PRs on the Cloudflare fork.
- PRs in the merge queue.
- Actions
- Runs the E2E tests for the Vite plugin.
- Cloudflare API credentials are only passed on Version Packages PRs (`changeset-release/main`), in the merge queue, or when the `ci:run-remote-tests` label is applied. Other PRs run the E2E suite without remote tests.
## Deploy Pages Previews (deploy-pages-preview.yml)
- Triggers
- Updates to PRs that have one of the `preview:...` labels.
- Actions
- Deploy a preview of the matching Pages project to Cloudflare.
## Deploy (to testing) and Test Playground Preview Worker (worker-playground-preview-testing-env-deploy-and-test.yml)
- Triggers
- Commits merged to the `main` branch, on the Cloudflare fork, which touch files in the `packages/playground-preview-worker` directory.
- Updates to PRs, on the Cloudflare fork, with the `playground-worker` label applied.
- Actions
- Runs integrations tests to ensure the behaviour of the Worker powering the Workers Playground.
## Create Pull Request Prerelease (prerelease.yml)
- Triggers
- Updates to PRs.
- Actions
- Creates an installable pre-release of any package containing `{ "workers-sdk": { "prerelease": true } }` in its `package.json` (e.g. Wrangler, C3, and Miniflare) on every PR.
- Adds a comment to the PR with links to the pre-releases.
## Housekeeping actions
### Add issues to DevProd project (issues.yml)
- Triggers
- Updates to issues.
- Actions
- Add the issue to a GitHub project.
### Triage Issue (triage-issue.yml)
- Triggers
- A new issue is opened (skips PRs and bot-authored issues).
- A new comment is created on an issue — re-triages with the latest context. Skips PRs, bot comments, and the triage bot's own sticky comment (matched by the sticky-comment marker) to avoid re-triage loops.
- Manual `workflow_dispatch` with an `issue-number` input.
- Actions
- Runs an OpenCode agent with the `.github/skills/issue-review.md` skill against the pre-fetched issue data (including existing comments) to produce a markdown triage report (`report.md`) and a structured JSON summary (`summary.json`).
- Uploads the report to the triage dashboard.
- Posts the report and structured summary as a maintainer-facing sticky comment on the issue and applies the suggested labels (validated against existing repo labels). Because the comment is sticky, re-triage updates the existing comment rather than posting a duplicate. Comments and labels are attributed to the workers-devprod bot via `GH_ACCESS_TOKEN`.
- The AI agent itself runs sandboxed (no shell or network access); all GitHub writes happen in workflow steps from the generated files.
### Generate changesets for dependabot PRs (c3-dependabot-versioning-prs.yml and miniflare-dependabot-versioning-prs.yml)
- Triggers
- Updates to PRs, by the dependabot user, which update one of:
- frameworks dependencies in C3,
- miniflare.
- Actions
- Generates changesets for the affected package.
### E2E Project Cleanup (e2e-project-cleanup.yml)
- Triggers
- Scheduled to run at 3am each day.
- Actions
- Deletes any Workers and Pages projects that were not properly cleaned up by the E2E tests.
## Main branch actions
### Handle Changesets (changesets.yml)
- Triggers
- Commits merged to the `main` branch, on the Cloudflare fork.
- Actions
- If there are changeset in the working directory, create or update a "Version Packages" PR to prep for a release.
- If there are no changesets, release any packages that have a bump to their version in this change.
- Public packages are deployed to npm
- Private packages will run their `deploy` script, if they have one.
## C3 related actions
### C3 E2E Tests (c3-e2e.yml)
- Triggers
- Updates to PRs.
- Actions
- Runs the E2E tests for C3.
- Cloudflare API credentials are only passed on Version Packages PRs (`changeset-release/main`), in the merge queue, or when the `ci:run-remote-tests` label is applied. Other PRs run the E2E suite without remote tests.
### Rerun Code Owners (rerun-codeowners.yml + rerun-codeowners-privileged.yml)
- Triggers
- A review is submitted or dismissed on a PR.
- Actions
- Re-runs the "Run Codeowners Plus" check so it re-evaluates approval status after the review change.
- Uses the `workflow_run` pattern: the trigger workflow exists solely to fire a `workflow_run` event; the privileged companion workflow (which has full permissions) reads the PR head SHA from `github.event.workflow_run.head_sha` and performs the re-run. This is necessary because `pull_request_review` gives a read-only token for fork PRs and has no `_target` variant.
### Rerun Remote Tests (rerun-remote-tests.yml)
- Triggers
- The `ci:run-remote-tests` or `run-c3-frameworks-tests` label is added to or removed from a PR.
- Actions
- Re-runs the E2E workflows for the PR so they pick up the label change and pass (or withhold) API credentials to the test steps.
- `ci:run-remote-tests` re-runs Wrangler, Vite, and C3 E2E workflows; `run-c3-frameworks-tests` re-runs only C3 E2E.
- Uses `pull_request_target` to get a privileged token even for fork PRs (safe because no untrusted code is checked out).
+69
View File
@@ -0,0 +1,69 @@
name: New PR Review
on:
pull_request:
types: [opened]
jobs:
review:
# Skip Version Packages PRs (auto-generated by the changesets action) since they don't need a Bonk review
if: |
github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name &&
!(github.event.pull_request.base.repo.owner.login == 'cloudflare' && github.event.pull_request.head.ref == 'changeset-release/main')
runs-on: ubuntu-latest
timeout-minutes: 30
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: false
permissions:
id-token: write
contents: read
issues: write
pull-requests: write
steps:
- name: Check if PR author is Cloudflare org member
run: |
STATUS=$(gh api \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"/orgs/cloudflare/members/${PR_AUTHOR}" \
--silent -i 2>/dev/null | head -1 | awk '{print $2}') || true
if [ "$STATUS" != "204" ]; then
echo "User ${PR_AUTHOR} is not a member of the Cloudflare organization"
exit 1
fi
echo "User ${PR_AUTHOR} is a Cloudflare org member"
env:
GH_TOKEN: ${{ secrets.READ_ONLY_ORG_GITHUB_TOKEN }}
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 30
persist-credentials: false
- name: Load review prompt
id: prompt
run: |
{
echo 'value<<EOF'
echo "You are reviewing PR #${{ github.event.pull_request.number }} on ${{ github.repository }}."
echo ""
cat .github/bonk_reviewer.md
echo EOF
} >> "$GITHUB_OUTPUT"
- name: Run Bonk
uses: ask-bonk/ask-bonk/github@24832587005550860c8ad13fd96519e731ca632a # main
env:
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CF_AI_GATEWAY_ACCOUNT_ID }}
CLOUDFLARE_GATEWAY_ID: ${{ secrets.CF_AI_GATEWAY_NAME }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CF_AI_GATEWAY_TOKEN }}
with:
model: "cloudflare-ai-gateway/anthropic/claude-opus-4-8"
variant: "high"
forks: "false"
permissions: write
opencode_version: 1.15.13 # pin to this version as certain ones cause ProviderInitError issues
prompt: ${{ steps.prompt.outputs.value }}
+80
View File
@@ -0,0 +1,80 @@
name: Bonk
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.event.issue.number || github.ref }}
cancel-in-progress: false
jobs:
bonk:
if: github.event.sender.type != 'Bot' && (contains(github.event.comment.body, '/bonk') || contains(github.event.comment.body, '@ask-bonk'))
runs-on: ubuntu-latest
timeout-minutes: 60
permissions:
id-token: write
contents: write
issues: write
pull-requests: write
steps:
- name: Check if comment author is Cloudflare org member
run: |
STATUS=$(gh api \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"/orgs/cloudflare/members/${COMMENT_AUTHOR}" \
--silent -i 2>/dev/null | head -1 | awk '{print $2}') || true
if [ "$STATUS" != "204" ]; then
echo "User ${COMMENT_AUTHOR} is not a member of the Cloudflare organization"
exit 1
fi
echo "User ${COMMENT_AUTHOR} is a Cloudflare org member"
env:
GH_TOKEN: ${{ secrets.READ_ONLY_ORG_GITHUB_TOKEN }}
COMMENT_AUTHOR: ${{ github.event.comment.user.login }}
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 1
persist-credentials: false
- name: Build prompt with triggering comment
id: prompt
run: |
{
DELIMITER=$(openssl rand -hex 16)
echo "value<<$DELIMITER"
echo "You were invoked by @${COMMENT_AUTHOR} on ${COMMENT_URL}"
echo ""
echo "Their comment:"
echo '```'
echo "$COMMENT_BODY"
echo '```'
echo ""
echo "This is your task. Read it carefully and act on it."
echo "$DELIMITER"
} >> "$GITHUB_OUTPUT"
env:
COMMENT_AUTHOR: ${{ github.event.comment.user.login }}
COMMENT_BODY: ${{ github.event.comment.body }}
COMMENT_URL: ${{ github.event.comment.html_url }}
- name: Run Bonk
uses: ask-bonk/ask-bonk/github@c39e982defd0114385df54e72012a3fc4333c4d4
env:
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CF_AI_GATEWAY_ACCOUNT_ID }}
CLOUDFLARE_GATEWAY_ID: ${{ secrets.CF_AI_GATEWAY_NAME }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CF_AI_GATEWAY_TOKEN }}
with:
model: "cloudflare-ai-gateway/anthropic/claude-opus-4-8"
variant: "high"
mentions: "/bonk,@ask-bonk"
permissions: write
agent: bonk
opencode_version: 1.15.13 # pin to this version as certain ones cause ProviderInitError issues
prompt: ${{ steps.prompt.outputs.value }}
@@ -0,0 +1,39 @@
name: "C3 - Generate changesets for dependabot PRs"
on:
pull_request_target: # zizmor: ignore[dangerous-triggers] dependabot-only job requires write access to push generated changesets; checkout v6 persists credentials under RUNNER_TEMP
paths:
- "packages/create-cloudflare/src/frameworks/package.json"
permissions:
# content:write permission needed to update add changesets to dependabot PRs
# (see tools/dependabot/generate-dependabot-pr-changesets.ts)
contents: write
jobs:
generate-changeset:
runs-on: ubuntu-slim
if: |
github.event.pull_request.user.login == 'dependabot[bot]' &&
github.event.pull_request.head.repo.full_name == github.repository
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 2
persist-credentials: true
ref: ${{ github.head_ref }}
token: ${{ secrets.GH_ACCESS_TOKEN }}
- name: Install Dependencies
uses: ./.github/actions/install-dependencies
- name: Configure Git
run: |
git config --global user.email wrangler@cloudflare.com
git config --global user.name 'Wrangler automated PR updater'
- name: Generate C3 changesets
# Keep the final param (the changeset prefix: `c3-frameworks-update`) in sync with the filter in the `.github/workflows/c3-e2e-dependabot.yml` workflow.
run: node -r esbuild-register tools/dependabot/generate-dependabot-pr-changesets.ts "$PR_NUMBER" create-cloudflare packages/create-cloudflare/src/frameworks/package.json c3-frameworks-update
env:
PR_NUMBER: ${{ github.event.number }}
+311
View File
@@ -0,0 +1,311 @@
name: C3 E2E
on:
merge_group:
pull_request:
permissions:
contents: read
pull-requests: read
# pnpm 11-specific settings - recognised by pnpm 11+ and
# silently ignored by pnpm 10.
#
# - pmOnFail=ignore: pnpm 11's default `download` would silently re-exec as
# the monorepo's pinned pnpm 10.33.0, defeating the matrix override.
# - verifyDepsBeforeRun=false: pnpm 11's default `install` would re-run
# `pnpm install` at the monorepo root before invoking the script, which
# then trips on engines.pnpm.
env:
pnpm_config_pm_on_fail: ignore
pnpm_config_verify_deps_before_run: "false"
jobs:
e2e:
# Runs the non-frameworks C3 E2E tests on all supported operating systems
# and package managers.
timeout-minutes: 45
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.os.name }}-${{ matrix.pm.name }}-${{ matrix.pm.version }}${{ matrix.experimental && '-experimental' || '' }}-${{ matrix.filter }}
cancel-in-progress: ${{ github.head_ref != 'changeset-release/main' }}
# `matrix.pm.label` is the display name in job/artifact names. Default
# pm entries use the plain name (e.g. `pnpm`) to keep historical required-
# check names stable; non-default entries (e.g. pnpm 11) suffix the version.
name: ${{ format('C3 E2E ({0}, {1}{2}) - {3}', matrix.pm.label, matrix.os.description, matrix.experimental && ', experimental' || '', matrix.filter) }}
strategy:
fail-fast: false
matrix:
experimental: [false]
filter: ["cli", "workers"]
os: [{ name: ubuntu-latest, description: Linux }]
pm:
- { name: pnpm, version: "10.33.0", label: "pnpm" }
- { name: pnpm, version: "11.5.1", label: "pnpm@11.5.1" }
- { name: npm, version: "0.0.0", label: "npm" }
# The yarn tests keep failing on Linux with out of space errors, with no clear reason why. Disabling for now.
# - { name: yarn, version: "1.0.0", label: "yarn" }
include:
# Windows and experimental entries stay on the default pnpm to
# preserve historical required-check names. pnpm 11 coverage comes
# from the Linux base entries above.
- os: { name: windows-latest, description: Windows }
pm: { name: pnpm, version: "10.33.0", label: "pnpm" }
filter: "cli"
- os: { name: windows-latest, description: Windows }
pm: { name: pnpm, version: "10.33.0", label: "pnpm" }
filter: "workers"
- os: { name: ubuntu-latest, description: Linux }
pm: { name: pnpm, version: "10.33.0", label: "pnpm" }
experimental: true
filter: "cli"
- os: { name: ubuntu-latest, description: Linux }
pm: { name: pnpm, version: "10.33.0", label: "pnpm" }
experimental: true
filter: "workers"
runs-on: ${{ matrix.os.name }}
steps:
- name: Checkout Repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
persist-credentials: false
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: changes
with:
filters: |
everything_but_markdown:
- '!**/*.md'
- name: Install Dependencies
if: steps.changes.outputs.everything_but_markdown == 'true'
uses: ./.github/actions/install-dependencies
with:
turbo-api: ${{ secrets.TURBO_API }}
turbo-team: ${{ secrets.TURBO_TEAM }}
turbo-token: ${{ secrets.TURBO_TOKEN }}
turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
- name: Install Python uv
if: steps.changes.outputs.everything_but_markdown == 'true'
uses: ./.github/actions/install-python-uv
# Needed because gatsby requires git
- name: Configure Git
shell: bash
run: |
git config --global user.email wrangler@cloudflare.com
git config --global user.name 'Wrangler Tester'
# Put the matrix pnpm version on PATH so scaffolded-project installs
# run it (not the monorepo's pinned pnpm 10.33.0). Without this, the
# matrix only relabels C3's `npm_config_user_agent` and pnpm 11
# regressions go undetected.
#
# `pnpm/action-setup` refuses when both `version` input and the root
# `package.json#packageManager` are set, so we point it at a stub
# `package.json`. We also loosen `engines.pnpm` to avoid pnpm 11
# bailing with ERR_PNPM_UNSUPPORTED_ENGINE at the monorepo root.
- name: Prepare workspace for matrix pnpm install
if: matrix.pm.name == 'pnpm' && steps.changes.outputs.everything_but_markdown == 'true'
shell: bash
run: |
mkdir -p .ci-pnpm-setup-stub
printf '{"name":"ci-stub","private":true}\n' > .ci-pnpm-setup-stub/package.json
node -e "
const fs = require('node:fs');
const pkg = JSON.parse(fs.readFileSync('package.json','utf8'));
if (pkg.engines && pkg.engines.pnpm) pkg.engines.pnpm = '*';
fs.writeFileSync('package.json', JSON.stringify(pkg, null, '\t') + '\n');
"
- name: Install matrix pnpm version for scaffolded installs
if: matrix.pm.name == 'pnpm' && steps.changes.outputs.everything_but_markdown == 'true'
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
with:
version: ${{ matrix.pm.version }}
package_json_file: .ci-pnpm-setup-stub/package.json
# Distinct dest so `addPath` prepends ahead of the monorepo's
# pinned pnpm 10.x from the earlier `~/setup-pnpm`.
dest: ~/matrix-pnpm
- name: Verify pnpm on PATH matches matrix version
if: matrix.pm.name == 'pnpm' && steps.changes.outputs.everything_but_markdown == 'true'
shell: bash
run: |
got="$(pnpm --version)"
want="${{ matrix.pm.version }}"
echo "pnpm on PATH: $got (expected $want)"
[ "$got" = "$want" ] || { echo "ERROR: PATH pnpm is $got, not $want — scaffolded installs would silently run the wrong version."; exit 1; }
- id: run-e2e
if: steps.changes.outputs.everything_but_markdown == 'true'
shell: bash
run: pnpm run test:e2e:c3
env:
NODE_VERSION: ${{ env.NODE_VERSION }}
E2E_EXPERIMENTAL: ${{ matrix.experimental }}
E2E_TEST_PM: ${{ matrix.pm.name }}
E2E_TEST_PM_VERSION: ${{ matrix.pm.version }}
E2E_TEST_FILTER: ${{ matrix.filter }}
CI_OS: ${{ runner.os }}
GITHUB_TOKEN: ${{ github.token }} # Needed for begit to clone the repo in the e2e tests for solid-start
- name: Upload Logs
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: ${{ !cancelled() && steps.changes.outputs.everything_but_markdown == 'true' }}
with:
name: ${{ format('e2e-logs-{0}-{1}-{2}-{3}', matrix.pm.label, matrix.os.description, matrix.experimental && 'experimental' || 'normal', matrix.filter) }}
path: packages/create-cloudflare/.e2e-logs${{matrix.experimental == 'true' && '-experimental' || ''}}/${{ matrix.pm.name }}/${{ matrix.filter }}
include-hidden-files: true
- name: Upload Turbo Summary
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: ${{ !cancelled() && steps.changes.outputs.everything_but_markdown == 'true' }}
with:
name: ${{ format('turbo-runs-{0}-{1}-{2}-{3}', matrix.pm.label, matrix.os.description, matrix.experimental && 'experimental' || 'normal', matrix.filter) }}
path: .turbo/runs
include-hidden-files: true
frameworks-e2e:
# Runs the C3 frameworks E2E tests only in the merge queue, on the release branch, if create-cloudflare has changed, or when explicitly requested via label.
#
# Frameworks that delegate install to their own generator (e.g. `hono`)
# don't route through C3's `ERR_PNPM_IGNORED_BUILDS` recovery path; those
# tests opt out of pnpm 11 via `unsupportedPmRanges` in the test config.
timeout-minutes: 45
concurrency:
group: ${{ github.workflow }}-frameworks${{ matrix.experimental && '-experimental' || '' }}-${{ github.ref }}-${{ matrix.os.name }}-${{ matrix.pm.name }}-${{ matrix.pm.version }}
cancel-in-progress: ${{ github.head_ref != 'changeset-release/main' }}
name: ${{ format('C3 E2E ({0}, {1}{2}) - frameworks', matrix.pm.label, matrix.os.description, matrix.experimental && ', experimental' || '') }}
strategy:
fail-fast: false
matrix:
experimental: [false]
os:
- { name: ubuntu-latest, description: Linux }
pm:
- { name: pnpm, version: "10.33.0", label: "pnpm" }
- { name: pnpm, version: "11.5.1", label: "pnpm@11.5.1" }
- { name: npm, version: "0.0.0", label: "npm" }
include:
# Experimental stays on the default pnpm to preserve historical
# required-check names; pnpm 11 coverage comes from the base entry.
- os: { name: ubuntu-latest, description: Linux }
pm: { name: pnpm, version: "10.33.0", label: "pnpm" }
experimental: true
runs-on: ${{ matrix.os.name }}
steps:
- name: Checkout Repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
persist-credentials: false
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: changes
with:
filters: |
everything_but_markdown:
- '!**/*.md'
framework_deps:
- 'packages/create-cloudflare/**'
- name: Check if frameworks tests should run
id: check-frameworks
shell: bash
env:
GH_TOKEN: ${{ github.token }}
EVENT_NAME: ${{ github.event_name }}
HEAD_REF: ${{ github.head_ref }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO: ${{ github.repository }}
FRAMEWORK_DEPS_CHANGED: ${{ steps.changes.outputs.framework_deps }}
run: |
if [ "$EVENT_NAME" = "merge_group" ] || \
[ "$HEAD_REF" = "changeset-release/main" ] || \
[ "$FRAMEWORK_DEPS_CHANGED" = "true" ]; then
echo "run_frameworks=true" >> "$GITHUB_OUTPUT"
elif [ "$EVENT_NAME" = "pull_request" ]; then
HAS_LABEL=$(gh pr view "$PR_NUMBER" \
--repo "$REPO" --json labels \
--jq '[.labels[].name] | any(. == "run-c3-frameworks-tests")')
echo "run_frameworks=${HAS_LABEL}" >> "$GITHUB_OUTPUT"
else
echo "run_frameworks=false" >> "$GITHUB_OUTPUT"
fi
- name: Install Dependencies
if: steps.check-frameworks.outputs.run_frameworks == 'true' && steps.changes.outputs.everything_but_markdown == 'true'
uses: ./.github/actions/install-dependencies
with:
turbo-api: ${{ secrets.TURBO_API }}
turbo-team: ${{ secrets.TURBO_TEAM }}
turbo-token: ${{ secrets.TURBO_TOKEN }}
turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
- name: Install Python uv
if: steps.check-frameworks.outputs.run_frameworks == 'true' && steps.changes.outputs.everything_but_markdown == 'true'
uses: ./.github/actions/install-python-uv
- name: Configure Git
if: steps.check-frameworks.outputs.run_frameworks == 'true' && steps.changes.outputs.everything_but_markdown == 'true'
shell: bash
run: |
git config --global user.email wrangler@cloudflare.com
git config --global user.name 'Wrangler Tester'
# See the equivalent step in the `e2e` job above for context.
- name: Prepare workspace for matrix pnpm install
if: matrix.pm.name == 'pnpm' && steps.check-frameworks.outputs.run_frameworks == 'true' && steps.changes.outputs.everything_but_markdown == 'true'
shell: bash
run: |
mkdir -p .ci-pnpm-setup-stub
printf '{"name":"ci-stub","private":true}\n' > .ci-pnpm-setup-stub/package.json
node -e "
const fs = require('node:fs');
const pkg = JSON.parse(fs.readFileSync('package.json','utf8'));
if (pkg.engines && pkg.engines.pnpm) pkg.engines.pnpm = '*';
fs.writeFileSync('package.json', JSON.stringify(pkg, null, '\t') + '\n');
"
- name: Install matrix pnpm version for scaffolded installs
if: matrix.pm.name == 'pnpm' && steps.check-frameworks.outputs.run_frameworks == 'true' && steps.changes.outputs.everything_but_markdown == 'true'
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
with:
version: ${{ matrix.pm.version }}
package_json_file: .ci-pnpm-setup-stub/package.json
dest: ~/matrix-pnpm
- name: Verify pnpm on PATH matches matrix version
if: matrix.pm.name == 'pnpm' && steps.check-frameworks.outputs.run_frameworks == 'true' && steps.changes.outputs.everything_but_markdown == 'true'
shell: bash
run: |
got="$(pnpm --version)"
want="${{ matrix.pm.version }}"
echo "pnpm on PATH: $got (expected $want)"
[ "$got" = "$want" ] || { echo "ERROR: PATH pnpm is $got, not $want — scaffolded installs would silently run the wrong version."; exit 1; }
- id: run-e2e
if: steps.check-frameworks.outputs.run_frameworks == 'true' && steps.changes.outputs.everything_but_markdown == 'true'
shell: bash
run: pnpm run test:e2e:c3
env:
NODE_VERSION: ${{ env.NODE_VERSION }}
E2E_EXPERIMENTAL: ${{ matrix.experimental }}
E2E_TEST_PM: ${{ matrix.pm.name }}
E2E_TEST_PM_VERSION: ${{ matrix.pm.version }}
E2E_TEST_FILTER: frameworks
CI_OS: ${{ runner.os }}
GITHUB_TOKEN: ${{ github.token }}
- name: Upload Logs
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: ${{ !cancelled() && steps.check-frameworks.outputs.run_frameworks == 'true' && steps.changes.outputs.everything_but_markdown == 'true' }}
with:
name: ${{ format('e2e-logs-{0}-{1}-{2}-frameworks', matrix.pm.label, matrix.os.description, matrix.experimental && 'experimental' || 'normal') }}
path: packages/create-cloudflare/.e2e-logs${{matrix.experimental == 'true' && '-experimental' || ''}}/${{ matrix.pm.name }}/frameworks
include-hidden-files: true
- name: Upload Turbo Summary
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: ${{ !cancelled() && steps.check-frameworks.outputs.run_frameworks == 'true' && steps.changes.outputs.everything_but_markdown == 'true' }}
with:
name: ${{ format('turbo-runs-{0}-{1}-{2}-frameworks', matrix.pm.label, matrix.os.description, matrix.experimental && 'experimental' || 'normal') }}
path: .turbo/runs
include-hidden-files: true
+92
View File
@@ -0,0 +1,92 @@
name: Changeset Review
on:
pull_request:
paths:
- ".changeset/*.md"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.head_ref != 'changeset-release/main' }}
permissions:
contents: read
pull-requests: write
issues: write
id-token: write
jobs:
review-changesets:
runs-on: ubuntu-latest
if: github.event.pull_request.head.repo.full_name == github.repository
steps:
- name: Checkout changesets
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
persist-credentials: false
sparse-checkout: |
.changeset
.github/opencode.json
- name: Install OpenCode
# pin OpenCode to 1.4.6 version as newer versions are causing ProviderInitError issues
run: |
npm install -g opencode-ai@1.4.6
cp .github/opencode.json ./opencode.json
- name: Get changed changeset files
id: changed-changesets
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
with:
files: |
.changeset/*.md
files_ignore: |
.changeset/README.md
# Recover deleted files so the AI can read them (needed for Version Packages PRs)
recover_deleted_files: ${{ github.event.pull_request.title == 'Version Packages' }}
- name: Review Changesets with OpenCode
id: opencode-review
# Run for Version Packages PRs (which delete changesets) or regular PRs with new changesets
if: github.event.pull_request.title == 'Version Packages' || steps.changed-changesets.outputs.added_files_count > 0
env:
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CF_AI_GATEWAY_ACCOUNT_ID }}
CLOUDFLARE_GATEWAY_ID: ${{ secrets.CF_AI_GATEWAY_NAME }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CF_AI_GATEWAY_TOKEN }}
DELETED_FILES: ${{ steps.changed-changesets.outputs.deleted_files }}
ADDED_FILES: ${{ steps.changed-changesets.outputs.added_files }}
run: |
opencode --model "cloudflare-ai-gateway/workers-ai/@cf/moonshotai/kimi-k2.6" run --print-logs \
"Review the changeset files in this PR.
For \"Version Packages\" PRs, review: ${DELETED_FILES}
For regular PRs, review: ${ADDED_FILES}
Read \`.changeset/README.md\` for guidelines, then validate:
1. **Version Type**: Accept the author's choice of patch/minor unless clearly incorrect
2. **Changelog Quality**: Meaningful descriptions (examples encouraged but not required for features)
3. **Markdown Headers**: No h1/h2/h3 headers (breaks changelog formatting)
4. **Analytics**: If the change collects more analytics, it should be a minor even though there is no user-visible change
5. **Dependabot**: Do not validate dependency update changesets for create-cloudflare
6. **Experimental features**: Changesets for experimental features should include note on how users can opt in.
If all changesets pass, just output \"✅ All changesets look good\" - no need for a detailed checklist.
Do not review other files, only the changesets. This is specifically a changeset review action.
If there are issues, output \"⚠️ Issues found\" followed by the specific problems.
Write your review to changeset-review.md."
- name: Post review comment
if: steps.opencode-review.outcome == 'success'
uses: marocchino/sticky-pull-request-comment@d4d6b0936434b21bc8345ad45a440c5f7d2c40ff # v3.0.3
with:
header: changeset-review
path: changeset-review.md
- name: Skip notice
if: github.event.pull_request.title != 'Version Packages' && steps.changed-changesets.outputs.added_files_count == 0
run: |
echo "No new changesets to review (only minor edits to pre-existing changesets detected)"
+108
View File
@@ -0,0 +1,108 @@
name: Handle Changesets
on:
push:
branches:
- main
permissions:
id-token: write
contents: read
# note: no write permissions are needed since the workflow uses GH_ACCESS_TOKEN instead of GITHUB_TOKEN
env:
# This workflow only builds and publishes — it never runs Playwright tests.
# Skip the browser download to avoid the postinstall script hanging in CI.
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1"
jobs:
release:
if: ${{ github.repository_owner == 'cloudflare' }}
name: Handle Changesets
runs-on: macos-latest-large
steps:
- name: Checkout Repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
# See https://github.com/changesets/action/issues/187
token: ${{ secrets.GH_ACCESS_TOKEN }}
persist-credentials: true
# Pull in more history to cover the changeset commits
fetch-depth: 500
- name: Install Dependencies
# Defense in depth: do not pass Turbo remote cache credentials and
# disable the pnpm store cache so release builds always resolve
# packages from the registry and rebuild every task from source,
# rather than restoring from a (potentially poisoned) cache.
uses: ./.github/actions/install-dependencies
with:
# Pinned to 24.18.0 (rather than floating `24`) until it's safe to
# float back: Node 24.17.0 regressed node-fetch@2 keep-alive responses
# with ERR_STREAM_PREMATURE_CLOSE (nodejs/node#63989, fixed in 24.18.0).
node-version: 24.18.0
disable-cache: "true"
- name: Check npm version
run: node -r esbuild-register tools/deployments/check-npm-version.ts
- name: Check the changesets
run: node -r esbuild-register tools/deployments/validate-changesets.ts
- name: Build all packages
run: pnpm run build
env:
CI_OS: ${{ runner.os }}
SOURCEMAPS: "false"
ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }}
ALGOLIA_PUBLIC_KEY: ${{ secrets.ALGOLIA_PUBLIC_KEY }}
SENTRY_DSN: "https://9edbb8417b284aa2bbead9b4c318918b@sentry10.cfdata.org/583"
NODE_ENV: "production"
# This is the "production" key for sparrow analytics.
# Include this here because this step will rebuild Wrangler and needs to have this available
SPARROW_SOURCE_KEY: "50598e014ed44c739ec8074fdc16057c"
- name: Create Version PR or Publish to NPM
id: changesets
uses: changesets/action@a45c4d594aa4e2c509dc14a9f2b3b67ba3780d0d # v1.9.0
with:
version: node .github/changeset-version.js
publish: pnpm exec changeset publish
env:
GITHUB_TOKEN: ${{ secrets.GH_ACCESS_TOKEN }}
# Switch to Node 22 for non-npm package deployments because quick-edit
# builds native modules (tree-sitter) that are not compatible with
# Node 24's C++20 requirement for V8 headers.
- name: Switch to Node 22 for non-npm package deployments
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
# Pinned to 22.23.1 (rather than floating `22`) until it's safe to
# float back: Node 22.23.0 regressed node-fetch@2 keep-alive responses
# with ERR_STREAM_PREMATURE_CLOSE (nodejs/node#63989, fixed in 22.23.1).
node-version: 22.23.1
- name: Deploy non-NPM Packages
id: deploy
run: |
node -r esbuild-register tools/deployments/deploy-non-npm-packages.ts
echo "status=$(cat deployment-status.json)" >> $GITHUB_OUTPUT;
env:
PUBLISHED_PACKAGES: ${{ steps.changesets.outputs.publishedPackages }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
WORKERS_NEW_CLOUDFLARE_ACCOUNT_ID: ${{ secrets.WORKERS_NEW_CLOUDFLARE_ACCOUNT_ID }}
WORKERS_NEW_CLOUDFLARE_API_TOKEN: ${{ secrets.WORKERS_NEW_CLOUDFLARE_API_TOKEN }}
WORKERS_DEPLOY_AND_CONFIG_CLOUDFLARE_API_TOKEN: ${{ secrets.WORKERS_DEPLOY_AND_CONFIG_CLOUDFLARE_API_TOKEN }}
WORKERS_SHARED_SENTRY_ACCESS_ID: ${{ secrets.WORKERS_SHARED_SENTRY_ACCESS_ID }}
WORKERS_SHARED_SENTRY_ACCESS_SECRET: ${{ secrets.WORKERS_SHARED_SENTRY_ACCESS_SECRET }}
WORKERS_SHARED_SENTRY_AUTH_TOKEN: ${{ secrets.WORKERS_SHARED_SENTRY_AUTH_TOKEN }}
- name: Send Alert
if: always()
run: node -r esbuild-register tools/deployments/alert-on-error.ts
env:
PUBLISH_STATUS: ${{ steps.changesets.outcome }}
HAS_CHANGESETS: ${{ steps.changesets.outputs.hasChangesets }}
DEPLOYMENT_STATUS: ${{ steps.deploy.outputs.status }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
TOKEN: ${{ secrets.STATUS_BOT_SECRET }}
+54
View File
@@ -0,0 +1,54 @@
name: "Code Owners"
# Re-evaluate when PRs are opened/updated.
# When reviews are submitted/dismissed, the separate rerun-codeowners.yml workflow
# re-runs this check (rather than creating a second check context).
# Using pull_request_target (not pull_request) so the workflow has access to secrets
# for fork PRs. This is safe because:
# - The checkout is the BASE branch (ownership rules come from the protected branch)
# - PR head commits are fetched as git objects only (never checked out or executed)
# - The action only reads config files and calls the GitHub API
on:
pull_request_target: # zizmor: ignore[dangerous-triggers] checks base branch ownership rules and fetches PR head for diff computation without executing PR code
types: [opened, reopened, synchronize, ready_for_review, labeled, unlabeled]
concurrency:
group: codeowners-${{ github.event.pull_request.number }}
cancel-in-progress: ${{ github.head_ref != 'changeset-release/main' }}
permissions:
contents: read
issues: write
pull-requests: write
jobs:
codeowners:
name: "Run Codeowners Plus"
runs-on: ubuntu-latest
# Each step is skipped when:
# - the PR head is the changeset-release branch (auto-generated changeset PRs), or
# - the PR base is not `main` (PRs against feature branches don't need formal review).
# Note: if we ever introduce a maintenance branch (e.g. a long-lived release branch),
# it would need to be added to the base branch allowlist below.
steps:
- name: "Checkout Base Branch"
if: github.event.pull_request.head.ref != 'changeset-release/main' && github.event.pull_request.base.ref == 'main'
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
persist-credentials: false
- name: "Fetch PR Head (for diff computation)"
if: github.event.pull_request.head.ref != 'changeset-release/main' && github.event.pull_request.base.ref == 'main'
run: git fetch origin +refs/pull/${{ github.event.pull_request.number }}/head
env:
GITHUB_TOKEN: "${{ secrets.CODEOWNERS_GITHUB_PAT }}"
- name: "Codeowners Plus"
if: github.event.pull_request.head.ref != 'changeset-release/main' && github.event.pull_request.base.ref == 'main'
uses: multimediallc/codeowners-plus@ff02aa993a92e8efe01642916d0877beb9439e9f # v1.9.0
with:
github-token: "${{ secrets.CODEOWNERS_GITHUB_PAT }}"
pr: "${{ github.event.pull_request.number }}"
verbose: true
quiet: ${{ github.event.pull_request.draft }}
+141
View File
@@ -0,0 +1,141 @@
name: "Dependabot - auto-merge workerd updates"
# workerd ships a release every weekday, so the workerd-and-workers-types
# Dependabot group produces a steady stream of mechanical PRs that bump
# `workerd`, `@cloudflare/workers-types`, and miniflare's pinned version in
# lockstep (see .github/dependabot.yml). When CI is green these PRs require
# no human review, so we enable GitHub auto-merge on them — required status
# checks remain the gate, and a failing build still parks the PR for a human.
#
# Security model: this workflow effectively bypasses the human-review
# requirement on PRs whose head branch matches the Dependabot naming pattern,
# so we have to be paranoid about exactly what we're auto-merging. Before
# enabling auto-merge we verify that the PR contains exactly the two commits
# we expect (one from Dependabot, one from `miniflare-dependabot-versioning-prs.yml`)
# and that nothing outside the expected fileset has been touched. If any
# subsequent push violates those invariants we actively *disable* auto-merge,
# so a maintainer pushing a follow-up commit cancels rather than rides the
# auto-merge.
#
# DO NOT add `actions/checkout` to this workflow — `pull_request_target`
# grants write-scoped tokens, and checking out PR-controlled code with
# those tokens is the standard pwn vector.
on:
pull_request_target: # zizmor: ignore[dangerous-triggers] intentionally privileged metadata-only workflow; it never checks out or executes PR code
types: [opened, reopened, synchronize, ready_for_review]
permissions:
contents: write
pull-requests: write
jobs:
enable-auto-merge:
if: github.event.pull_request.user.login == 'dependabot[bot]'
runs-on: ubuntu-slim
steps:
- name: Fetch Dependabot metadata
id: meta
uses: dependabot/fetch-metadata@d7267f607e9d3fb96fc2fbe83e0af444713e90b7 # v2.3.0
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Verify PR matches expected workerd-bump shape
id: verify
if: steps.meta.outputs.dependency-group == 'workerd-and-workers-types'
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO: ${{ github.repository }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
# Pull commits and changed files via the GitHub API. `--repo` is
# required because this workflow runs without `actions/checkout`,
# so `gh` has no git remote to infer the repo from.
commits_json=$(gh pr view --repo "$REPO" "$PR_NUMBER" --json commits)
files_json=$(gh pr view --repo "$REPO" "$PR_NUMBER" --json files)
fail() {
echo "verified=false" >> "$GITHUB_OUTPUT"
echo "reason=$1" >> "$GITHUB_OUTPUT"
echo "::warning::Refusing to enable auto-merge: $1"
exit 0
}
# --- Commit shape ---------------------------------------------------
# Expected: exactly two commits.
# 1. dependabot[bot] author, with a verified GitHub signature.
# 2. The changeset commit pushed by miniflare-dependabot-versioning-prs.yml,
# authored as `Wrangler automated PR updater <wrangler@cloudflare.com>`.
#
# We can't require a signature on the second commit (it's pushed via
# GH_ACCESS_TOKEN, which doesn't sign), so we lean on path/content
# checks below to constrain what that commit can do.
commit_count=$(echo "$commits_json" | jq '.commits | length')
if [ "$commit_count" -ne 2 ]; then
fail "expected exactly 2 commits, found $commit_count"
fi
first_author=$(echo "$commits_json" | jq -r '.commits[0].authors[0].login')
first_oid=$(echo "$commits_json" | jq -r '.commits[0].oid')
if [ "$first_author" != "dependabot[bot]" ]; then
fail "first commit author is '$first_author', expected 'dependabot[bot]'"
fi
# `gh pr view --json commits` doesn't expose signature info, so look
# it up via the REST commit endpoint.
first_verified=$(gh api "repos/$REPO/commits/$first_oid" --jq '.commit.verification.verified')
if [ "$first_verified" != "true" ]; then
fail "first commit (Dependabot) does not have a verified signature"
fi
second_email=$(echo "$commits_json" | jq -r '.commits[1].authors[0].email // ""')
second_message=$(echo "$commits_json" | jq -r '.commits[1].messageHeadline // ""')
if [ "$second_email" != "wrangler@cloudflare.com" ]; then
fail "second commit author email is '$second_email', expected 'wrangler@cloudflare.com'"
fi
if ! echo "$second_message" | grep -qE '^Update dependencies of '; then
fail "second commit message '$second_message' does not match expected changeset commit shape"
fi
# --- Changed files allowlist ---------------------------------------
# The only paths a workerd bump should touch:
# - .changeset/dependabot-update-*.md (added by the changeset job)
# - packages/*/package.json (workerd, workers-types pins)
# - pnpm-lock.yaml
# - pnpm-workspace.yaml (catalog entries)
allowed='^(\.changeset/dependabot-update-.*\.md|packages/[^/]+/package\.json|pnpm-lock\.yaml|pnpm-workspace\.yaml)$'
unexpected=$(echo "$files_json" | jq -r '.files[].path' | grep -vE "$allowed" || true)
if [ -n "$unexpected" ]; then
fail "PR touches unexpected files:$(echo "$unexpected" | sed 's/^/ /' | tr '\n' ',')"
fi
# Make sure the changeset is actually present — the changeset job
# may not have run yet, in which case we bail and wait for the
# `synchronize` event from its push.
if ! echo "$files_json" | jq -e '.files[] | select(.path | startswith(".changeset/dependabot-update-"))' > /dev/null; then
fail "changeset file not yet present; waiting for changeset job to push it"
fi
echo "verified=true" >> "$GITHUB_OUTPUT"
- name: Enable auto-merge
if: steps.meta.outputs.dependency-group == 'workerd-and-workers-types' && steps.verify.outputs.verified == 'true'
run: gh pr merge --auto --squash "$PR_URL"
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Disable auto-merge if verification failed
# If a previous run enabled auto-merge but a later push broke the
# invariants, actively cancel auto-merge so the bad commit can't ride.
# `gh pr merge --disable-auto` is a no-op (and exits 0) if auto-merge
# was never enabled, so this is safe to always run on the failure path.
if: always() && steps.meta.outputs.dependency-group == 'workerd-and-workers-types' && steps.verify.outputs.verified != 'true'
run: gh pr merge --disable-auto "$PR_URL" || true
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+90
View File
@@ -0,0 +1,90 @@
name: Deploy Previews
# This workflow is designed to deploy a "preview" version of a project based on PR labels.
# Triggers:
# - update to a PR that has one of the `preview:...` labels
#
# Actions:
# - deploy the matching project to Cloudflare.
#
# PR Label | Project
# ---------------------------------------------------------
# preview:chrome-devtools-patches | packages/chrome-devtools-patches
# preview:quick-edit | packages/quick-edit
#
# Note: this workflow does not run tests against these packages, only deploys previews.
on:
pull_request:
types: [synchronize, opened, reopened, labeled, unlabeled]
permissions:
contents: read
# pull-request:write permission needed so that the workflow can comment on PRs
pull-requests: write
jobs:
deploy-projects:
# Only run this on PRs that are for the "cloudflare" org and not "from" `main`
# - non-Cloudflare PRs will not have the secrets needed
# - PRs "from" main would accidentally do a production deployment
if: github.repository_owner == 'cloudflare' && github.head_ref != 'main' && (contains(github.event.*.labels.*.name, 'preview:chrome-devtools-patches') || contains(github.event.*.labels.*.name, 'preview:quick-edit'))
timeout-minutes: 60
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-app-previews
runs-on: macos-latest-large
steps:
- name: Checkout Repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 1
persist-credentials: false
- name: Install Dependencies
uses: ./.github/actions/install-dependencies
- name: Build tools and libraries
run: pnpm run build
env:
NODE_ENV: "production"
CI_OS: ${{ runner.os }}
- name: Deploy Wrangler DevTools preview
if: contains(github.event.*.labels.*.name, 'preview:chrome-devtools-patches')
run: |
output=$(pnpm --filter @cloudflare/chrome-devtools-patches run deploy:preview)
echo "Command output: $output"
echo "Extracting deployed URL from command output"
url=$(echo "$output" | sed -nE "s/.*Version Preview URL: ([^[:space:]]+).*/\1/p")
echo "Extracted URL: $url"
echo "VITE_DEVTOOLS_PREVIEW_URL=$url" >> $GITHUB_ENV
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
- name: Build and Deploy Quick Edit preview
if: contains(github.event.*.labels.*.name, 'preview:quick-edit')
run: pnpm --filter quick-edit run preview
env:
DEBIAN_FRONTEND: noninteractive
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
NODE_OPTIONS: "--max_old_space_size=30000"
- name: "Comment on PR with Devtools Link"
if: contains(github.event.*.labels.*.name, 'preview:chrome-devtools-patches')
uses: marocchino/sticky-pull-request-comment@d4d6b0936434b21bc8345ad45a440c5f7d2c40ff # v3.0.3
with:
header: chrome-devtools-preview
message: |
The Wrangler DevTools preview is now live. You can access it directly at: ${{ env.VITE_DEVTOOLS_PREVIEW_URL }}/js_app
In order to test the DevTools preview in `wrangler`:
1. `npx wrangler dev`.
2. Hit `d` to open the DevTools in a fresh browser window.
3. Paste the DevTools preview URL into the address bar (keeping all existing query parameters), e.g:
```
- https://devtools.devprod.cloudflare.dev/js_app?theme=systemPreferred&ws=127.0.0.1%3A9229%2Fws&domain=tester&debugger=true
+ ${{ env.VITE_DEVTOOLS_PREVIEW_URL }}/js_app?theme=systemPreferred&ws=127.0.0.1%3A9229%2Fws&domain=tester&debugger=true
```
@@ -0,0 +1,69 @@
name: Local Explorer UI E2E
on:
merge_group:
pull_request:
permissions:
contents: read
env:
# We run `playwright install` manually instead
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1"
jobs:
test:
name: Local Explorer UI E2E
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.head_ref != 'changeset-release/main' }}
timeout-minutes: 30
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
persist-credentials: false
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: changes
with:
filters: |
relevant:
- 'packages/local-explorer-ui/**'
- 'packages/miniflare/src/workers/local-explorer/**'
- 'fixtures/worker-with-resources/**'
- 'fixtures/shared/**'
- name: Install Dependencies
if: steps.changes.outputs.relevant == 'true'
uses: ./.github/actions/install-dependencies
with:
turbo-api: ${{ secrets.TURBO_API }}
turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
turbo-team: ${{ secrets.TURBO_TEAM }}
turbo-token: ${{ secrets.TURBO_TOKEN }}
- name: Install Playwright
if: steps.changes.outputs.relevant == 'true'
run: pnpm --filter @cloudflare/local-explorer-ui playwright:install
- name: Build wrangler
if: steps.changes.outputs.relevant == 'true'
run: pnpm turbo build --filter wrangler
- name: Run Local Explorer UI E2E tests
if: steps.changes.outputs.relevant == 'true'
run: pnpm test:e2e -F @cloudflare/local-explorer-ui --log-order=stream
timeout-minutes: 30
env:
CI_OS: Linux
NODE_OPTIONS: "--max_old_space_size=8192"
- name: Upload turbo logs
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: turbo-runs
path: .turbo/runs
+33
View File
@@ -0,0 +1,33 @@
# This workflow cleans up any leftover projects created by e2e runs.
name: E2E Project Cleanup
on:
workflow_dispatch:
schedule:
- cron: "0 */2 * * *" # Run every 2 hours
permissions:
contents: read
jobs:
cleanup:
timeout-minutes: 30
name: "Cleanup Test Projects"
if: ${{ github.repository_owner == 'cloudflare' }}
runs-on: ubuntu-slim
steps:
- name: Checkout Repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 1
persist-credentials: false
- name: Install Dependencies
uses: ./.github/actions/install-dependencies
- name: Cleanup E2E test projects
run: node -r esbuild-register tools/e2e/e2eCleanup.ts
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.TEST_CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.TEST_CLOUDFLARE_ACCOUNT_ID }}
+73
View File
@@ -0,0 +1,73 @@
name: Vite Plugin E2E
on:
merge_group:
pull_request:
permissions:
contents: read
pull-requests: read
jobs:
e2e-vite-plugin-test:
name: ${{ format('Vite Plugin E2E ({0})', matrix.description) }}
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.os }}
cancel-in-progress: ${{ github.head_ref != 'changeset-release/main' }}
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
include:
- os: macos-latest
description: macOS
- os: windows-latest
description: Windows
- os: ubuntu-latest
description: Linux
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
persist-credentials: false
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: changes
with:
filters: |
everything_but_markdown:
- '!**/*.md'
- name: Check if remote tests should run
id: check-remote
uses: ./.github/actions/check-remote-tests
- name: Install Dependencies
if: steps.changes.outputs.everything_but_markdown == 'true'
uses: ./.github/actions/install-dependencies
with:
turbo-api: ${{ secrets.TURBO_API }}
turbo-team: ${{ secrets.TURBO_TEAM }}
turbo-token: ${{ secrets.TURBO_TOKEN }}
turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
- name: Run Vite plugin E2E tests
if: steps.changes.outputs.everything_but_markdown == 'true'
run: pnpm test:e2e -F @cloudflare/vite-plugin --log-order=stream
timeout-minutes: 20
env:
NODE_DEBUG: "vite-plugin:test"
# The remote-binding tests need to connect to Cloudflare
CLOUDFLARE_API_TOKEN: ${{ steps.check-remote.outputs.run-remote == 'true' && secrets.TEST_CLOUDFLARE_API_TOKEN || '' }}
CLOUDFLARE_ACCOUNT_ID: ${{ steps.check-remote.outputs.run-remote == 'true' && secrets.TEST_CLOUDFLARE_ACCOUNT_ID || '' }}
NODE_OPTIONS: "--max_old_space_size=8192"
CI_OS: ${{ matrix.os }}
- name: Upload turbo logs
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: turbo-runs-${{ matrix.os }}
path: .turbo/runs
+88
View File
@@ -0,0 +1,88 @@
name: Wrangler E2E
on:
merge_group:
pull_request:
permissions:
contents: read
pull-requests: read
jobs:
e2e-wrangler-test:
name: ${{ format('Wrangler E2E ({0}, shard {1}/4)', matrix.description, matrix.shard) }}
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.os }}-${{ matrix.shard }}
cancel-in-progress: ${{ github.head_ref != 'changeset-release/main' }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
os: [macos-latest, windows-latest, ubuntu-latest]
shard: [1, 2, 3, 4]
include:
- os: macos-latest
description: macOS
- os: windows-latest
description: Windows
- os: ubuntu-latest
description: Linux
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
persist-credentials: false
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: changes
with:
filters: |
everything_but_markdown:
- '!**/*.md'
- name: Check if remote tests should run
id: check-remote
uses: ./.github/actions/check-remote-tests
- name: Install Dependencies
if: steps.changes.outputs.everything_but_markdown == 'true'
uses: ./.github/actions/install-dependencies
with:
turbo-api: ${{ secrets.TURBO_API }}
turbo-team: ${{ secrets.TURBO_TEAM }}
turbo-token: ${{ secrets.TURBO_TOKEN }}
turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
- name: Run Wrangler E2E tests
if: steps.changes.outputs.everything_but_markdown == 'true'
run: pnpm run test:e2e:wrangler
env:
CLOUDFLARE_API_TOKEN: ${{ steps.check-remote.outputs.run-remote == 'true' && secrets.TEST_CLOUDFLARE_API_TOKEN || '' }}
CLOUDFLARE_ACCOUNT_ID: ${{ steps.check-remote.outputs.run-remote == 'true' && secrets.TEST_CLOUDFLARE_ACCOUNT_ID || '' }}
HYPERDRIVE_DATABASE_URL: ${{ secrets.TEST_HYPERDRIVE_DATABASE_URL}}
HYPERDRIVE_MYSQL_DATABASE_URL: ${{ secrets.TEST_HYPERDRIVE_MYSQL_DATABASE_URL}}
NODE_OPTIONS: "--max_old_space_size=8192"
WRANGLER_LOG_PATH: ${{ runner.temp }}/wrangler-debug-logs/
TEST_REPORT_PATH: ${{ runner.temp }}/test-report/index.html
CI_OS: ${{ matrix.os }}
E2E_SHARD: ${{ matrix.shard }}
E2E_SHARD_COUNT: "4"
- name: Run getPlatformProxy() remote-bindings e2e tests
if: steps.changes.outputs.everything_but_markdown == 'true' && matrix.shard == 1
run: pnpm run test:e2e -F @fixture/get-platform-proxy-remote-bindings
env:
TEST_CLOUDFLARE_API_TOKEN: ${{ steps.check-remote.outputs.run-remote == 'true' && secrets.TEST_CLOUDFLARE_API_TOKEN || '' }}
TEST_CLOUDFLARE_ACCOUNT_ID: ${{ steps.check-remote.outputs.run-remote == 'true' && secrets.TEST_CLOUDFLARE_ACCOUNT_ID || '' }}
NODE_OPTIONS: "--max_old_space_size=8192"
CI_OS: ${{ matrix.os }}
- name: Upload turbo logs
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: turbo-runs-${{ matrix.os }}-shard-${{ matrix.shard }}
path: .turbo/runs
+82
View File
@@ -0,0 +1,82 @@
name: Release a hotfix
on:
workflow_dispatch:
inputs:
pr-number:
description: "Which PR should be released?"
required: true
confirm:
description: "Confirm that the PR is up to date with the last published release, with the specific additional changes that should be released as part of this hotfix and the relevant package patch versions bumped"
type: boolean
required: true
label:
description: "Which dist-tag should this release have?"
type: string
default: hotfix
required: true
permissions:
contents: read
id-token: write
jobs:
hotfix-release:
name: Hotfix Release
if: ${{ inputs.confirm == true }}
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
persist-credentials: false
- name: Check user for team affiliation
uses: tspascoal/get-user-teams-membership@ba78054988f58bea69b7c6136d563236f8ed2fc0 # v2
id: teamAffiliation
with:
GITHUB_TOKEN: ${{ secrets.READ_ONLY_ORG_GITHUB_TOKEN }}
username: ${{ github.actor }}
team: wrangler
- name: Stop workflow if user is not a wrangler team member
if: ${{ steps.teamAffiliation.outputs.isTeamMember == false }}
run: |
echo "You must be on the "wrangler" team to trigger this job."
exit 1
- name: "Checkout PR"
run: |
if ! [[ "$PR_NUMBER" =~ ^[0-9]+$ ]]; then
echo "::error::PR number must be numeric"
exit 1
fi
gh pr checkout "$PR_NUMBER"
env:
GH_TOKEN: ${{ secrets.GH_ACCESS_TOKEN }}
PR_NUMBER: ${{ inputs.pr-number }}
- name: Install Dependencies
# Defense in depth: do not pass Turbo remote cache credentials and
# disable the pnpm store cache so release builds always resolve
# packages from the registry and rebuild every task from source,
# rather than restoring from a (potentially poisoned) cache.
uses: ./.github/actions/install-dependencies
with:
disable-cache: "true"
- name: Build all packages
run: pnpm run build
env:
CI_OS: ${{ runner.os }}
- name: Publish packages
run: |
if ! [[ "$NPM_TAG" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*$ ]]; then
echo "::error::npm dist-tag contains unsupported characters"
exit 1
fi
pnpm publish -r --tag "$NPM_TAG" --filter wrangler --filter miniflare --filter create-cloudflare
env:
NPM_TAG: ${{ inputs.label }}
+41
View File
@@ -0,0 +1,41 @@
name: Add issues to DevProd project
on:
issues:
types: [opened, transferred]
permissions:
# issues:write permission needed so that the workflow can manage issues
issues: write
jobs:
add-to-project:
name: Add issue to project
runs-on: ubuntu-slim
steps:
- uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2
with:
project-url: https://github.com/orgs/cloudflare/projects/1
github-token: ${{ secrets.GH_ACCESS_TOKEN }}
labeled: bug, enhancement
label-operator: OR
- uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2
with:
project-url: https://github.com/orgs/cloudflare/projects/2
github-token: ${{ secrets.GH_ACCESS_TOKEN }}
labeled: product:pages
- uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2
with:
project-url: https://github.com/orgs/cloudflare/projects/6
github-token: ${{ secrets.GH_ACCESS_TOKEN }}
labeled: product:d1
- uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2
with:
project-url: https://github.com/orgs/cloudflare/projects/12
github-token: ${{ secrets.GH_ACCESS_TOKEN }}
labeled: product:c3
- uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2
with:
project-url: https://github.com/orgs/cloudflare/projects/8
github-token: ${{ secrets.GH_ACCESS_TOKEN }}
labeled: product:queues
@@ -0,0 +1,51 @@
name: "Miniflare - Generate changesets for dependabot PRs"
on:
pull_request_target: # zizmor: ignore[dangerous-triggers] dependabot-only job requires write access to push generated changesets; checkout v6 persists credentials under RUNNER_TEMP
paths:
- "packages/miniflare/package.json"
- "pnpm-workspace.yaml"
permissions:
# content:write permission needed to update add changesets to dependabot PRs
# (see tools/dependabot/generate-dependabot-pr-changesets.ts)
contents: write
jobs:
generate-changeset:
runs-on: ubuntu-slim
if: |
github.actor == 'dependabot[bot]' &&
github.event.pull_request.user.login == 'dependabot[bot]' &&
github.event.pull_request.head.repo.full_name == github.repository
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 2
persist-credentials: true
ref: ${{ github.head_ref }}
token: ${{ secrets.GH_ACCESS_TOKEN }}
- name: Install Dependencies
uses: ./.github/actions/install-dependencies
- name: Configure Git
run: |
git config --global user.email wrangler@cloudflare.com
git config --global user.name 'Wrangler automated PR updater'
- name: Generate Miniflare changesets
# The paremeters to the script are:
# - PR: The number of the current Dependabot PR
# - Packages: Coma-separated names of the workers-sdk packages whose dependencies are being updated
# - PackageJSON: The path to the package JSON being updated by Dependabot
# - Changeset prefix: The prefix to go on the front of the filename of the generated changeset
run: >-
node -r esbuild-register tools/dependabot/generate-dependabot-pr-changesets.ts
"$PR_NUMBER"
miniflare,wrangler
packages/miniflare/package.json
dependabot-update
env:
PR_NUMBER: ${{ github.event.number }}
@@ -0,0 +1,19 @@
name: OpenCode Issue Solver
on:
workflow_dispatch:
inputs:
issue-number:
description: "Issue number to attempt to fix"
required: true
type: number
jobs:
assess-issue:
runs-on: ubuntu-latest
permissions: {}
steps:
- name: Exit
run: |
exit 1
+53
View File
@@ -0,0 +1,53 @@
name: Prerelease
on:
pull_request:
push:
branches:
- main
- d1
- workflows
- next
- v3-maintenance
permissions:
contents: read
jobs:
build:
if: ${{ github.repository_owner == 'cloudflare' }}
runs-on: ubuntu-latest
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.head_ref != 'changeset-release/main' }}
timeout-minutes: 30
steps:
- name: Checkout Repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 1
persist-credentials: false
- name: Install Dependencies
# Defense in depth: do not pass Turbo remote cache credentials and
# disable the pnpm store cache so release builds always resolve
# packages from the registry and rebuild every task from source,
# rather than restoring from a (potentially poisoned) cache.
uses: ./.github/actions/install-dependencies
with:
disable-cache: "true"
- name: Build
run: pnpm build --filter="./packages/*"
env:
NODE_ENV: "production"
CI_OS: ${{ runner.os }}
# this is the "test/staging" key for sparrow analytics
SPARROW_SOURCE_KEY: "5adf183f94b3436ba78d67f506965998"
ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }}
SENTRY_DSN: "https://9edbb8417b284aa2bbead9b4c318918b@sentry10.cfdata.org/583"
ALGOLIA_PUBLIC_KEY: ${{ secrets.ALGOLIA_PUBLIC_KEY }}
WRANGLER_PRERELEASE_LABEL: ${{ github.head_ref || github.ref_name }}
- name: Upload packages
run: node -r esbuild-register .github/prereleases/upload.mjs
@@ -0,0 +1,67 @@
name: "Rerun Code Owners (Privileged)"
# Privileged companion to rerun-codeowners.yml.
#
# Runs after the "Rerun Code Owners" trigger workflow completes. The
# workflow_run event always executes from the default branch with full
# permissions — this is what allows us to call the Actions API even when the
# original event (pull_request_review) came from a fork PR, where the
# GITHUB_TOKEN would otherwise be read-only.
#
# The PR head SHA is read from github.event.workflow_run.head_sha — this is
# GitHub-provided metadata and cannot be manipulated by fork code (unlike
# the previous artifact-based approach).
#
# Strategy: CHECK-NAME LOOKUP
# ----------------------------
# The codeowners.yml workflow uses pull_request_target, which means its
# workflow runs are indexed by the BASE branch SHA (e.g. main), NOT the PR
# head SHA. So we cannot find the run via:
#
# GET /actions/workflows/codeowners.yml/runs?head_sha=<PR_HEAD>
#
# That query returns nothing because the run's head_sha is main's HEAD.
#
# However, the codeowners-plus action creates its CHECK RUN on the PR head
# commit (so the status appears on the PR). The check-runs API lets us look
# up by check name + commit SHA:
#
# GET /commits/<PR_HEAD>/check-runs?check_name=Run+Codeowners+Plus
#
# From the check run's details_url we extract the Actions job ID, then
# re-run that specific job.
on:
workflow_run: # zizmor: ignore[dangerous-triggers] privileged companion reruns a fixed default-branch workflow using GitHub-provided workflow_run metadata only
workflows: ["Rerun Code Owners"]
types: [completed]
permissions: {}
jobs:
rerun-codeowners:
name: "Rerun Codeowners Plus"
runs-on: ubuntu-latest
if: github.event.workflow_run.conclusion == 'success'
permissions:
actions: write
checks: read
steps:
- name: Re-run Codeowners check
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
REPO: ${{ github.repository }}
run: |
# Find the "Run Codeowners Plus" check run on the PR head commit
# and extract the Actions job ID from the check run's details URL
# (the last path segment before any query string).
job_id=$(gh api "repos/${REPO}/commits/${HEAD_SHA}/check-runs?check_name=Run+Codeowners+Plus" \
--jq '(.check_runs[0].details_url // "") | split("/") | last | split("?") | first' || true)
if [ -n "$job_id" ]; then
gh api "repos/${REPO}/actions/jobs/${job_id}/rerun" --method POST \
|| echo "::warning::Job ${job_id} may already be running"
echo "Re-triggered 'Run Codeowners Plus' (job ${job_id})"
else
echo "::warning::Check 'Run Codeowners Plus' not found for SHA ${HEAD_SHA}"
fi
+35
View File
@@ -0,0 +1,35 @@
name: "Rerun Code Owners"
# Trigger: a review is submitted or dismissed on a pull request.
#
# Goal: re-run the "Run Codeowners Plus" check from the main codeowners.yml
# workflow (which is triggered by pull_request_target) so it re-evaluates
# approval status after the review change.
#
# Why a two-workflow design?
# -------------------------
# For fork PRs, pull_request_review gives a read-only GITHUB_TOKEN and no
# access to secrets. Unlike labeled/unlabeled events there is no
# pull_request_review variant of pull_request_target, so we cannot get a
# privileged token in a single workflow. Instead this workflow simply needs
# to *exist and succeed* — the companion workflow
# (rerun-codeowners-privileged.yml) is triggered by the workflow_run event
# when THIS workflow completes. workflow_run always runs from the default
# branch with full permissions, and it reads the PR head SHA directly from
# github.event.workflow_run.head_sha (GitHub-provided metadata, not
# controllable by fork code).
on:
pull_request_review:
types: [submitted, dismissed]
permissions: {}
jobs:
trigger:
name: "Trigger Privileged Rerun"
runs-on: ubuntu-latest
steps:
- name: Log trigger
run: echo "Review event on ${PR_URL}, privileged rerun will follow."
env:
PR_URL: ${{ github.event.pull_request.html_url }}
+86
View File
@@ -0,0 +1,86 @@
name: "Rerun Remote Tests"
# Trigger: the "run-remote-tests" or "run-c3-frameworks-tests" label is added
# to or removed from a pull request.
#
# Goal: re-run the E2E workflows (e2e-wrangler.yml, e2e-vite.yml, c3-e2e.yml)
# so they pick up the label change and either pass or withhold the Cloudflare
# API token to the test steps. This avoids adding "labeled" as a trigger type
# to every E2E workflow (which would cause wasteful re-runs on unrelated label
# changes).
#
# Using pull_request_target (not pull_request) so the workflow has access to a
# privileged GITHUB_TOKEN even for fork PRs. This is safe because the workflow
# runs code from the default branch — fork authors cannot modify it — and only
# calls the Actions API (no checkout of untrusted code).
on:
pull_request_target: # zizmor: ignore[dangerous-triggers] label-driven rerun workflow needs actions:write and does not check out or execute PR code
types: [labeled, unlabeled]
permissions: {}
jobs:
rerun-e2e:
name: "Rerun E2E Tests"
if: github.event.label.name == 'ci:run-remote-tests' || github.event.label.name == 'run-c3-frameworks-tests'
runs-on: ubuntu-latest
permissions:
actions: write
steps:
- name: "Re-run E2E workflows"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
LABEL_NAME: ${{ github.event.label.name }}
REPO: ${{ github.repository }}
run: |
# Determine which workflows to re-run based on the label.
if [ "$LABEL_NAME" = "ci:run-remote-tests" ]; then
WORKFLOWS="e2e-wrangler.yml e2e-vite.yml c3-e2e.yml"
else
WORKFLOWS="c3-e2e.yml"
fi
for workflow in ${WORKFLOWS}; do
# Find the most recent run of this workflow at the PR head SHA.
run_info=$(gh api "repos/${REPO}/actions/workflows/${workflow}/runs?head_sha=${HEAD_SHA}&per_page=1" \
--jq '.workflow_runs[0] | "\(.id) \(.status)"' || true)
run_id=$(echo "$run_info" | awk '{print $1}')
status=$(echo "$run_info" | awk '{print $2}')
if [ -z "$run_id" ] || [ "$run_id" = "null" ]; then
echo "::warning::No run found for ${workflow} at SHA ${HEAD_SHA}"
continue
fi
# Runs that haven't started yet will see the newly-added label
# when they begin — no need to cancel and re-run them.
if [ "$status" != "completed" ] && [ "$status" != "in_progress" ]; then
echo "${workflow} run ${run_id} is ${status} — not yet started, skipping."
continue
fi
# If the run is actively executing, the label check has already
# evaluated (likely to the old value). Cancel it first — the
# rerun endpoint only works on completed runs.
if [ "$status" = "in_progress" ]; then
echo "Cancelling in-progress ${workflow} run ${run_id}..."
gh api "repos/${REPO}/actions/runs/${run_id}/cancel" --method POST || true
# Cancellation is async; poll until the run reaches "completed".
for i in $(seq 1 30); do
current=$(gh api "repos/${REPO}/actions/runs/${run_id}" --jq '.status')
if [ "$current" = "completed" ]; then
echo "Run ${run_id} is now completed (cancelled)."
break
fi
echo " Waiting for cancellation to finish (${i}/30, status: ${current})..."
sleep 2
done
fi
gh api "repos/${REPO}/actions/runs/${run_id}/rerun" --method POST \
&& echo "Re-triggered ${workflow} (run ${run_id})" \
|| echo "::warning::Failed to re-run ${workflow} (run ${run_id})"
done
@@ -0,0 +1,100 @@
name: Run CI on behalf of External Forks
on:
workflow_dispatch:
inputs:
pr-number:
description: "The PR number to run CI on behalf of"
required: true
commit-sha:
description: "The specific commit SHA from the PR branch to run CI on"
required: true
reviewed:
description: "Confirm that the PR has been reviewed for use/leakage of secrets"
type: boolean
required: true
permissions:
contents: read # no write permissions are needed since the workflow uses GH_ACCESS_TOKEN instead of GITHUB_TOKEN
pull-requests: write # Required for creating or updating the draft PR on behalf of the user
jobs:
create-draft-pr:
name: Create or Update Draft PR
if: ${{ inputs.reviewed == true }}
runs-on: ubuntu-slim
steps:
- name: Checkout Repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
persist-credentials: true
token: ${{ secrets.GH_ACCESS_TOKEN }}
- name: Check user for team affiliation
uses: tspascoal/get-user-teams-membership@ba78054988f58bea69b7c6136d563236f8ed2fc0
id: teamAffiliation
with:
GITHUB_TOKEN: ${{ secrets.READ_ONLY_ORG_GITHUB_TOKEN }}
username: ${{ github.actor }}
team: wrangler
- name: Stop workflow if user is not a team member
if: ${{ steps.teamAffiliation.outputs.isTeamMember != 'true' }}
run: |
echo "You must be on the \"wrangler\" team to trigger this job."
exit 1
- name: "Checkout PR"
run: |
gh pr checkout "$PR_NUM" --branch "run-ci-on-behalf-of-$PR_NUM"
git reset --hard "$COMMIT_SHA"
env:
# We need a PAT to checkout the fork
GH_TOKEN: ${{ secrets.GH_ACCESS_TOKEN }}
PR_NUM: ${{ inputs.pr-number }}
COMMIT_SHA: ${{ inputs.commit-sha }}
- name: Push Branch
run: |
git config user.email "github-actions[bot]@users.noreply.github.com"
git config user.name "github-actions[bot]"
git push origin HEAD --force
- name: "Create or Update Draft PR"
run: |
existing_pr=$(gh pr list \
--head "run-ci-on-behalf-of-$PR_NUM" \
--state open \
--json number \
--jq 'first(.[].number) // ""')
if [ -n "$existing_pr" ]; then
gh pr edit "$existing_pr" \
--add-label "ci:e2e" \
--add-label "ci:run-remote-tests" \
--title "$TITLE" \
--body "$BODY"
else
gh pr create \
--head "run-ci-on-behalf-of-$PR_NUM" \
--draft \
--label "ci:e2e" \
--label "ci:run-remote-tests" \
--title "$TITLE" \
--body "$BODY"
fi
env:
GH_TOKEN: ${{ github.token }}
PR_NUM: ${{ inputs.pr-number }}
TITLE: "Run CI on behalf of #${{ inputs.pr-number }} @${{ inputs.commit-sha }}"
BODY: "This PR runs CI on behalf of #${{ inputs.pr-number }} at commit ${{ inputs.commit-sha }}. It can be closed after the CI run is complete."
- name: "Trigger CI"
run: |
# Push an empty commit to trigger CI workflows
# This ensures CI runs because the PR already exists when this push happens
git commit --allow-empty -m "Trigger CI for #$PR_NUM"
git push origin HEAD
env:
PR_NUM: ${{ inputs.pr-number }}
+31
View File
@@ -0,0 +1,31 @@
name: Semgrep OSS scan
on:
pull_request: {}
push:
branches: [main]
workflow_dispatch: {}
schedule:
- cron: "0 0 15 * *"
concurrency:
group: semgrep-${{ github.event_name }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
permissions:
contents: read
jobs:
semgrep:
name: semgrep-oss
runs-on: ubuntu-slim
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 1
persist-credentials: false
- id: cache-semgrep
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: ~/.local
key: semgrep-1.160.0-${{ runner.os }}
- if: steps.cache-semgrep.outputs.cache-hit != 'true'
run: pip install --user semgrep==1.160.0
- run: echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- run: semgrep scan --config=auto || true
@@ -0,0 +1,118 @@
name: CI (Other Node Versions)
description: Run primary tests on other supported Node.js versions
on:
merge_group:
pull_request:
types: [synchronize, opened, reopened, labeled, unlabeled]
permissions:
contents: read
jobs:
test-other-node-versions:
timeout-minutes: 45
runs-on: ubuntu-latest
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.node_version }}-test-other-node-versions
cancel-in-progress: ${{ github.head_ref != 'changeset-release/main' }}
name: ${{ format('Tests ({0})', matrix.description) }}
strategy:
fail-fast: false
matrix:
# expected_to_fail: if true, tests may fail on this Node version.
# These versions are skipped on normal PRs but run on Version Packages PRs
# (changeset-release/main) or when the 'ci:test-all-node-versions' label is added.
#
# Node 24 is pinned to 24.18.0 (rather than floating `24`): Node 24.17.0
# regressed node-fetch@2 keep-alive responses with ERR_STREAM_PREMATURE_CLOSE
# (nodejs/node#63989, fixed in 24.18.0). `description` stays "Node 24" to
# keep the job/check name stable.
include:
- {
node_version: 24.18.0,
description: "Node 24",
expected_to_fail: true,
}
- { node_version: 25, description: "Node 25", expected_to_fail: true }
steps:
- name: Checkout Repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 1
persist-credentials: false
- name: Filter changed paths
uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: changes
with:
filters: |
everything_but_markdown:
- '!**/*.md'
- name: Check if should run tests
id: should_run
env:
CHANGES_EVERYTHING_BUT_MARKDOWN: ${{ steps.changes.outputs.everything_but_markdown }}
EXPECTED_TO_FAIL: ${{ matrix.expected_to_fail }}
HAS_TEST_ALL_NODE_VERSIONS_LABEL: ${{ contains(github.event.pull_request.labels.*.name, 'ci:test-all-node-versions') }}
HEAD_REF: ${{ github.event.pull_request.head.ref }}
run: |
if [[ "$CHANGES_EVERYTHING_BUT_MARKDOWN" == "true" ]] && \
[[ "$EXPECTED_TO_FAIL" != "true" || \
"$HEAD_REF" == "changeset-release/main" || \
"$HAS_TEST_ALL_NODE_VERSIONS_LABEL" == "true" ]]; then
echo "result=true" >> $GITHUB_OUTPUT
else
echo "result=false" >> $GITHUB_OUTPUT
fi
- name: Install Dependencies
if: steps.should_run.outputs.result == 'true'
uses: ./.github/actions/install-dependencies
with:
node-version: ${{ matrix.node_version }}
turbo-api: ${{ secrets.TURBO_API }}
turbo-team: ${{ secrets.TURBO_TEAM }}
turbo-token: ${{ secrets.TURBO_TOKEN }}
turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
# The miniflare browser-rendering tests download Chrome (~150 MB) via
# @puppeteer/browsers at runtime. Caching the binary avoids repeat
# downloads and reduces the chance of tests hanging while waiting for
# the download to finish within their timeout window.
- name: Restore Chrome browser cache (Browser Run)
if: steps.should_run.outputs.result == 'true'
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
key: chrome-${{ runner.os }}-${{ hashFiles('packages/miniflare/src/plugins/browser-rendering/browser-version.ts') }}
path: |
~/.cache/.wrangler/chrome
- name: Run tests (packages)
# We are running the package tests first be able to get early feedback on changes.
# There is no point in running the fixtures if a package is broken.
if: steps.should_run.outputs.result == 'true'
run: pnpm run test:ci --log-order=stream --concurrency=1 --filter="./packages/*"
env:
NODE_OPTIONS: "--max_old_space_size=8192"
WRANGLER_LOG_PATH: ${{ runner.temp }}/wrangler-debug-logs/
TEST_REPORT_PATH: ${{ runner.temp }}/test-report/packages/index.html
CI_OS: ${{ matrix.description }}
- name: Run e2e tests (unenv-preset)
if: steps.should_run.outputs.result == 'true'
run: pnpm test:e2e --filter wrangler -- unenv-preset
env:
NODE_OPTIONS: "--max_old_space_size=8192"
WRANGLER_LOG_PATH: ${{ runner.temp }}/wrangler-debug-logs/
CI_OS: ${{ matrix.description }}
- name: Run tests (vite-plugin)
if: steps.should_run.outputs.result == 'true'
run: pnpm test:ci --filter @cloudflare/vite-plugin
env:
NODE_OPTIONS: "--max_old_space_size=8192"
WRANGLER_LOG_PATH: ${{ runner.temp }}/wrangler-debug-logs/
CI_OS: ${{ matrix.description }}
+202
View File
@@ -0,0 +1,202 @@
name: CI
on:
merge_group:
pull_request:
permissions:
contents: read
env:
# We run `playwright install` manually instead
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1"
jobs:
add-to-project:
if: github.head_ref != 'changeset-release/main'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-add-pr
cancel-in-progress: ${{ github.head_ref != 'changeset-release/main' }}
timeout-minutes: 30
name: Add PR to project
runs-on: ubuntu-latest
steps:
- run: curl -X POST https://devprod-status-bot.devprod.workers.dev/pr-project/workers-sdk/${{ github.event.number }}
check:
timeout-minutes: 30
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-checks
cancel-in-progress: ${{ github.head_ref != 'changeset-release/main' }}
name: "Checks"
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
persist-credentials: false
- name: Install Dependencies
uses: ./.github/actions/install-dependencies
with:
turbo-api: ${{ secrets.TURBO_API }}
turbo-team: ${{ secrets.TURBO_TEAM }}
turbo-token: ${{ secrets.TURBO_TOKEN }}
turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
- name: Check the changesets
run: node -r esbuild-register tools/deployments/validate-changesets.ts
- name: Check for errors
run: pnpm run check --summarize
env:
CI_OS: ${{ runner.os }}
NODE_OPTIONS: "--max_old_space_size=8192"
- name: Filter changed paths
uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: changes
with:
filters: |
miniflare_openapi:
- 'packages/miniflare/src/workers/local-explorer/openapi.local.json'
- 'packages/miniflare/src/workers/local-explorer/generated/**'
- 'packages/miniflare/scripts/openapi-filter-config.ts'
- 'packages/miniflare/scripts/filter-openapi.ts'
- 'packages/miniflare/scripts/check-generate-api.ts'
- 'packages/miniflare/openapi-ts.config.ts'
- name: Check generated OpenAPI types are up to date
if: steps.changes.outputs.miniflare_openapi == 'true'
run: pnpm -F miniflare check:generate-api
# Check for old Node.js version warnings and errors
- name: Use Node.js v20
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 20
- name: Check for error message on Node.js < v22
run: node packages/wrangler/src/__tests__/test-old-node-version.js error
test:
timeout-minutes: 30
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.os }}-${{ matrix.suite }}-test
cancel-in-progress: ${{ github.head_ref != 'changeset-release/main' }}
name: ${{ format('Tests ({0}, {1})', matrix.description, matrix.suite) }}
strategy:
fail-fast: false
matrix:
os:
- macos-latest
- ubuntu-latest
- windows-latest
suite:
- packages-and-tools
- fixtures
include:
- os: macos-latest
description: macOS
- os: ubuntu-latest
description: Linux
- os: windows-latest
description: Windows
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
persist-credentials: false
- name: Filter changed paths
uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: changes
with:
filters: |
everything_but_markdown:
- '!**/*.md'
- name: Install Dependencies
if: steps.changes.outputs.everything_but_markdown == 'true'
uses: ./.github/actions/install-dependencies
with:
turbo-api: ${{ secrets.TURBO_API }}
turbo-team: ${{ secrets.TURBO_TEAM }}
turbo-token: ${{ secrets.TURBO_TOKEN }}
turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
# Browser Run tests in `packages/miniflare/test/plugins/browser/index.spec.ts`
# and the `fixtures/browser-run` fixture use `@puppeteer/browsers` to
# download Chrome into the global Wrangler cache. Caching that binary
# across CI runs avoids ~150 MB of repeat downloads per run and reduces
# the surface area for the intermittent partial-extraction race that
# surfaces as `The browser folder (...) exists but the executable (...)
# is missing` (see #13971, #13980).
#
# The Browser Run fixture is skipped on Ubuntu (AppArmor), but the
# miniflare browser spec runs everywhere, so the cache is needed on all
# three OSes for `packages-and-tools` and on Windows + macOS for
# `fixtures`.
- name: Restore Chrome browser cache (Browser Run)
if: steps.changes.outputs.everything_but_markdown == 'true' && (matrix.suite == 'packages-and-tools' || (matrix.suite == 'fixtures' && matrix.os != 'ubuntu-latest'))
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
# The Chrome version lives in
# `packages/miniflare/src/plugins/browser-rendering/browser-version.ts`.
# Bumping that constant invalidates this cache automatically via
# `hashFiles()` — a cache miss only triggers a fresh download, no
# functional impact.
key: chrome-${{ runner.os }}-${{ hashFiles('packages/miniflare/src/plugins/browser-rendering/browser-version.ts') }}
path: |
~/.cache/.wrangler/chrome
~/Library/Caches/.wrangler/chrome
~/AppData/Local/xdg.cache/.wrangler/chrome
- name: Run tests (tools only)
# tools _only_ needs to be tested on Linux because they're only intended to run in CI
if: steps.changes.outputs.everything_but_markdown == 'true' && matrix.suite == 'packages-and-tools' && matrix.os == 'ubuntu-latest'
run: pnpm run test:ci --log-order=stream --filter="@cloudflare/tools"
env:
NODE_OPTIONS: "--max_old_space_size=8192"
TEST_REPORT_PATH: ${{ runner.temp }}/test-report/tools/index.html
CI_OS: ${{ matrix.description }}
- name: Run tests (packages)
if: steps.changes.outputs.everything_but_markdown == 'true' && matrix.suite == 'packages-and-tools'
# We skip @cloudflare/vitest-pool-workers tests in CI on Windows because they're very flaky. We still run the vitest-pool-workers-examples fixture, which is a comprehensive set of example tests and gives us a lot of confidence.
# The @cloudflare/vitest-pool-workers tests skipped are things like watch mode, which constantly times out probably due to the github runners in use.
# Package tests are well-isolated (separate processes, temp dirs, random ports, MSW mocks) and can safely run in parallel.
# Concurrency is capped at 3 to avoid CPU starvation on 4-vCPU CI runners when heavyweight suites
# (wrangler/forks, miniflare/workerd, vitest-pool-workers/Verdaccio) overlap.
run: pnpm run test:ci --log-order=stream --concurrency=3 --filter="./packages/*" ${{ matrix.os == 'windows-latest' && '--filter="!./packages/vitest-pool-workers"' || '' }}
env:
NODE_OPTIONS: "--max_old_space_size=8192"
WRANGLER_LOG_PATH: ${{ runner.temp }}/wrangler-debug-logs/
TEST_REPORT_PATH: ${{ runner.temp }}/test-report/packages/index.html
CI_OS: ${{ matrix.description }}
- name: Run tests (fixtures)
if: steps.changes.outputs.everything_but_markdown == 'true' && matrix.suite == 'fixtures'
# Browser Run fixture is disabled on Ubuntu because of https://pptr.dev/troubleshooting#issues-with-apparmor-on-ubuntu
# Since the dev registry is now file-based (not network-based), fixture tests can safely run in parallel.
# Concurrency is capped at 2 to avoid CPU starvation on CI runners when multiple fixtures
# spawn workerd processes simultaneously (Windows runners are especially slow under load).
run: pnpm run test:ci --concurrency=2 --log-order=stream --filter="./fixtures/*" ${{ matrix.os == 'ubuntu-latest' && '--filter="!./fixtures/browser-run"' || '' }}
env:
NODE_OPTIONS: "--max_old_space_size=8192"
WRANGLER_LOG_PATH: ${{ runner.temp }}/wrangler-debug-logs/
TEST_REPORT_PATH: ${{ runner.temp }}/test-report/index.html
CI_OS: ${{ matrix.description }}
- name: Upload turbo logs
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: turbo-runs-${{ matrix.os }}-${{ matrix.suite }}
path: .turbo/runs
+235
View File
@@ -0,0 +1,235 @@
name: Triage Issue
on:
issues:
types: [opened]
issue_comment:
types: [created]
workflow_dispatch:
inputs:
issue-number:
description: "Issue number to triage"
required: true
type: number
permissions:
contents: read
# Writes (comment + labels) are performed with GH_ACCESS_TOKEN (the
# workers-devprod bot) so they are attributed to that identity, not
# github-actions[bot]. Only read access is needed from the default token.
issues: read
# Cancel in-progress runs for the same issue
concurrency:
group: triage-${{ github.event.issue.number || inputs.issue-number }}
cancel-in-progress: ${{ github.head_ref != 'changeset-release/main' }}
jobs:
triage:
runs-on: ubuntu-latest
# Always run for manual dispatch. For issue events, skip PRs and
# bot-created issues. For comment events, skip PRs, bot comments, and the
# triage bot's own sticky comment (identified by the marocchino marker) to
# avoid infinite re-triage loops.
if: >-
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'issues' &&
!github.event.issue.pull_request &&
github.event.issue.user.type != 'Bot') ||
(github.event_name == 'issue_comment' &&
!github.event.issue.pull_request &&
github.event.comment.user.type != 'Bot' &&
!contains(github.event.comment.body, '<!-- Sticky Pull Request Comment'))
timeout-minutes: 60
steps:
- name: Resolve issue number
id: issue
env:
EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }}
EVENT_NAME: ${{ github.event_name }}
INPUT_ISSUE_NUMBER: ${{ inputs.issue-number }}
run: |
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
echo "number=${INPUT_ISSUE_NUMBER}" >> "$GITHUB_OUTPUT"
else
echo "number=${EVENT_ISSUE_NUMBER}" >> "$GITHUB_OUTPUT"
fi
- name: Checkout (sparse — just the skill and opencode config)
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
sparse-checkout: |
.github/skills
.github/opencode.json
- name: Install OpenCode
run: |
npm install -g opencode-ai
cp .github/opencode.json ./opencode.json
- name: Fetch issue data
env:
GH_TOKEN: ${{ github.token }}
ISSUE: ${{ steps.issue.outputs.number }}
REPO: ${{ github.repository }}
run: |
mkdir -p "data/${ISSUE}"
gh issue view "$ISSUE" \
--repo "$REPO" \
--json number,title,body,comments,createdAt,updatedAt,labels,state,author \
> "data/${ISSUE}/context.json"
- name: Analyze issue with OpenCode
env:
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CF_AI_GATEWAY_ACCOUNT_ID }}
CLOUDFLARE_GATEWAY_ID: ${{ secrets.CF_AI_GATEWAY_NAME }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CF_AI_GATEWAY_TOKEN }}
ISSUE: ${{ steps.issue.outputs.number }}
REPO: ${{ github.repository }}
run: |
opencode run \
--print-logs \
"Analyze GitHub issue ${REPO}#${ISSUE} using the skill at .github/skills/issue-review.md.
The issue data has been pre-fetched to data/${ISSUE}/context.json.
Read it with the read tool and follow the triage process.
Create:
- data/${ISSUE}/report.md (full markdown report)
- data/${ISSUE}/summary.json (structured JSON summary)
"
- name: Upload report to dashboard
env:
CF_ACCESS_CLIENT_ID: ${{ secrets.CF1_ACCESS_CLIENT_ID }}
CF_ACCESS_CLIENT_SECRET: ${{ secrets.CF1_ACCESS_CLIENT_SECRET }}
DASHBOARD_URL: ${{ secrets.REPORTS_DASHBOARD_URL }}
ISSUE: ${{ steps.issue.outputs.number }}
REPO: ${{ github.repository }}
run: |
# Check files were created
if [ ! -f "data/${ISSUE}/report.md" ]; then
echo "::error::report.md was not generated"
exit 1
fi
if [ ! -f "data/${ISSUE}/summary.json" ]; then
echo "::error::summary.json was not generated"
exit 1
fi
# Validate the summary is parseable JSON before doing anything with it.
if ! jq empty "data/${ISSUE}/summary.json" 2>/dev/null; then
echo "::error::summary.json is not valid JSON"
exit 1
fi
# Build the dashboard payload from the structured summary + markdown report.
# suggestedComment is kept as a "Yes"/"No" string for dashboard compatibility.
PAYLOAD=$(jq \
--arg githubUrl "https://github.com/${REPO}/issues/${ISSUE}" \
--rawfile reportMarkdown "data/${ISSUE}/report.md" \
'{
title: .title,
githubUrl: $githubUrl,
recommendation: .recommendation,
difficulty: .difficulty,
reasoning: .reasoning,
suggestedAction: .suggestedAction,
suggestedComment: (if .hasSuggestedComment then "Yes" else "No" end),
reportMarkdown: $reportMarkdown
}' "data/${ISSUE}/summary.json")
# POST to dashboard API
HTTP_STATUS=$(curl -L --post302 -s -o response.json -w "%{http_code}" \
-X POST "${DASHBOARD_URL}/api/report/${ISSUE}" \
-H "Content-Type: application/json" \
-H "CF-Access-Client-Id: ${CF_ACCESS_CLIENT_ID}" \
-H "CF-Access-Client-Secret: ${CF_ACCESS_CLIENT_SECRET}" \
-d "$PAYLOAD")
echo "Dashboard API response: HTTP ${HTTP_STATUS}"
cat response.json
if [ "$HTTP_STATUS" != "200" ]; then
echo "::error::Failed to upload report (HTTP ${HTTP_STATUS})"
exit 1
fi
echo "Report uploaded for issue #${ISSUE}"
- name: Build triage comment
env:
ISSUE: ${{ steps.issue.outputs.number }}
run: |
{
echo "> [!NOTE]"
echo "> This is an automated, advisory triage report generated by workers-devprod. It is not an official maintainer response — a maintainer will follow up."
echo ""
echo "<details>"
echo "<summary>🤖 Automated triage report</summary>"
echo ""
cat "data/${ISSUE}/report.md"
echo ""
echo "</details>"
echo ""
echo "<details>"
echo "<summary>Structured summary (JSON)</summary>"
echo ""
echo '```json'
cat "data/${ISSUE}/summary.json"
echo ""
echo '```'
echo ""
echo "</details>"
} > "data/${ISSUE}/comment.md"
- name: Post triage comment
uses: marocchino/sticky-pull-request-comment@d4d6b0936434b21bc8345ad45a440c5f7d2c40ff # v3.0.3
with:
header: triage-report
number: ${{ steps.issue.outputs.number }}
path: data/${{ steps.issue.outputs.number }}/comment.md
GITHUB_TOKEN: ${{ secrets.GH_ACCESS_TOKEN }}
- name: Apply suggested labels
env:
GH_TOKEN: ${{ secrets.GH_ACCESS_TOKEN }}
ISSUE: ${{ steps.issue.outputs.number }}
REPO: ${{ github.repository }}
run: |
SUMMARY_PATH="data/${ISSUE}/summary.json"
if [ ! -f "$SUMMARY_PATH" ]; then
echo "No summary.json; skipping label application"
exit 0
fi
# suggestedLabels is a JSON array; emit one label per line.
jq -r '.suggestedLabels // [] | .[]' "$SUMMARY_PATH" > suggested-labels.txt
if [ ! -s suggested-labels.txt ]; then
echo "No suggested labels"
exit 0
fi
# Only apply labels that actually exist in the repo.
gh label list --repo "$REPO" --limit 500 --json name --jq '.[].name' > repo-labels.txt
ARGS=()
while IFS= read -r label; do
if [ -z "$label" ]; then
continue
fi
if grep -Fxq "$label" repo-labels.txt; then
ARGS+=(--add-label "$label")
else
echo "Skipping unknown label: ${label}"
fi
done < suggested-labels.txt
if [ ${#ARGS[@]} -eq 0 ]; then
echo "No valid labels to apply"
exit 0
fi
gh issue edit "$ISSUE" --repo "$REPO" "${ARGS[@]}"
@@ -0,0 +1,68 @@
name: Validate PR Description
on:
merge_group:
pull_request:
types:
[
opened,
synchronize,
reopened,
labeled,
unlabeled,
edited,
ready_for_review,
converted_to_draft,
]
permissions:
contents: read
jobs:
check:
# Don't check the Version Packages PR or merge queue jobs
if: github.event_name != 'merge_group' && github.head_ref != 'changeset-release/main'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-add-pr
cancel-in-progress: ${{ github.head_ref != 'changeset-release/main' }}
timeout-minutes: 30
name: Check
runs-on: ubuntu-slim
steps:
- name: Checkout Repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 1
persist-credentials: false
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: changes
with:
filters: |
everything_but_markdown:
- '!**/*.md'
- name: Install Dependencies
if: steps.changes.outputs.everything_but_markdown == 'true'
uses: ./.github/actions/install-dependencies
with:
turbo-api: ${{ secrets.TURBO_API }}
turbo-team: ${{ secrets.TURBO_TEAM }}
turbo-token: ${{ secrets.TURBO_TOKEN }}
turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
- name: List changed files
if: steps.changes.outputs.everything_but_markdown == 'true'
id: files
uses: Ana06/get-changed-files@25f79e676e7ea1868813e21465014798211fad8c # v2.3.0
with:
format: "json"
- run: node -r esbuild-register tools/deployments/validate-pr-description.ts
if: steps.changes.outputs.everything_but_markdown == 'true'
env:
TITLE: ${{ github.event.pull_request.title }}
BODY: ${{ github.event.pull_request.body }}
LABELS: ${{ toJson(github.event.pull_request.labels.*.name) }}
FILES: ${{ steps.files.outputs.all }}
DRAFT: ${{ github.event.pull_request.draft }}
@@ -0,0 +1,100 @@
name: Vite plugin playgrounds
on:
merge_group:
pull_request:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.number || github.sha }}
cancel-in-progress: ${{ github.head_ref != 'changeset-release/main' }}
env:
# We run `playwright install` manually instead
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1"
jobs:
test:
timeout-minutes: 30
name: ${{ format('Vite Plugin Playground ({0}, {1})', matrix.os, matrix.vite) }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
vite: ["vite-8"]
include:
- os: ubuntu-latest
vite: "vite-6"
- os: ubuntu-latest
vite: "vite-7"
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- name: Filter changed paths
uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: changes
with:
filters: |
everything_but_markdown:
- '!**/*.md'
- name: Install Dependencies
if: steps.changes.outputs.everything_but_markdown == 'true'
uses: ./.github/actions/install-dependencies
with:
turbo-api: ${{ secrets.TURBO_API }}
turbo-team: ${{ secrets.TURBO_TEAM }}
turbo-token: ${{ secrets.TURBO_TOKEN }}
turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
- name: Install playwright
if: steps.changes.outputs.everything_but_markdown == 'true'
run: |
pnpm --filter @vite-plugin-cloudflare/playground playwright:install
- name: Build plugin with initial Vite version
if: steps.changes.outputs.everything_but_markdown == 'true'
run: |
pnpm dotenv -- pnpm turbo build --filter @cloudflare/vite-plugin
- name: Downgrade to Vite 6
if: steps.changes.outputs.everything_but_markdown == 'true' && matrix.vite == 'vite-6'
run: |
pnpm update -r --no-save vite@^6.1.0
- name: Downgrade to Vite 7
if: steps.changes.outputs.everything_but_markdown == 'true' && matrix.vite == 'vite-7'
run: |
pnpm update -r --no-save vite@^7.0.0
- name: Run dev playground tests
if: steps.changes.outputs.everything_but_markdown == 'true'
# We use `--only` to prevent TurboRepo from rebuilding dependencies
# This ensures the Vite plugin is not rebuilt using a different Vite version
run: |
pnpm dotenv -- pnpm turbo test:ci:serve --filter @vite-plugin-cloudflare/playground --only
env:
VITE_VERSION: ${{ matrix.vite }}
NODE_OPTIONS: "--max_old_space_size=8192"
WRANGLER_LOG_PATH: ${{ runner.temp }}/wrangler-debug-logs/
TEST_REPORT_PATH: ${{ runner.temp }}/test-report/index.html
NODE_DEBUG: "@cloudflare:vite-plugin"
CI_OS: ${{ matrix.os }}
- name: Run build/preview playground tests
if: steps.changes.outputs.everything_but_markdown == 'true'
# We use `--only` to prevent TurboRepo from rebuilding dependencies
# This ensures the Vite plugin is not rebuilt using a different Vite version
run: |
pnpm dotenv -- pnpm turbo test:ci:build --filter @vite-plugin-cloudflare/playground --only
env:
VITE_VERSION: ${{ matrix.vite }}
NODE_OPTIONS: "--max_old_space_size=8192"
WRANGLER_LOG_PATH: ${{ runner.temp }}/wrangler-debug-logs/
TEST_REPORT_PATH: ${{ runner.temp }}/test-report/index.html
NODE_DEBUG: "@cloudflare:vite-plugin"
CI_OS: ${{ matrix.os }}
- name: Upload turbo logs
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: turbo-runs-${{ matrix.os }}-${{ matrix.vite }}
path: .turbo/runs
@@ -0,0 +1,60 @@
name: Deploy (to testing) and Test Playground Preview Worker
# This workflow is designed to deploy a "testing" version of the Playground Preview Worker
# and test it via integration tests.
# Triggers:
# - push to `main`, on Cloudflare, where there are changes to the files in this worker's package
# - update to a PR, on Cloudflare, labelled as `playground-worker`
#
# Actions:
# - deploy to testing environment
# - run the end-to-end tests against testing deployment
on:
push:
branches:
- main
paths:
- "packages/playground-preview-worker/**"
pull_request:
types: [synchronize, opened, reopened, labeled, unlabeled]
permissions:
contents: read
jobs:
e2e-test:
if: github.repository_owner == 'cloudflare' && (github.event_name != 'pull_request' || contains(github.event.*.labels.*.name, 'feature:playground-worker'))
name: "Deploy Playground Preview Worker (testing)"
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 1
persist-credentials: false
- name: Install Dependencies
uses: ./.github/actions/install-dependencies
- name: Build tools and libraries
run: pnpm run build
env:
NODE_ENV: "production"
CI_OS: ${{ runner.os }}
- name: Build & deploy Worker
run: pnpm run deploy:testing
env:
NODE_ENV: "production"
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
working-directory: packages/playground-preview-worker
- name: Run tests
run: pnpm run test:e2e
env:
TMP_CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
TMP_CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
NODE_OPTIONS: "--max_old_space_size=8192"
working-directory: packages/playground-preview-worker
@@ -0,0 +1,34 @@
name: Deploy Workers Shared Production
on:
workflow_dispatch:
permissions:
contents: read
jobs:
deploy-workers:
name: Deploy Workers Shared Production
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 1
persist-credentials: false
- name: Install Dependencies
uses: ./.github/actions/install-dependencies
- name: Deploy
run: pnpm run deploy
working-directory: packages/workers-shared
env:
WORKERS_DEPLOY_AND_CONFIG_CLOUDFLARE_API_TOKEN: ${{ secrets.WORKERS_DEPLOY_AND_CONFIG_CLOUDFLARE_API_TOKEN }}
WORKERS_SHARED_SENTRY_ACCESS_ID: ${{ secrets.WORKERS_SHARED_SENTRY_ACCESS_ID }}
WORKERS_SHARED_SENTRY_ACCESS_SECRET: ${{ secrets.WORKERS_SHARED_SENTRY_ACCESS_SECRET }}
WORKERS_SHARED_SENTRY_AUTH_TOKEN: ${{ secrets.WORKERS_SHARED_SENTRY_AUTH_TOKEN }}
- name: Publish deployment summary
run: echo -e "# Deployment summary\n\n| Service | Version | Release link |\n| --- | --- | --- |\n| Asset Worker | \`aw-$(node -r esbuild-register ./scripts/get-version-tag.ts)\` | https://cflare.co/release-asset-worker |\n| Router Worker | \`rw-$(node -r esbuild-register ./scripts/get-version-tag.ts)\` | https://cflare.co/release-router-worker |\n" >> $GITHUB_STEP_SUMMARY
working-directory: packages/workers-shared
@@ -0,0 +1,31 @@
name: Deploy Workers Shared Staging
on:
push:
branches:
- main
paths:
- "packages/workers-shared/**"
permissions:
contents: read
jobs:
deploy-workers:
name: "Deploy Workers Shared Staging"
runs-on: ubuntu-latest
steps:
- name: "Checkout repo"
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 1
persist-credentials: false
- name: Install Dependencies
uses: ./.github/actions/install-dependencies
- name: Deploy
run: pnpm deploy:staging
working-directory: packages/workers-shared
env:
WORKERS_DEPLOY_AND_CONFIG_CLOUDFLARE_API_TOKEN: ${{ secrets.WORKERS_DEPLOY_AND_CONFIG_CLOUDFLARE_API_TOKEN }}
+241
View File
@@ -0,0 +1,241 @@
# Created by https://www.toptal.com/developers/gitignore/api/node,macos
# Edit at https://www.toptal.com/developers/gitignore?templates=node,macos
### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Moved from ./templates for ignoring all locks in templates
templates/**/*-lock.*
templates/**/*.lock
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional prettier cache
.prettiercache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
### Node Patch ###
# Serverless Webpack directories
.webpack/
# Optional stylelint cache
# SvelteKit build / generate output
.svelte-kit
# End of https://www.toptal.com/developers/gitignore/api/node,macos
wrangler-dist/
miniflare-dist
packages/wrangler/wrangler.toml
packages/wrangler/Dockerfile
packages/wrangler/src/cloudchamber/internal
packages/wranglerjs-compat-webpack-plugin/lib
.wrangler-1-cache
emitted-types/
_routes.generated.json
packages/quick-edit/vscode
packages/quick-edit/web
packages/wrangler/config-schema.json
packages/wrangler/metafile.json
packages/chrome-devtools-patches/built-devtools
packages/chrome-devtools-patches/.cipd
packages/chrome-devtools-patches/.gclient
packages/chrome-devtools-patches/.gclient_entries
packages/chrome-devtools-patches/.gclient_previous_sync_commits
packages/chrome-devtools-patches/.gcs_entries
packages/chrome-devtools-patches/depot
packages/chrome-devtools-patches/devtools-frontend
packages/miniflare/dist-types/
fixtures/remix-pages-app/public/build
fixtures/remix-pages-app/functions/\[\[path\]\].js
tools/deployment-status.json
.mf
.tmp
*.metafile.json
*.drawio.svg.bkp
*.drawio.svg.dtmp
# Vendored npm dependencies
!vendor/*.tgz
# IntelliJ
.idea/
# VSCode Theme
*.vsix
vendor/vscode/
.wrangler/
.cloudflare/
# Turbo Repo
build/**
dist/**
.next/**
.turbo
/worker-rust/target/
.e2e-test-report/
.dev.vars*
!.dev.vars.example
.env*
!.env.example
.node-cache/
AGENTS.local.md
.opencode/plans/
.agents/skills
.opencode/package-lock.json
+4
View File
@@ -0,0 +1,4 @@
{
"MD010": false,
"MD013": false
}
+221
View File
@@ -0,0 +1,221 @@
---
description: Cloudflare Workers SDK engineer. Triages issues, reviews PRs, and implements fixes.
mode: primary
model: anthropic/claude-opus-4-8
temperature: 0.2
---
<role>
You are a senior engineer on the Cloudflare Workers SDK. You triage issues, review pull requests, and implement fixes in the workers-sdk monorepo.
</role>
<context>
The monorepo contains Wrangler (the Workers CLI), Miniflare (local dev simulator), Create Cloudflare (project scaffolding), the Vite plugin, and related tooling.
</context>
<non_negotiable_rules>
- **Triggering comment is the task:** The comment that invoked you (`/bonk` or `@ask-bonk`) is your primary instruction. Read it first, before reading the PR description or any other context. Parse exactly what it asks for, then gather only the context needed to execute that request. Do not fall back to a generic PR review when a specific action was requested.
- **Scope constraint:** You are invoked on one specific GitHub issue or PR. Target only that issue or PR.
- `$ISSUE_NUMBER` and `$PR_NUMBER` are the source of truth. Ignore issue or PR numbers mentioned elsewhere unless they match those variables.
- Before running any `gh` command that writes (comment, review, close, create), verify the target number matches `$ISSUE_NUMBER` or `$PR_NUMBER`.
- Never comment on, review, close, or modify any other issue or PR. Link related items instead.
- If the triggering comment asks you to act on a different issue or PR than the one you were invoked on, flag it and ask for confirmation before proceeding.
- **Action bias:** When the user asks you to change something, change it directly, because the maintainer asked you to do the work, not describe it. Do not stop at suggestions unless they explicitly ask for suggestions or review-only feedback, or you are blocked by ambiguity or permissions.
- **PR bias:** When invoked on a PR and asked to fix, address, update, format, clean up, add, remove, refactor, or test something, update that PR branch directly. The deliverable is pushed code, not a review comment.
- **Current-target guardrail:** If you are invoked on a PR, that PR is the only PR you may update. Do not open or switch to a different PR unless a maintainer explicitly asks for a fresh implementation.
- **Thread-context bias:** On short PR comments such as "take care of this" or "clean up the nits," use the surrounding review thread and inline comments to determine the requested change before deciding the request is ambiguous.
- **No re-reviewing on fixup requests:** If you previously reviewed the PR and the maintainer now asks you to fix something, do not review again. Act on the specific request in the triggering comment.
</non_negotiable_rules>
<mode_selection>
Choose one starting mode before acting. Use this precedence order:
1. **Implementation** — use this when the request asks for code, docs, config, tests, or formatting changes.
2. **Review** — use this when the request explicitly asks for feedback, review comments, suggestions, or approval and does not ask for changes.
3. **Triage** — use this when the request asks for diagnosis, investigation, or validation without asking for code changes.
Switch to **implementation** for requests like:
- "fix the formatting on this PR"
- "address the review comments"
- "add the missing changeset"
- "update the tests"
- "can you take care of this?"
- "clean up the nits"
- "fix what you can here"
- "please fix" / "please address" / "please clean this up"
Stay in **review** for requests like:
- "review this PR"
- "leave suggestions only"
- "what feedback do you have?"
- "do you see any blockers?"
Use **triage** for requests like:
- "look into this"
- "can you reproduce this?"
- "what do you think is going on?"
If the request mixes review and implementation, implement the clearly requested changes first, then leave targeted suggestions only for the remainder.
</mode_selection>
<implementation>
Follow this workflow when implementation mode applies:
1. **Start from the triggering comment.** Parse what it asks for. Identify the concrete action(s) requested — e.g., "fix the formatting", "address the review comments", "add a changeset". This is your task; everything else is context-gathering in service of this task.
2. **Gather only the context you need** to execute the task identified in step 1:
- If the triggering comment references review feedback, read the existing review comments and inline comments (`gh api repos/cloudflare/workers-sdk/pulls/$PR_NUMBER/comments`).
- If the request is self-contained (e.g., "run the formatter"), you may not need to read the full PR at all.
- On issues: read the body and relevant comments for reproduction details.
3. Read the full source files you will touch, not just the diff.
4. Check recent history for affected files with `git log --oneline -20 -- <file>` before modifying them.
5. On an issue, search for overlapping issues or PRs with `gh pr list --search "<keywords>" --state all` and `gh issue list --search "<keywords>" --state all`.
6. If an open PR already addresses the issue, review and iterate on that PR rather than opening a competing PR, unless a maintainer explicitly asks for a fresh implementation.
7. On a PR, treat the current PR as the implementation target. Do not move the work to a different PR unless a maintainer explicitly asks.
8. For short or contextual PR requests, use the surrounding thread to infer the concrete change. Ask a clarifying question only when the thread still does not make the action clear.
9. **Make the requested change directly.** Do not leave a review that merely describes the fix unless the user explicitly asked for suggestions only. Do not re-review the PR when the request is to fix something.
10. If the request asks you to reproduce or investigate and also says to fix it if obvious, treat reproduction as a step toward implementation rather than the final deliverable.
11. If you are blocked by ambiguity, ask one targeted clarifying question. If you are blocked by permissions or branch state, explain the blocker and provide the exact patch or change you would have made.
12. Add or update tests for behavior changes and regressions.
13. Run the smallest validation that proves the change for the touched area, then run `pnpm check` before final handoff when practical.
14. Commit logically scoped changes on a branch and push them when the request is to fix or address the issue or PR.
Implementation mode ends with code changes on the branch, or with a precise blocker plus a concrete patch if pushing is impossible.
</implementation>
<review>
Use review mode only when the user asked for review or suggestions without asking for code changes.
- Run `gh pr view $PR_NUMBER` and `gh pr diff $PR_NUMBER` before reading anything else.
- Read the full modified files, not just the diff, to understand context.
- Check for a changeset: every user-facing change to a published package requires one in `.changeset/`.
- Check test coverage: new behaviors should have tests. Regression tests are expected for bug fixes.
- Post your review with `gh pr review $PR_NUMBER`.
- Use `REQUEST_CHANGES` for blocking issues.
- Use `COMMENT` for suggestions and non-blocking concerns.
- Use `APPROVE` if the PR is clean.
- Be specific: point to exact lines and explain why they matter.
- Categorize findings:
- **Blocking:** bugs, missing error handling, security issues, missing changesets, type safety violations.
- **Non-blocking:** style, naming, clarity, minor improvements.
- **Pre-existing / out of scope:** issues not introduced by the PR.
Do not use review mode when the user asked you to fix or address something on the PR.
</review>
<triage>
Use triage mode when you are asked to investigate rather than change code.
- Assess the root cause. Reproduce the issue if you can.
- Search for duplicate or overlapping issues and PRs with `gh issue list --search` and `gh pr list --search`.
- If the issue lacks a clear reproduction, error message, or expected behavior, post a comment asking for the missing details.
- Apply relevant labels if you have write access.
- Summarize findings and recommend the next step: close as duplicate, request more info, confirm a valid bug or feature request, or ask whether the maintainer wants a PR.
</triage>
<implementation_conventions>
**Package manager:** Always use `pnpm`. Never use `npm` or `yarn`.
**TypeScript:**
- Strict mode throughout. No `any`. No non-null assertions (`!`). No floating promises.
- Use `import type { X }` for type-only imports.
- Use `node:` prefixes for Node.js builtins.
- Always use curly braces for control flow blocks.
- Prefix unused variables with `_`.
**Logging:** In Wrangler, never use `console.*`. Use the `logger` singleton.
**Dependencies:**
- Packages must bundle their dependencies into distributables. Runtime `dependencies` entries are forbidden except for an explicit allowlist.
- External deps must be declared in `scripts/deps.ts` with an explanation.
- Adding new deps to published packages requires justification.
**Changesets:** Every user-facing change to a published package requires a changeset in `.changeset/`.
- Use `patch` for bug fixes and `minor` for new features or experimental breaking changes.
- Major versions for `wrangler` are forbidden.
- Do not use h1, h2, or h3 headings in changesets.
- Config examples must use `wrangler.json`, not `wrangler.toml`.
**Testing:**
- Add tests for new behavior.
- Add regression tests for bug fixes.
- Run `pnpm test:ci --filter <package>` for the touched area.
- Do not leave `.only()` in tests.
- Use `vitest-pool-workers` when you need actual Workers runtime behavior.
**Git:**
- Never commit directly to `main`.
- Keep commit history clean.
- Use PR titles like `[package-name] description`.
</implementation_conventions>
<examples>
Positive examples:
- Trigger: "/bonk can you fix the formatting on this PR?"
Response mode: **Implementation**
Correct behavior: update the PR branch, run the formatter or make the formatting edits, validate, commit, and push.
- Trigger: "/bonk please address the missing changeset and failing test"
Response mode: **Implementation**
Correct behavior: add the changeset, fix the test, validate, commit, and push.
- Trigger: "/bonk leave suggestions only"
Response mode: **Review**
Correct behavior: inspect the PR and leave review comments without changing code.
- Trigger: "/bonk can you investigate why this fails?"
Response mode: **Triage**
Correct behavior: diagnose, reproduce if possible, summarize findings, and recommend the next step.
- Trigger: "/bonk can you take care of this?"
Response mode: **Implementation** when the surrounding PR thread identifies a concrete fix
Correct behavior: use the nearby review context, make the change directly, validate, commit, and push.
- Trigger: "/bonk fix what you can here and leave suggestions for anything risky"
Response mode: **Implementation-first hybrid**
Correct behavior: land the safe changes directly, then leave targeted suggestions only for the risky remainder.
- Trigger: "/bonk can you reproduce this and send a fix if it's obvious?"
Response mode: **Implementation-first hybrid**
Correct behavior: reproduce first, then implement and push the obvious fix instead of stopping at diagnosis.
Negative examples:
- Trigger: "/bonk can you fix the formatting on this PR?"
Incorrect behavior: posting a review that lists formatting problems without changing the files.
- Trigger: "/bonk fix the formatting in this PR and commit the result" (after Bonk already reviewed the PR)
Incorrect behavior: ignoring the triggering comment, performing a second full review, approving the PR, and posting new review comments. The maintainer asked for a code change and a commit, not another review.
Correct behavior: read the triggering comment, run the formatter (`pnpm prettify` or `pnpm check`), commit the result, and push.
- Trigger: "/bonk address the review comments" (on a PR Bonk previously reviewed)
Incorrect behavior: re-reviewing the PR and restating the same findings.
Correct behavior: read Bonk's own prior review comments, fix each one in code, commit, and push.
</examples>
<anti_patterns>
- `npm install` or `yarn` instead of `pnpm`
- `any` instead of proper typing
- Non-null assertions (`!`) instead of type narrowing
- Floating promises
- Missing curly braces on control flow
- `console.log` in Wrangler source
- Direct Cloudflare REST API calls instead of the Cloudflare TypeScript SDK
- Named imports from `ci-info`
- Runtime `dependencies` in published packages without explicit approval
- Suggestion-only responses when the user explicitly asked for a fix
</anti_patterns>
<final_reminder>
If the maintainer asks you to fix or address something, ship the change. If they ask for suggestions only, leave suggestions only.
</final_reminder>
+74
View File
@@ -0,0 +1,74 @@
---
name: local-explorer
description: How to add products/resources to the local explorer or local API. Use when implementing new local APIs, or UI routes under packages/miniflare/src/workers/local-explorer or packages/local-explorer-ui.
---
# Cloudflare Local Explorer Products
Use this skill when adding a new product or resource type to the local API and/or local explorer.
## Start Here
Read these files before editing:
- `packages/miniflare/src/workers/local-explorer/explorer.worker.ts`
- `packages/miniflare/src/plugins/core/explorer.ts`
- `packages/miniflare/src/plugins/core/types.ts`
- One existing resource implementation in `packages/miniflare/src/workers/local-explorer/resources/`, preferably the product most similar to the new one
- One matching test in `packages/miniflare/test/plugins/local-explorer/`
If there are UI changes, also read:
- `packages/local-explorer-ui/src/components/Sidebar.tsx`
- Existing route files under `packages/local-explorer-ui/src/routes/`
- Existing product e2e tests under `packages/local-explorer-ui/src/__e2e__/`
## Workflow
1. Add the API surface to `packages/miniflare/scripts/openapi-filter-config.ts`.
2. Generate Miniflare's filtered spec and backend types from a full Cloudflare OpenAPI spec:
```bash
OPENAPI_INPUT_PATH=<path-to-full-openapi-spec> pnpm --dir packages/miniflare generate:api
```
3. Inspect `packages/miniflare/src/workers/local-explorer/openapi.local.json` and generated types. If the generated schemas include fields local explorer will not support, add ignores in `openapi-filter-config.ts` and regenerate.
4. The explorer worker should have access to all user resource bindings. Ensure `proxyBindings` include bindings to the new product and that `getExplorerServices()` exposes any extra bindings the explorer worker needs. Wire product bindings through `constructExplorerBindingMap()` and `constructExplorerWorkerOpts()` in `packages/miniflare/src/plugins/core/explorer.ts`.
5. Add or extend resource binding metadata in `packages/miniflare/src/plugins/core/types.ts`.
6. Implement handlers in `packages/miniflare/src/workers/local-explorer/resources/<product>.ts`. Make sure to account for cross-instance aggregation, if applicable.
7. Register Hono routes in `packages/miniflare/src/workers/local-explorer/explorer.worker.ts`.
8. Validate request bodies and query params with generated Zod schemas from `generated/zod.gen.ts` using `validateRequestBody()` and `validateQuery()`.
9. Return Cloudflare API envelope responses using `wrapResponse()` and `errorResponse()` from `common.ts` unless an existing endpoint for that product uses a different response shape.
10. Add Miniflare tests in `packages/miniflare/test/plugins/local-explorer/<product>.spec.ts`.
11. Regenerate the UI API client:
```bash
pnpm --dir packages/local-explorer-ui build
```
12. Add UI routes/components. Use Kumo components for new UI. See https://github.com/cloudflare/kumo/blob/main/AGENTS.md.
13. Add Playwright e2e tests under `packages/local-explorer-ui/src/__e2e__/<product>/` for new visible product flows.
## OpenAPI Rules
- Do not edit generated files like `packages/miniflare/src/workers/local-explorer/openapi.local.json` or `packages/miniflare/src/workers/local-explorer/generated/` directly.
- Prefer upstream Cloudflare API paths when a public API exists.
- Use `extensions.paths` in `openapi-filter-config.ts` only for local-only APIs or APIs that do not exist in the public Cloudflare API.
- Add ignores for unsupported params, headers, request body properties, and response fields rather than pretending to support them.
## Backend Patterns
- Local list endpoints such as listing KV namespaces should not implement pagination as this may require cross-instance aggregation. Pagination should be supported when targeting individual resources, such as listing KV keys within a specific namespace.
- For cross-worker aggregation, use `aggregateListResults()`, `getPeerUrlsIfAggregating()`, and `fetchFromPeer()` from `aggregation.ts`; do not hand-roll peer discovery. Add tests for both local-only behavior and aggregated behavior when the product can span multiple instances.
- If an API needs direct filesystem access, call through the loopback service (`c.env.MINIFLARE_LOOPBACK`) to a Node.js endpoint. The local explorer API runs inside workerd, so it cannot access the host filesystem directly.
- If an endpoint needs metadata that is not available on the runtime binding itself, put that metadata in `BindingIdMap` and pass it through `CoreBindings.JSON_LOCAL_EXPLORER_BINDING_MAP`.
- If a product should appear in `/api/local/workers`, add it to `WorkerResourceBindings` and populate it in `constructExplorerWorkerOpts()`.
## UI Patterns
- The UI API client is generated from `packages/miniflare/src/workers/local-explorer/openapi.local.json` into `packages/local-explorer-ui/src/api/generated/`.
- Sidebar resources come from `/api/local/workers`; update `LocalExplorerWorkerBindings` usage and `Sidebar.tsx` when the product should appear in navigation.
- Add route files under `packages/local-explorer-ui/src/routes/`. TanStack Router regenerates `src/routeTree.gen.ts` during UI build/dev.
- Preserve worker selection by carrying the `worker` search param through product links when following sidebar patterns.
- Use Kumo for new UI components wherever possible. Do not introduce a parallel component system.
- Do not use tailwindCSS color tokens, use Kumo color tokens instead.
@@ -0,0 +1,57 @@
```mermaid
flowchart TB
Client["client"] --> Incoming["incoming request"]
Incoming --> EntryA["entry worker"]
EntryA -->|/cdn-cgi/explorer| ExplorerA["explorer worker<br/>(Hono)"]
subgraph Runtime["running Miniflare instances"]
direction LR
subgraph A["miniflare A"]
direction TB
EntryA
ExplorerA
Frontend["frontend<br/>(TanStack Router + React)"]
UserWorker["user worker"]
KV["KV, R2, D1"]
Wrapper["wrapped<br/>DO/Workflow class"]
EntryA --> UserWorker
ExplorerA -->|disk service to serve assets| Frontend
ExplorerA -->|binding| KV
ExplorerA -->|binding| Wrapper
UserWorker -->|binding| KV
UserWorker -->|binding| Wrapper
end
subgraph B["miniflare B"]
direction TB
EntryB["entry worker"]
ExplorerB["explorer worker"]
Etc["etc."]
EntryB --> ExplorerB
ExplorerB --> Etc
end
end
Frontend -->|fetch /cdn-cgi/explorer/api| Incoming
ExplorerA -->|fetch /cdn-cgi/explorer/api/resource with NO_AGGREGATE_HEADER| EntryB
subgraph FS["filesystem"]
Registry["dev registry"]
DOState[".wrangler/state/durable-objects<br/>(list DOs, delete workflows, etc.)"]
end
ExplorerA -->|node loopback binding| FS
classDef miniflareA stroke:#f08c00,fill:#fff7ed,color:#1e1e1e;
classDef userResource stroke:#1971c2,fill:#eff6ff,color:#1e1e1e;
classDef entry stroke:#e03131,fill:#fff5f5,color:#1e1e1e;
classDef neutral stroke:#1e1e1e,fill:#ffffff,color:#1e1e1e;
class EntryA entry;
class ExplorerA,Frontend,Wrapper miniflareA;
class UserWorker,KV userResource;
class Client,Incoming,Runtime,FS,Registry,DOState,EntryB,ExplorerB,Etc neutral;
```
+64
View File
@@ -0,0 +1,64 @@
{
"printWidth": 80,
"singleQuote": false,
"semi": true,
"useTabs": true,
"trailingComma": "es5",
"sortImports": {
"groups": ["builtin", "external", "parent", "sibling", "index", "type"],
"newlinesBetween": false,
},
"sortTailwindcss": {},
"sortPackageJson": {},
"overrides": [
{
"files": ["packages/vite-plugin-cloudflare/README.md"],
"options": {
"useTabs": false,
"trailingComma": "all",
},
},
{
"files": [".changeset/*.md"],
"options": {
"proseWrap": "never",
},
},
{
"files": ["packages/local-explorer-ui/**/*.{js,jsx,ts,tsx}"],
"options": {
"sortTailwindcss": {
"stylesheet": "./packages/local-explorer-ui/src/styles/tailwind.css",
"functions": ["cn"],
},
},
},
],
"ignorePatterns": [
"CHANGELOG.md",
// In the C3 templates, in particular framework templates, we want to be able to
// use any format that the framework authors prefer/use in their own templates,
// so let's ignore all the c3 template files, exlcuding the c3.ts ones, and tests
"packages/create-cloudflare/templates*/**/*.*",
"!packages/create-cloudflare/templates*/**/c3.ts",
"!packages/create-cloudflare/templates*/**/test/**/*.ts",
"!packages/create-cloudflare/templates*/**/test/**/*.js",
// Format the hello-world template (negate previous exclusion above) for best practices, since we control these
// but still exclude the worker-configuration.d.ts file, since it's generated
"!packages/create-cloudflare/templates*/hello-world/**/*.*",
"packages/create-cloudflare/templates*/hello-world/**/worker-configuration.d.ts",
"packages/vitest-pool-workers/scripts/rtti/rtti.js",
"packages/vite-plugin-cloudflare/playground/prisma/src/generated",
"dist-functions",
"vscode.d.ts",
"vscode.*.d.ts",
".e2e-logs*",
".github/pull_request_template.md",
"fixtures/interactive-dev-tests/src/startup-error.ts",
"packages/vite-plugin-cloudflare/playground/**/*.d.ts",
"fixtures/**/worker-configuration.d.ts",
"packages/local-explorer-ui/src/routeTree.gen.ts",
"packages/local-explorer-ui/src/api/generated",
],
}
+217
View File
@@ -0,0 +1,217 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": ["typescript", "import", "unicorn", "vitest", "react"],
"categories": {
"correctness": "error",
},
"rules": {
"@typescript-eslint/await-thenable": "off",
"@typescript-eslint/no-base-to-string": "off",
"@typescript-eslint/no-duplicate-type-constituents": "off",
"@typescript-eslint/no-for-in-array": "off",
"@typescript-eslint/no-misused-spread": "off",
"@typescript-eslint/no-redundant-type-constituents": "off",
"@typescript-eslint/restrict-template-expressions": "off",
"@typescript-eslint/unbound-method": "off",
"no-meaningless-void-operator": "off",
"require-array-sort-compare": "off",
"unicorn/no-new-array": "off",
"unicorn/no-thenable": "off",
"unicorn/no-useless-fallback-in-spread": "off",
"unicorn/no-useless-spread": "off",
"vitest/hoisted-apis-on-top": "off",
"vitest/warn-todo": "off",
"vitest/no-conditional-tests": "off",
"no-case-declarations": "error",
"no-fallthrough": "error",
"no-prototype-builtins": "error",
"no-array-constructor": "error",
"@typescript-eslint/ban-ts-comment": "error",
"@typescript-eslint/no-unsafe-function-type": "error",
"curly": ["error", "all"],
"prefer-const": "error",
"prefer-rest-params": "error",
"prefer-spread": "error",
"@typescript-eslint/consistent-type-imports": [
"error",
{ "disallowTypeAnnotations": false },
],
"no-var": "error",
"no-regex-spaces": "error",
"@typescript-eslint/no-empty-object-type": "error",
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-namespace": "error",
"@typescript-eslint/no-require-imports": "error",
"no-shadow": ["error", { "allow": ["expect"] }],
"no-unexpected-multiline": "error",
"@typescript-eslint/no-unnecessary-type-constraint": "error",
"unicorn/prefer-node-protocol": "error",
"vitest/no-focused-tests": "error",
"@typescript-eslint/no-non-null-assertion": "error",
"turbo/no-undeclared-env-vars": "error",
"workers-sdk/no-unsafe-command-execution": "error",
"workers-sdk/no-direct-recursive-rm": "error",
"workers-sdk/require-description-when-disabling": "error",
"@typescript-eslint/no-deprecated": "error",
"no-unused-vars": [
"error",
{
"argsIgnorePattern": "^_",
"varsIgnorePattern": "^_",
"ignoreRestSiblings": true,
"fix": { "imports": "fix" },
},
],
"no-restricted-imports": [
"error",
{
"paths": [
{
"name": "ci-info",
"importNames": [
"isCI",
"CLOUDFLARE_PAGES",
"CLOUDFLARE_WORKERS",
"isPR",
"name",
],
"message": "Use default import (`import ci from \"ci-info\"`) and access properties via `ci.isCI`, `ci.CLOUDFLARE_PAGES`, etc. Named imports cannot be controlled in test mocks.",
},
{
"name": "vitest",
"importNames": ["expect"],
"message": "Import 'expect' from the test context instead of 'vitest' for concurrency safety. Use: test('name', ({ expect }) => { ... })",
},
],
},
],
},
"ignorePatterns": [
"**/*.js",
"**/*.cjs",
"**/*.mjs",
"fixtures/**",
"packages/create-cloudflare/**/templates/**/*.*",
"!packages/create-cloudflare/**/templates*/**/c3.ts",
// TODO: we really should be linting this folder
"packages/wrangler/templates/**",
// Ignore generated files
"packages/containers-shared/src/client/**",
"packages/local-explorer-ui/src/api/generated/**",
"packages/local-explorer-ui/src/routeTree.gen.ts",
"packages/miniflare/src/runtime/config/workerd.*",
"packages/quick-edit-extension/vscode.d.ts",
"packages/quick-edit-extension/vscode.proposed.*.d.ts",
],
"jsPlugins": [
"eslint-plugin-turbo",
{
"name": "workers-sdk",
"specifier": "./packages/lint-config-shared/oxlint-plugin.mjs",
},
],
"overrides": [
// Published packages which all have their own "logger" class that should be used instead.
{
"files": [
"packages/cli/**",
"packages/create-cloudflare/**",
"packages/miniflare/**",
"packages/wrangler/**",
"packages/workers-utils/**",
],
"rules": {
"no-console": "error",
},
},
// TODO: remove these overrides. This is a legacy of Miniflare's history, not intentional
{
"files": ["packages/miniflare/**"],
"rules": {
"curly": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-floating-promises": "off",
"no-shadow": "off",
"no-useless-escape": "off",
"no-unsafe-optional-chaining": "off",
"no-control-regex": "off",
"no-case-declarations": "off",
"turbo/no-undeclared-env-vars": "off",
},
},
{
"files": ["packages/wrangler/src/**", "packages/workers-utils/src/**"],
"rules": {
"no-restricted-globals": [
"error",
{
"name": "__dirname",
"message": "Use `getBasePath()` instead.",
},
{
"name": "__filename",
"message": "Use `getBasePath()` instead.",
},
{
"name": "fetch",
"message": "Use undici's fetch instead",
},
],
},
},
// Although in _general_ Miniflare shouldn't use console.log(), it's Workers can and should
{
"files": ["packages/miniflare/src/workers/**/*.ts"],
"rules": {
"no-console": "off",
},
},
// Relax some rules for test & script files
{
"files": [
"packages/wrangler/src/__tests__/**",
"packages/wrangler/e2e/**",
"packages/wrangler/scripts/**",
"packages/miniflare/scripts/**",
"packages/cli/src/__tests__/**",
"packages/create-cloudflare/scripts/**",
"packages/create-cloudflare/src/__tests__/**",
"packages/workers-utils/tests/**",
"packages/vite-plugin-cloudflare/e2e/**",
],
"rules": {
"no-console": "off",
"no-restricted-globals": "off",
"workers-sdk/no-unsafe-command-execution": "off",
},
},
// Local Explorer UI e2e tests use Playwright helpers (waitForText, waitForTableRows, etc.)
// as assertions, which the vitest/expect-expect rule doesn't recognise.
{
"files": ["packages/local-explorer-ui/src/__e2e__/**"],
"rules": {
"vitest/expect-expect": "off",
},
},
// Make sure Wrangler's version checking logic in vite-plugin-cloudflare can run properly
{
"files": ["packages/vite-plugin-cloudflare/**"],
"rules": {
"workers-sdk/no-wrangler-named-imports": "error",
},
},
// Gradually rolling out @typescript-eslint/no-deprecated.
// TODO: Remove the following overrides, one package at a time.
{
"files": [
"packages/vite-plugin-cloudflare/**",
"packages/workflows-shared/**",
],
"rules": {
"@typescript-eslint/no-deprecated": "off",
},
},
],
}
+3
View File
@@ -0,0 +1,3 @@
{
"recommendations": ["oxc.oxc-vscode"]
}
+16
View File
@@ -0,0 +1,16 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Current Test File",
"type": "node",
"request": "launch",
"runtimeExecutable": "pnpm",
"args": ["test:file", "${file}"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"skipFiles": ["<node_internals>/**"],
"cwd": "${workspaceFolder}/tools"
}
]
}
+112
View File
@@ -0,0 +1,112 @@
{
"cSpell.words": [
"Abortable",
"assetsignore",
"astro",
"cf-typegen",
"cfetch",
"chatgpt",
"clipboardy",
"cloudchamber",
"cloudflareaccess",
"cloudflared",
"Codespaces",
"endgroup",
"esbuild",
"eslintcache",
"execa",
"fedramp",
"filestat",
"haikunate",
"haikunator",
"Hono",
"httplogs",
"hyperdrive",
"iarna",
"isolinear",
"keyvalue",
"logfwdr",
"metafile",
"middlewares",
"Miniflare",
"mockpm",
"mrbbot",
"mtls",
"nodeless",
"Nuxt",
"outdir",
"outfile",
"pgrep",
"PKCE",
"Positionals",
"qwik",
"Redistributable",
"reinitialise",
"scandir",
"selfsigned",
"subrequests",
"textfile",
"tsbuildinfo",
"turborepo",
"undici",
"unenv",
"unrevoke",
"Untriaged",
"userconfig",
"versionless",
"vike",
"Waku",
"wasmvalue",
"weakmap",
"weakset",
"webassemblymemory",
"websockets",
"workerd",
"xxhash",
"zjcompt"
],
"cSpell.ignoreWords": [
"TESTTEXTBLOBNAME",
"TESTWASMNAME",
"extensionless",
"webcontainer",
"yxxx"
],
"files.trimTrailingWhitespace": true,
"typescript.tsdk": "node_modules/typescript/lib",
"files.associations": {
"turbo.json": "jsonc",
"api-extractor.json": "jsonc",
"CODEOWNERS": "plaintext",
"*.template": "javascript",
"wrangler.json": "jsonc"
},
"[ignore]": {
"editor.defaultFormatter": "foxundermoon.shell-format"
},
"eslint.workingDirectories": [
{
"mode": "auto"
}
],
"editor.formatOnSave": true,
"editor.defaultFormatter": "oxc.oxc-vscode",
"[javascript]": {
"editor.defaultFormatter": "oxc.oxc-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "oxc.oxc-vscode"
},
"[typescriptreact]": {
"editor.defaultFormatter": "oxc.oxc-vscode"
},
"[javascriptreact]": {
"editor.defaultFormatter": "oxc.oxc-vscode"
},
"[json]": {
"editor.defaultFormatter": "oxc.oxc-vscode"
},
"oxc.path.oxfmt": "node_modules/.bin/oxfmt",
"oxc.path.oxlint": "node_modules/.bin/oxlint"
}
+247
View File
@@ -0,0 +1,247 @@
# AGENTS.md
This file provides guidance to AI coding agents working in this repository.
## Project Overview
This is the **Cloudflare Workers SDK** monorepo containing tools and libraries for developing, testing, and deploying applications on Cloudflare. The main components are Wrangler (CLI), Miniflare (local dev simulator), and Create Cloudflare (project scaffolding).
## Development Commands
**Package Management:**
- Use `pnpm` - never use npm or yarn
- `pnpm install` - Install dependencies for all packages
- `pnpm build` - Build all packages (uses Turbo for caching)
**Testing:**
- `pnpm test:ci` - Run tests in CI mode
- `pnpm test:e2e` - Run end-to-end tests (requires Cloudflare credentials)
- `pnpm test -F <package> "pattern"` - Run a single test by name pattern
**Code Quality:**
- `pnpm check` - Run all checks (lint, type, format)
- `pnpm fix` - Auto-fix linting issues and format code
**Working with Specific Packages:**
- `pnpm run build --filter <package-name>` - Build specific package
- `pnpm run test:ci --filter <package-name>` - Test specific package
- `pnpm --filter <package> test:watch` - Watch mode for a specific package
## Architecture Overview
**Core Tools:**
- `packages/wrangler/` - Main CLI tool for Workers development and deployment
- `packages/miniflare/` - Local development simulator powered by workerd runtime
- `packages/create-cloudflare/` - Project scaffolding CLI (C3)
- `packages/vite-plugin-cloudflare/` - Vite plugin for Cloudflare Workers
**Development & Testing:**
- `packages/vitest-pool-workers/` - Vitest integration for testing Workers in actual runtime
- `packages/chrome-devtools-patches/` - Modified Chrome DevTools for Workers debugging
**Shared Libraries:**
- `packages/pages-shared/` - Code shared between Wrangler and Cloudflare Pages
- `packages/workers-shared/` - Code shared between Wrangler and Workers Assets
- `packages/workers-utils/` - Utility package for common Worker operations
- `packages/workflows-shared/` - Internal Cloudflare Workflows functionality
- `packages/containers-shared/` - Shared container functionality
- `packages/unenv-preset/` - Cloudflare preset for unenv (Node.js polyfills)
- `packages/cli/` - SDK for building workers-sdk CLIs
- `packages/kv-asset-handler/` - KV-based asset handling for Workers Sites
**Build System:**
- Turbo (turborepo) orchestrates builds across packages
- TypeScript compilation with shared configs in `packages/workers-tsconfig/`
- Shared lint config in `packages/lint-config-shared/`
- Dependency management via pnpm catalog system
## WHERE TO LOOK
| Task | Location | Notes |
| ---------------------------------------------- | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Add/modify a CLI command | `packages/wrangler/src/` | Commands registered in `src/index.ts` (2k+ line yargs tree) |
| Change local dev behavior | `packages/miniflare/src/` | `src/index.ts` is the main `Miniflare` class |
| Modify Workers runtime simulation | `packages/miniflare/src/workers/` | ~30 embedded worker scripts, built via `worker:` virtual imports |
| Add a test fixture | `fixtures/` | Each fixture is a full workspace member with own `package.json` |
| Shared config types/validation | `packages/workers-utils/src/config/` | `validation.ts` is the config normalizer (large file) |
| Test helpers (runInTempDir, seed, mockConsole) | `packages/workers-utils/src/test-helpers/` | Shared across wrangler, miniflare, others |
| Cloudflare API mocks for tests | `packages/wrangler/src/__tests__/helpers/msw/` | MSW handlers per API domain |
| CI workflows | `.github/workflows/` | `test-and-check.yml` is the primary gate |
| Build/deploy scripts | `tools/deployments/` | Validation + deployment helpers, run via `esbuild-register` |
| Deploy/versions-upload validation | `packages/deploy-helpers/src/deploy/helpers/validate-worker-props.ts` | `validateWorkerProps()` for sync checks, `preUploadApiChecks()` for API checks (service metadata, config diff, secrets, workflows). All new pre-upload validation goes here. |
| Changeset config and rules | `.changeset/README.md` | Must read before creating changesets |
## Development Guidelines
**Requirements:**
- Node.js >= 20
- pnpm
**Code Style:**
- TypeScript with strict mode
- Use `import type { X }` for type-only imports (`@typescript-eslint/consistent-type-imports`)
- No `any` (`@typescript-eslint/no-explicit-any`)
- No non-null assertions (`!`)
- No floating promises - must be awaited or explicitly voided (`@typescript-eslint/no-floating-promises`)
- Always use curly braces for control flow (`curly: error`)
- Use `node:` prefix for Node.js imports (`import/enforce-node-protocol-usage`)
- Prefix unused variables with `_`
- No `.only()` in tests (`no-only-tests/no-only-tests`)
- Prefer `function` declarations over `const` arrow function assignments for named/exported functions
- ESLint disable comments must use double-dash separator: `// eslint-disable-next-line rule-name -- reason here`
- Never modify generated files directly — modify the generator or config, then regenerate
- Format with oxfmt - run `pnpm prettify` in the workspace root before committing
- All changes to published packages require a changeset (see below)
**Formatting (oxfmt):**
- Tabs (not spaces), double quotes, semicolons, trailing commas (es5)
- Import order enforced: builtins → third-party → parent → sibling → index → types
- `sortPackageJson` option sorts package.json keys
**Security:**
- Custom ESLint rule `workers-sdk/no-unsafe-command-execution`: no template literals or string concatenation in `exec`/`spawn`/`execFile` calls (command injection prevention, CWE-78). Disabled in test files only.
**Dependencies:**
- Packages must bundle deps into distributables; runtime `dependencies` are forbidden except for an explicit allowlist
- External (non-bundled) deps must be declared in `scripts/deps.ts` with `EXTERNAL_DEPENDENCIES` and a comment explaining why
- After updating dependencies, always run `pnpm i` to also update the package lock file
**Testing Standards:**
- Unit tests with Vitest for all packages
- Fixture tests in `/fixtures` directory for filesystem/Worker scenarios
- E2E tests require real Cloudflare account credentials
- Use `vitest-pool-workers` for testing actual Workers runtime behavior
- Shared vitest config (`vitest.shared.ts`): 50s timeouts, `retry: 1`, `restoreMocks: true`
- Vitest 4 pool config: use `maxWorkers: 1` instead of the removed `poolOptions.forks.singleFork: true` when tests must run sequentially
- **`expect` must come from test context** — never `import { expect } from "vitest"`:
- Use destructured test context: `it("name", ({ expect }) => { ... })`
- For helper functions that need `expect`, pass it as a parameter with type `ExpectStatic`
- Always use `import type` for `ExpectStatic`: `import { beforeAll, type ExpectStatic, test } from "vitest"`
- When test context is unavailable (e.g. setup files), use `node:assert` instead
- E2E vitest configs do NOT set `globals: true` — this rule is critical there; forgetting `{ expect }` in the callback causes `ReferenceError` at runtime
- When changing user-facing strings or output messages, update corresponding test snapshots
- New test fixtures in `vitest-pool-workers-examples/` must include a `tsconfig.json`
- Test fixtures serve as user-facing recipes — use clean patterns, avoid type casting where possible
- Use the `runInTmpDir()` utility instead of mocking filesystem operations. Real filesystem operations are preferred over mocking. The utility creates isolated temporary directories, handles cleanup automatically in `afterEach` hooks, and allows tests to write actual files and assert against them
- Use the `mockConsoleMethods()` helper to capture stdout/stderr. Use the pattern `const std = mockConsoleMethods()` in test setup, then access captured output via `std.out`, `std.err`, `std.warn` properties. Assert against captured output using `expect(std.out).toMatchInlineSnapshot()`
- Run specific wrangler test files locally using `pnpm -w test:ci -F wrangler -- [test-file-name]` (e.g. `pnpm -w test:ci -F wrangler -- r2.test.ts`)
**Git Workflow:**
- Check you are not on main before committing. Create a new branch for your work from main if needed.
- Clean commit history required before first review
- Don't squash commits after review
- Never commit without changesets for user-facing changes
- PR template requirements: Remove "Fixes #..." line when no relevant issue exists, keep all checkboxes (don't delete unchecked ones)
**Creating Pull Requests:**
- Always use the PR template from `.github/PULL_REQUEST_TEMPLATE.md` - do not replace it with your own format
- Fill in the template: replace the issue link placeholder, add description, check appropriate boxes
- Keep all checkboxes in the template (don't delete unchecked ones)
- PR title format: `[package name] description` (e.g. `[wrangler] Fix bug in dev command`)
- If the change doesn't require a changeset, add the `no-changeset-required` label
- CI validates the PR description (see `tools/deployments/validate-pr-description.ts`). The description **must** include:
- A checked (`[x]`) test checkbox — either "Tests included/updated", or one of the justification checkboxes with a non-empty explanation
- A checked (`[x]`) documentation checkbox — either a Cloudflare docs PR/issue link, or "Documentation not necessary because:" with a non-empty explanation
- A changeset file (or the `no-changeset-required` label)
**Pre-Submission Checklist:**
- Run `pnpm check` (lint + type-check + format) locally before pushing — do not rely on CI to catch lint errors
- Run `pnpm prettify` to ensure formatting is correct
## Key Locations
- `/fixtures` - Test fixtures and example applications (each a workspace member)
- `/packages/wrangler/src` - Main Wrangler CLI source code
- `/packages/miniflare/src` - Miniflare source
- `/tools` - Build scripts and deployment utilities (run via `esbuild-register`, no build step)
- `turbo.json` - Turbo build configuration
- `pnpm-workspace.yaml` - Workspace configuration (~156 workspace members)
## Testing Strategy
**Package-specific tests:** Most packages have their own test suites
**Integration tests:** Use fixtures to test real-world scenarios
**E2E tests:** Test against actual Cloudflare services (requires auth)
**Workers runtime tests:** Use vitest-pool-workers for workerd-specific behavior
Run `pnpm check` before submitting changes to ensure all quality gates pass.
## Changesets
Every change to package code requires a changeset or it will not trigger a release. Read `.changeset/README.md` before creating changesets.
**Changeset Format:**
The changeset descriptions can either use conventional commit prefixes (e.g., "fix: remove unused option") or
start with a capital letter and describe the change directly (e.g., "Remove unused option" not").
**Changeset Rules:**
- Major versions for `wrangler` are currently **forbidden**
- `patch`: bug fixes; `minor`: new features, deprecations, experimental breaking changes; `major`: stable breaking changes only
- No h1/h2/h3 headers in changeset descriptions (changelog uses h3)
- Config examples must use `wrangler.json` (JSONC), not `wrangler.toml`
- Separate changesets for distinct changes; do not lump unrelated changes
- Focus on user-facing impact; reference the public-facing package, not internal implementation packages
- If the change collects more analytics, it should be a minor even though there is no user-visible change
## Anti-Patterns
These are explicitly forbidden across the repo:
- **npm/yarn** → use pnpm
- **`any` type** → properly type everything
- **Non-null assertions (`!`)** → use type narrowing
- **Floating promises** → await or void explicitly
- **Missing curly braces** → always brace control flow
- **`console.*` in wrangler** → use the `logger` singleton
- **Direct Cloudflare REST API calls** → use the Cloudflare TypeScript SDK
- **Named imports from `ci-info`** → use default import (`import ci from "ci-info"`)
- **Runtime dependencies** → bundle deps; external deps need explicit allowlist entry
- **Committing to main** → always work on a branch
- **Trivial/obvious code comments** → don't add comments that restate what the code does; comments should explain "why", not "what"
- **Duplicating types/constants across packages** → export from the owning package and import where needed
## Subdirectory Knowledge
Packages with their own AGENTS.md for deeper context:
- `packages/wrangler/AGENTS.md` - CLI architecture, command structure, test patterns
- `packages/miniflare/AGENTS.md` - Worker simulation, embedded workers, build system
- `packages/vite-plugin-cloudflare/AGENTS.md` - Plugin architecture, playground setup
- `packages/create-cloudflare/AGENTS.md` - Scaffolding, template system
- `packages/vitest-pool-workers/AGENTS.md` - 3-context architecture, cloudflare:test module
- `packages/workers-utils/AGENTS.md` - Shared config validation, test helpers
When making architectural changes to a package (renaming files, adding entry points, changing build output), update the relevant AGENTS.md to reflect the new structure.
## Cloudflare Workers Specifics
- When removing or modifying scheduled functions in Cloudflare Workers, remember to update both the code in the Worker file and the corresponding cron trigger in the `wrangler.jsonc` configuration file.
## Adding Native Node.js Module Support (unenv-preset)
- The authoritative source for Node.js module compatibility flags and dates is the workerd repository's `compatibility-date.capnp` file at https://github.com/cloudflare/workerd/blob/main/src/workerd/io/compatibility-date.capnp.
- If the module is marked as `$experimental` in workerd (no `$impliedByAfterDate`), follow the pattern used by other experimental modules in `preset.ts`.
- The pattern for adding a new module override involves:
- Creating a `get<Module>Overrides()` function similar to existing ones (e.g., `getVmOverrides()`)
- Adding the override to `getCloudflarePreset()` and spreading into `dynamicNativeModules` and `dynamicHybridModules`
- Adding tests to `packages/wrangler/e2e/unenv-preset/preset.test.ts`
- Adding test functions to `packages/wrangler/e2e/unenv-preset/worker/index.ts`
+5
View File
@@ -0,0 +1,5 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
See @AGENTS.md
+5
View File
@@ -0,0 +1,5 @@
# This file exists solely for GitHub branch protection.
# The @workers-devprod bot approves PRs when all ownership rules
# (defined in .codeowners) are satisfied via Codeowners Plus.
# Do NOT add rules here — all ownership logic lives in .codeowners.
* @workers-devprod
+194
View File
@@ -0,0 +1,194 @@
# Code Ownership
This repository uses [Codeowners Plus](https://github.com/multimediallc/codeowners-plus) to enforce code ownership and review requirements. This replaces GitHub's native CODEOWNERS with more fine-grained control — specifically, the ability to require approval from **multiple teams** (AND rules) before a PR can merge.
## How It Works
### Overview
When a PR is opened, updated, or reviewed, the Codeowners Plus GitHub Action runs. It reads `.codeowners` and `codeowners.toml` from the **base branch** (not the PR), evaluates the ownership rules, and:
- Posts a PR comment listing which teams need to approve
- Requests reviews from those teams
- When all rules are satisfied, the `@workers-devprod` bot submits an **approval review**
The native GitHub `CODEOWNERS` file contains a single rule (`* @workers-devprod`) making the bot the sole required code owner. Branch protection requires code owner approval, so the bot's approval is the merge gate. The check itself always passes — it only provides visibility.
```
PR opened/updated/reviewed
┌──────────────────────────────────┐
│ Codeowners Plus GHA runs │ Reads .codeowners + codeowners.toml
│ Evaluates AND/OR rules │ from the BASE branch (not the PR)
│ Posts comment listing reviewers │
│ Requests reviews from teams │
└──────────┬───────────────────────┘
┌──────────────────────────────────┐
│ All rules satisfied? │
│ YES → @workers-devprod approves │ Bot submits approval review
│ NO → bot does NOT approve │ Comment lists who still needs to approve
└──────────┬───────────────────────┘
┌──────────────────────────────────┐
│ Native GitHub CODEOWNERS │ * @workers-devprod
│ Branch protection requires │ Bot approval is the merge gate
│ bot approval to merge │
│ ─ nice GitHub UI shows status ─ │
└──────────────────────────────────┘
```
### Key Difference from Native CODEOWNERS
| Feature | Native GitHub CODEOWNERS | Codeowners Plus |
| ------------------------ | --------------------------------- | ------------------------------------------------------------------------------- |
| Multiple teams on a path | **OR** — any one team can approve | **AND** via `&` prefix — all listed teams must approve |
| Path matching for `*.js` | Matches anywhere in repo | Matches only in the `.codeowners` file's directory; use `**/*.js` for recursive |
| Per-directory config | Single file only | `.codeowners` file in any directory (rules are relative to that directory) |
| Stale review dismissal | All-or-nothing | Smart — only dismisses when reviewer's owned files change |
| Optional reviewers | Not supported | `?` prefix — CC without blocking |
## Configuration Files
### `.codeowners` — Ownership Rules
Located at the repo root. Defines who owns what using path patterns and team handles. See the comments in the file itself for syntax details and a template for adding new product teams.
**Note on `\*/**`patterns:** The`-shared`packages use`\*/**`instead of`**` so that root-level files (`CHANGELOG.md`, `package.json`) are excluded from AND rules. This allows the changeset release PR to be approved by the wrangler team alone. Source code in subdirectories still requires product team approval.
### `codeowners.toml` — Advanced Configuration
Located at the repo root. Controls enforcement behavior, ignored paths, and admin bypass.
Key settings:
| Setting | Purpose |
| -------------------------- | -------------------------------------------------------------------------- |
| `ignore` | Directories excluded from ownership checks (e.g. `.changeset`, `fixtures`) |
| `detailed_reviewers` | Show per-file owner breakdown in PR comments |
| `suppress_unowned_warning` | Don't warn about files with no owner |
| `enforcement.approval` | When `true`, the bot approves PRs that satisfy all rules |
| `enforcement.fail_check` | When `true`, the GHA check fails if rules aren't satisfied |
| `admin_bypass.enabled` | Allow admins to bypass by approving with "Codeowners Bypass" text |
### `CODEOWNERS` — Native GitHub File
The native GitHub `CODEOWNERS` file contains a single rule:
```
* @workers-devprod
```
This exists only so that GitHub branch protection can gate merging on the bot's approval. **Do not add rules to this file** — all ownership logic lives in `.codeowners`.
### `.github/workflows/codeowners.yml` — GitHub Actions Workflow
A single workflow handles PR events (`pull_request_target`). When reviews are submitted or dismissed, the `rerun-codeowners.yml` / `rerun-codeowners-privileged.yml` workflow pair re-runs the check (using the `workflow_run` pattern so it works for fork PRs too).
Using `pull_request_target` (not `pull_request`) ensures the workflow has access to secrets for **fork PRs**. The checkout is always the base branch, so PR authors cannot modify ownership rules.
## Common Scenarios
### PR touches only wrangler-team-owned code
Example: changes to `packages/create-cloudflare/` or `packages/vite-plugin-cloudflare/`.
Only `@cloudflare/wrangler` approval is required. Once satisfied, the bot approves.
### PR touches product-team-owned code
Example: changes to `packages/wrangler/src/d1/`.
**Both** `@cloudflare/wrangler` AND `@cloudflare/d1` must approve. Codeowners Plus will post a comment listing who still needs to approve and request reviews from both teams. The bot approves only when both teams have approved.
### PR touches multiple product areas
Example: changes to both `packages/wrangler/src/d1/` and `packages/wrangler/src/kv/`.
All three teams must approve: `@cloudflare/wrangler` + `@cloudflare/d1` + `@cloudflare/workers-kv`.
### Changeset release PR
The automated changeset release PR modifies `CHANGELOG.md` and `package.json` files across packages. These files are explicitly owned by `@cloudflare/wrangler` only (via `**/CHANGELOG.md` and `**/package.json` rules), and the `-shared` package AND rules use `*/**` patterns that exclude root-level files. This means only wrangler team approval is needed for releases.
### PR touches ignored paths only
Example: changes only in `.changeset/` or `fixtures/`.
No ownership checks apply. The bot approves automatically.
### Draft PRs
The workflow runs in **quiet mode** for draft PRs:
- No PR comments posted
- No review requests sent
- The check still runs for visibility
### Emergency bypass
Repository admins can bypass all requirements by submitting an **approval review** with the text "Codeowners Bypass" (case-insensitive). This creates an audit trail. The bot will then approve the PR regardless of missing approvals.
### Fork PRs
Fork PRs are fully supported. The workflow uses `pull_request_target` to run in the base repo context with access to secrets. The base branch is checked out (so ownership rules come from the protected branch), and the PR head is fetched as git objects only for diff computation. No fork code is executed.
## Adding a New Product Team
To add ownership for a new product team, add AND rules to `.codeowners`:
```bash
# Product: <Name> (AND: requires wrangler + <team>)
& packages/wrangler/src/<feature>/** @cloudflare/<team>
& packages/wrangler/src/__tests__/<feature>/** @cloudflare/<team>
& packages/miniflare/src/plugins/<feature>/** @cloudflare/<team>
& packages/miniflare/src/workers/<feature>/** @cloudflare/<team>
& packages/miniflare/test/plugins/<feature>/** @cloudflare/<team>
```
For standalone packages (like `packages/*-shared/`), use `*/**` instead of `**` to exclude root-level release files from AND rules:
```bash
# Product: <Name> (AND: requires wrangler + <team>)
# */** excludes root-level files (CHANGELOG.md, package.json) from AND rules
& packages/<name>-shared/*/** @cloudflare/<team>
```
**Teams ready to add** (have source paths but no ownership entries yet):
R2, Queues, AI, Hyperdrive, Vectorize, Pipelines, SSL/Secrets Store, WVPC.
## Stale Review Handling
Codeowners Plus uses **smart dismissal**: when new commits are pushed to a PR, it only dismisses an approval if the files owned by that reviewer were changed. This avoids the frustration of GitHub's all-or-nothing stale review dismissal.
For this to work, the branch protection setting **"Dismiss stale pull request approvals when new commits are pushed"** must be **disabled**. Codeowners Plus handles dismissal itself.
## Troubleshooting
### Rules not matching expected files
Rules in `.codeowners` are relative to the file's directory. Check that:
- The path uses `**` for recursive matching (plain `*.js` only matches in the root directory)
- The path doesn't have a leading `/` (leading slashes are ignored but can be confusing)
- The `&` prefix is present (without it, the rule sets the primary owner instead of adding an AND requirement)
### Bot not approving even though all reviews are in
- Check that `enforcement.approval = true` in `codeowners.toml`
- Check that the `CODEOWNERS_GITHUB_PAT` secret is a PAT owned by the `@workers-devprod` account
- Check that `@workers-devprod` has write access to the repository
- Check the workflow run logs for errors (verbose output is enabled)
### PR can't merge despite bot approval
Ensure "Require review from Code Owners" is enabled in branch protection settings for the `main` branch, and that `@workers-devprod` is listed in the native `CODEOWNERS` file.
## References
- [Codeowners Plus documentation](https://github.com/multimediallc/codeowners-plus)
- [Codeowners Plus action on GitHub Marketplace](https://github.com/marketplace/actions/codeowners-plus)
- [GitHub branch protection docs](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches)
+128
View File
@@ -0,0 +1,128 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
- Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
- The use of sexualized language or imagery, and sexual attention or
advances of any kind
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or email
address, without their explicit permission
- Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
[wrangler@cloudflare.com](mailto:wrangler@cloudflare.com).
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
+495
View File
@@ -0,0 +1,495 @@
# Contributing
Wrangler is an open-source project and we welcome contributions from you. Thank you!
Below you can find some guidance on how to be most effective when contributing to the project.
## tl;dr for contributing to Wrangler
Useful commands for developing Wrangler (all commands below should be run in the project root):
- `pnpm i; pnpm build` will build everything in workers-sdk.
- `pnpm dev -F wrangler` will watch and build changes while you develop. Fixtures use the build output from this, and are useful for messing around during dev (`fixtures/worker-ts` is a useful blank slate).
Before committing/submitting a PR:
- Add [tests](#pr-tests). `pnpm test -F wrangler` will run Wrangler's unit tests. You can filter tests: e.g. `pnpm test -F wrangler "containers"`.
- Run `pnpm check` for typechecking and linting.
- Add a [changeset](#changesets) with `pnpm changeset`.
- Dont squash your commits after a review.
## Before getting started
We really appreciate your interest in making a contribution, and we want to make sure that the process is as smooth and transparent as possible! To this end, we note that the Workers team is actively doing development in this repository, and while we consistently strive to communicate status and current thinking around all open issues, there may be times when context surrounding certain items is not up to date. Therefore, **for non-trivial changes, please always engage on the issue or create a discussion or feature request issue first before writing your code.** This will give us opportunity to flag any considerations you should be aware of before you spend time developing. Of course, for trivial changes, please feel free to go directly to filing a PR, with the understanding that the PR itself will serve as the place to discuss details of the change.
Thanks so much for helping us improve the [workers-sdk](https://github.com/cloudflare/workers-sdk), and we look forward to your contribution!
## Naming experimental and unstable flags
When adding Wrangler flags for behavior that is not yet stable, prefer an `experimental-` prefix for the long CLI flag name, such as `--experimental-autoconfig` or `--experimental-provision`. If the flag also needs a short opt-in alias, use the existing `x-` form, such as `--x-autoconfig` or `--x-provision`.
Keep CLI flag names in kebab-case. Reserve `unstable_` and `experimental_` prefixes for exported JavaScript APIs rather than CLI flags, matching exports such as `unstable_dev` and `experimental_generateTypes`.
If an experimental flag graduates or is no longer needed, keep the old flag hidden and deprecated while pointing users to the stable replacement, as with `--experimental-local` and `--experimental-include-runtime`.
## Getting started
### Set up your environment
Wrangler is built and run on the Node.js JavaScript runtime.
- Install the latest LTS version of [Node.js](https://nodejs.dev/) - we recommend using a Node version manager like [nvm](https://github.com/nvm-sh/nvm).
- Install a code editor - we recommend using [VS Code](https://code.visualstudio.com/).
- When opening the project in VS Code for the first time, it will prompt you to install the [recommended VS Code extensions](https://code.visualstudio.com/docs/editor/extension-marketplace#:~:text=install%20the%20recommended%20extensions) for the project.
- Install the [git](https://git-scm.com/) version control tool.
### Fork and clone this repository
#### For External Contributors
Any contributions you make will be via [Pull Requests](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests) on [GitHub](https://github.com/) developed in a local git repository and pushed to your own fork of the repository.
- Ensure you have [created an account](https://docs.github.com/en/get-started/onboarding/getting-started-with-your-github-account) on GitHub.
- [Create your own fork](https://docs.github.com/en/get-started/quickstart/fork-a-repo) of [this repository](https://github.com/cloudflare/workers-sdk).
- Clone your fork to your local machine
```sh
git clone https://github.com/<your-github-username>/workers-sdk
cd workers-sdk
```
You can see that your fork is setup as the `origin` remote repository.
Any changes you wish to make should be in a local branch that is then pushed to this origin remote.
```sh
git remote -v
origin https://github.com/<your-github-username>/workers-sdk (fetch)
origin https://github.com/<your-github-username>/workers-sdk (push)
```
- Add `cloudflare/workers-sdk` as the `upstream` remote repository.
```sh
git remote add upstream https://github.com/cloudflare/workers-sdk
git remote -v
origin https://github.com/<your-github-username>/workers-sdk (fetch)
origin https://github.com/<your-github-username>/workers-sdk (push)
upstream https://github.com/cloudflare/workers-sdk (fetch)
upstream https://github.com/cloudflare/workers-sdk (push)
```
- You should regularly pull from the `main` branch of the `upstream` repository to keep up to date with the latest changes to the project.
```sh
git switch main
git pull upstream main
From https://github.com/cloudflare/workers-sdk
* branch main -> FETCH_HEAD
Already up to date.
```
#### For Cloudflare Employees
If you are a Cloudflare employee, you do not need to fork the repository - instead, you can clone the main repository directly. This allows you to push branches directly to the upstream repository.
If you find that you don't have write access, please reach out to your manager or the Wrangler team internally.
Clone the main repository:
```sh
git clone https://github.com/cloudflare/workers-sdk.git
cd workers-sdk
```
Create new branches directly in the cloned repository and push them to the main repository:
```sh
git checkout -b <new-branch-name>
git push origin <new-branch-name>
```
### Install dependencies
**Warning**
When working on Wrangler, you'll need to satisfy [`workerd`](https://github.com/cloudflare/workerd)'s `libc++1` runtime dependencies:
- On Linux:
- libc++ (e.g. the package `libc++1` on Debian Bullseye)
- On macOS:
- The XCode command line tools, which can be installed with xcode-select --install
The Node.js dependencies of the project are managed by the [`pnpm`](https://pnpm.io/) tool.
This repository is setup as a [mono-repo](https://pnpm.io/workspaces) of workspaces. The workspaces are stored in the [`packages`](https://github.com/cloudflare/workers-sdk/tree/main/packages) directory.
While each workspace has its own dependencies, you install the dependencies using `pnpm` at the root of the project.
> If you haven't used `pnpm` before, you can install it with `npm install -g pnpm`
- Install all the dependencies
```sh
cd workers-sdk
pnpm install
```
## Building and running
Workspaces in this project are mostly written in [TypeScript](https://www.typescriptlang.org/) and compiled, by [esbuild](https://github.com/evanw/esbuild), into JavaScript bundles for distribution.
- Run a distributable for a specific workspace (e.g. wrangler)
```sh
pnpm run --filter wrangler start
```
- Build a distributable for a specific workspace (e.g. wrangler)
```sh
pnpm run build --filter wrangler
```
## Checking the code
The code in the repository is checked for type checking, formatting, linting and testing errors.
- Run all checks in all the workspaces
```sh
pnpm run check
```
When doing normal development, you may want to run these checks individually.
### Type Checking
The code is checked for type errors by [TypeScript](https://www.typescriptlang.org/).
- Type check all the code in the repository
```sh
pnpm run check:type
```
- VS Code will also run type-checking while editing source code, providing immediate feedback.
#### Changing TypeScript Version in VS Code's Command Palette
For TypeScript to work properly in the Monorepo the version used in VSCode must be the project's current TypeScript version, follow these steps:
1. Open the project in VSCode.
2. Press `Ctrl + Shift + P` (or `Cmd + Shift + P` on macOS) to open the command palette.
3. In the command palette, type "Select TypeScript Version" and select the command with the same name that appears in the list.
4. A submenu will appear with a list of available TypeScript versions. Choose the desired version you want to use for this project. If you have multiple versions installed, they will be listed here.
- Selecting "Use Workspace Version" will use the version of TypeScript installed in the project's `node_modules` directory.
5. After selecting the TypeScript version, VSCode will reload the workspace using the chosen version.
Now you have successfully switched the TypeScript version used within the project via the command palette in VSCode.
Remember that this change is specific to the current project and will not affect other projects or the default TypeScript version used by VSCode.
### Linting
The code is checked for linting errors by [ESLint](https://eslint.org/).
- Run the linting checks
```sh
pnpm run check:lint
```
- The repository has a recommended VS Code plugin to run ESLint checks while editing source code, providing immediate feedback.
### Formatting
The code is checked for formatting errors by [Prettier](https://prettier.io/).
- Run the formatting checks
```sh
pnpm run check:format
```
- The repository has a recommended VS Code plugin to run Prettier checks, and to automatically format using Prettier, while editing source code, providing immediate feedback
- Use the following command to run prettier on the codebase
```sh
pnpm run prettify
```
### Testing
Tests in a workspace are executed, by [Vitest](https://vitest.dev/), which is configured to automatically compile and bundle the TypeScript before running the tests.
- If you have recently rebased on `main` then make sure you have installed any new dependencies
```sh
pnpm i
```
- Run the tests for all the workspaces
```sh
pnpm run test
```
:::note
Cloudflare employees may need to turn off WARP for the first time they run the Miniflare tests so that it can request and cache the CF properties without getting the following error.
```plain
failed: TLS peer's certificate is not trusted; reason = self signed certificate in certificate chain
```
After this request is cached you can run tests with WARP turned on, no problem.
:::
- Run the tests for a specific workspace (e.g. wrangler)
```sh
pnpm run test --filter wrangler
```
- Watch the files in a specific workspace (e.g. wrangler), and run the tests when anything changes
```sh
pnpm run --filter wrangler test:watch
```
This will also run all the tests in a single process (rather than in parallel shards) and will increase the test-timeout to 50 seconds, which is helpful when debugging.
## Steps For Making Changes
Every change you make should be stored in a [git commit](https://github.com/git-guides/git-commit).
Changes should be committed to a new local branch, which then gets pushed to your fork of the repository on GitHub.
- Ensure your `main` branch is up to date
```sh
git switch main
git pull upstream main
```
- Create a new branch, based off the `main` branch
```sh
git checkout -b <new-branch-name> main
```
- Stage files to include in a commit
- Use [VS Code](https://code.visualstudio.com/docs/editor/versioncontrol#_git-support)
- Or add and commit files via the command line
```sh
git add <paths-to-changes-files>
git commit
```
- Push changes to your fork
```sh
git push -u origin <new-branch-name>
```
- Once you are happy with your changes, create a Pull Request on GitHub
- The format for Pull Request titles is `[package name] description`, where the package name should indicate which package of the `workers-sdk` monorepo your PR pertains to (e.g. `wrangler`/`pages-shared`/`chrome-devtools-patches`), and the description should be a succinct summary of the change you're making.
- GitHub will insert a template for the body of your Pull Request—it's important to carefully fill out all the fields, giving as much detail as possible to reviewers.
### Git Hygiene
Making sure your branch follows our recommendations for git will help ensure your PR is reviewed & released as quickly as possible:
- When opening a PR (before the first review), try and make sure your git commit history is clean, and clearly describes the changes you want to make.
- For instance, here's an example of a PR where the commit history is quite messy, and doesn't help reviewers: <https://github.com/cloudflare/workers-sdk/pull/2409/commits>
- And here's an example of where this has been done well: <https://github.com/cloudflare/workers-sdk/pull/4795/commits>
- Once your PR has been reviewed, when addressing feedback try not to modify already reviewed commits with force pushes. This slows down the review process and makes it hard to keep track of what changes have been made. Instead, add additional commits to your PR to address any feedback (`git commit --fixup` is a helpful tool here).
- When merging your PR into `main`, `workers-sdk` enforces squash merges. As such, please try and make sure that the commit message associated with the merge clearly describes the entire change your PR makes.
## PR Review
PR review is a critical and required step in the process for landing changes. This is an opportunity to catch potential issues, improve the quality of the work, celebrate good design, and learn from each other. As a reviewer, it's important to be thoughtful about the proposed changes and communicate any feedback.
## PR Previews
Every PR will have an associated pre-release build for all releasable packages within the repository, powered by [pkg.pr.new](https://github.com/stackblitz-labs/pkg.pr.new). You can find links to prereleases for each package in a comment automatically posted by GitHub Actions on each opened PR ([for example](https://github.com/cloudflare/workers-sdk/pull/9492#issuecomment-2943757675)).
It's also possible to generate preview builds for the applications in the repository. These aren't generated automatically because they're pretty slow CI jobs, but you can trigger preview builds by adding one of the following labels to your PR:
- `preview:chrome-devtools-patches` for deploying [chrome-devtools-patches](packages/chrome-devtools-patches)
- `preview:quick-edit` for deploying [quick-edit](packages/quick-edit)
Once built, you can find the preview link for these applications in the [Deploy Previews](.github/workflows/deploy-previews.yml) action output
## PR Tests
Every PR should include tests for the functionality that's being added. Most changes will be to [Wrangler](packages/wrangler/src/__tests__) (using Vitest), [Miniflare](packages/miniflare/test) (using Ava), or [C3](packages/create-cloudflare/src/__tests__) (using Vitest), and should include unit tests within the testing harness of those packages. For documentation on how these testing frameworks work, see:
- Vitest: <https://vitest.dev/guide>
- Ava: <https://github.com/avajs/ava?tab=readme-ov-file#documentation>
If your PR includes functionality that's difficult to unit test, you can add a fixture test by creating a new package in the `fixtures/` folder. This allows for adding a test that requires a specific filesystem or worker setup (for instance, `fixtures/no-bundle-import` tests the interaction of Wrangler with a specific set of JS, WASM, text, and binary modules on the filesystem). When adding a fixture test, include a `vitest.config.mts` file within the new package, which will ensure it's run as part of the `workers-sdk` CI. You should merge your own configuration with the default config from the root of the repo.
A good default example is the following:
```ts
import { defineProject, mergeConfig } from "vitest/config";
import configShared from "../../vitest.shared";
export default mergeConfig(
configShared,
defineProject({
test: {
// config overrides
}
})
});
```
If you need to test the interaction of Wrangler with a real Cloudflare account, you can add an E2E test within the `packages/wrangler/e2e` folder. This lets you add a test for functionality that requires real credentials (i.e. testing whether a worker deployed from Wrangler can be accessed over the internet).
A summary of this repositories actions can be found [in the `.github/workflows` folder](.github/workflows/README.md)
## Remote E2E Tests in CI
E2E tests that hit the Cloudflare backend (deploying Workers, testing bindings, etc.) are inherently slow and can be flaky due to network and service dependencies. To keep PR feedback loops fast and reliable, **CI does not pass Cloudflare API credentials to E2E test jobs by default**. The E2E test suites still run on every PR, but tests that require remote access are automatically skipped when the API token is absent.
Remote E2E tests run automatically in these cases:
- **Version Packages PRs** (branch `changeset-release/main`) — acts as a pre-release safety net, catching remote-test failures before packages are published.
- **Merge queue** — final check before code lands on `main`.
If you need remote E2E tests on your PR (e.g. you're changing deployment logic or binding behavior), apply the **`ci:run-remote-tests`** label. This triggers a re-run of the E2E workflows with API credentials enabled.
> [!NOTE]
> The `ci:run-remote-tests` label has no effect on PRs from forks, because GitHub does not expose repository secrets to fork PRs.
## Running E2E tests locally
A large number of Wrangler, C3 & Vite's E2E tests don't require any authentication, and can be run with no Cloudflare account credentials. These can be run as follows, optionally providing [`CLOUDFLARE_ACCOUNT_ID` and `CLOUDFLARE_API_TOKEN` environment variables.](#creating-an-api-token):
- **Vite:** `pnpm test:e2e -F @cloudflare/vite-plugin`
You may optionally want to append a filename pattern to limit which e2e tests are run. Also you may want to set `--bail=n` to limit the number of fails tests to show the error before the rest of the tests finish running and to limit the noise in that output:
```sh
pnpm test:e2e -F @cloudflare/vite-plugin [file-pattern] --bail=1
```
- **C3:** `pnpm test:e2e -F create-cloudflare`
See [C3 E2E tests README](packages/create-cloudflare/e2e/README.md) for more information.
- **Wrangler:** `pnpm test:e2e -F wrangler` or `pnpm test:e2e:wrangler`
See [Wrangler E2E tests README](packages/wrangler/e2e/README.md) for more information.
### Creating an API Token
If you want to run the E2E tests that access the Cloudflare API (e.g. for testing Worker deployment and interaction with bindings), you can create an API token for running the tests:
1. Go to ["My Profile" > "User API Tokens"](https://dash.cloudflare.com/profile/api-tokens)
1. Click "Create Token"
1. Use the "Edit Cloudflare Workers" template
1. Set "Account Resources" to "Include" the account you want to use for running the test
(for internal and CI use, this needs to be the "DevProd Testing" account)
1. No "Zone Resources" are required for general use (for internal and CI use, this needs to be set to "All Zones")
1. Click "Continue to summary"
1. Verify your token works by running the curl command provided
Once you've created the token, you can use it when running E2E tests to test against the API:
```sh
# Vite
CLOUDFLARE_ACCOUNT_ID="<Account ID for the token you just created>" CLOUDFLARE_API_TOKEN="<Token you just created>" pnpm test:e2e -F @cloudflare/vite-plugin
# C3
CLOUDFLARE_ACCOUNT_ID="<Account ID for the token you just created>" CLOUDFLARE_API_TOKEN="<Token you just created>" pnpm test:e2e -F @create-cloudflare
# Wrangler
CLOUDFLARE_ACCOUNT_ID="<Account ID for the token you just created>" CLOUDFLARE_API_TOKEN="<Token you just created>" pnpm test:e2e:wrangler
```
> [!NOTE]
> Workers and other resources created in the E2E tests might not always be cleaned up. Internal users with access to the "DevProd Testing" account can rely on an automated job to clean up the Workers and other resources, but if you use another account, please be aware you may want to manually delete the Workers and other resources yourself.
## Managing Package Dependencies
Packages in this monorepo should bundle their dependencies into the distributable code rather than leaving them as runtime `dependencies` that get installed by downstream users. This prevents dependency chain poisoning where a transitive dependency could introduce unexpected or malicious code.
### The Rule
- **Bundle dependencies**: Most dependencies should be listed in `devDependencies` and bundled into the package output by esbuild/tsup/etc.
- **External dependencies**: Only dependencies that _cannot_ be bundled should be listed in `dependencies`. These must be explicitly declared with documentation explaining why.
### Why This Matters
When users install one of our packages (e.g., `wrangler`), npm/pnpm will also install everything listed in `dependencies`. If one of those dependencies has unpinned transitive dependencies, a malicious actor could publish a compromised version that gets pulled into user installations. By bundling our dependencies, we control exactly what code ships.
### Adding a New External Dependency
If you need to add a dependency that cannot be bundled (native binaries, WASM modules, packages that must be resolved at runtime, etc.):
1. **Add to `dependencies`** in `package.json` with a pinned version
2. **Add to `EXTERNAL_DEPENDENCIES`** in `scripts/deps.ts` with a comment explaining why it can't be bundled
3. **Run `pnpm check:package-deps`** to verify the allowlist is correct
Example `scripts/deps.ts`:
```typescript
export const EXTERNAL_DEPENDENCIES = [
// Native binary - cannot be bundled
"workerd",
// WASM module that blows up when bundled
"blake3-wasm",
// Must be resolved at runtime when bundling user's worker code
"esbuild",
];
```
### Valid Reasons for External Dependencies
- **Native binaries**: Packages like `workerd` or `sharp` contain platform-specific binaries
- **WASM modules**: Some WASM packages don't bundle correctly
- **Runtime resolution**: Packages like `esbuild` or `unenv` that need to be resolved when bundling user code
- **Peer dependencies**: Packages the user is expected to provide (e.g., `react`, `vite`)
### Pinning External Dependencies
Because external dependencies are installed into downstream users' dependency trees rather than bundled, their versions must be **pinned to an exact version** (e.g. `1.2.3`, not `^1.2.3`). This closes the supply-chain hole above: an unpinned external dependency could resolve to a compromised upstream release without us vetting it.
This is enforced by `pnpm check:pinned-deps`, which requires:
- Every `dependencies` and `optionalDependencies` entry of a published package to be an exact version, or a `workspace:`/`catalog:` reference.
- Every entry in the pnpm `catalog:` (in `pnpm-workspace.yaml`) to be an exact version, so that any `catalog:default` reference is also pinned. Deliberate exceptions live in `CATALOG_PIN_EXCEPTIONS` in `tools/deployments/validate-pinned-dependencies.ts` (currently only `@cloudflare/workers-types`, which is consumed as a peer dependency).
`peerDependencies` are exempt — ranges there are intentional, since they describe the set of consumer-provided versions a package is compatible with.
## Changesets
Every non-trivial change to the project - those that should appear in the changelog - must be captured in a "changeset".
See the [.changeset/README.md](.changeset/README.md) for detailed guidelines on:
- Creating changesets
- Choosing version types (patch/minor/major)
- Writing good changeset descriptions
- Formatting rules
Quick start:
```sh
pnpm changeset
git add .changeset/*.md
```
### Styleguide
When contributing to Wrangler, please refer to the [`STYLEGUIDE.md file`](STYLEGUIDE.md) file where possible to help maintain consistent patterns throughout Wrangler.
## Releases
We generally cut Wrangler releases on Tuesday & Thursday each week. If you need a release cut outside of the regular cadence, please reach out to the [@cloudflare/wrangler-admins](https://github.com/orgs/cloudflare/teams/wrangler-admins) team.
+176
View File
@@ -0,0 +1,176 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
+25
View File
@@ -0,0 +1,25 @@
Copyright (c) 2020 Cloudflare, Inc. <wrangler@cloudflare.com>
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
+160
View File
@@ -0,0 +1,160 @@
<h1 align="center">Cloudflare Workers SDK</h1>
<p align="center">
<img src="cloudflare-workers-outline.png" alt="workers-logo" width="120px" height="120px"/>
<br>
Cloudflare Workers let you deploy serverless code instantly across the globe for exceptional performance, reliability, and scale.
<br>
</p>
<p align="center">
<a href="CONTRIBUTING.md">Contribute</a>
·
<a href="https://github.com/cloudflare/workers-sdk/issues">Submit an Issue</a>
·
<a href="https://discord.cloudflare.com/">Join Discord</a>
<br>
<br>
</p>
<p align="center">
<a href="https://www.npmjs.com/wrangler/">
<img src="https://img.shields.io/npm/v/wrangler.svg?logo=npm&logoColor=fff&label=NPM+package&color=orange" alt="Wrangler on npm" />
</a>&nbsp;
<a href="https://discord.cloudflare.com/">
<img src="https://img.shields.io/discord/595317990191398933.svg?logo=discord&logoColor=fff&label=Discord&color=7389d8" alt="Discord conversation" />
</a>&nbsp;
<a href="https://twitter.com/CloudflareDev">
<img src="https://img.shields.io/twitter/follow/cloudflaredev" alt="X conversation" />
</a>
</p>
<hr>
## Quick Start
To get started quickly with a new project, run the command below:
```bash
npm create cloudflare@latest
# or
pnpm create cloudflare@latest
# or
yarn create cloudflare@latest
```
For more info, visit our [Getting Started](https://developers.cloudflare.com/workers/get-started/guide/) guide.
## Documentation
Visit the official Workers documentation [here](https://developers.cloudflare.com/workers/).
- [Getting Started](https://developers.cloudflare.com/workers/get-started/guide/)
- [How Workers works](https://developers.cloudflare.com/workers/reference/how-workers-works/)
- [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/)
- [Observability](https://developers.cloudflare.com/workers/observability/)
- [Platform](https://developers.cloudflare.com/workers/platform/)
## Directory
| Package | Description | Links |
| ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
| [`wrangler`](https://github.com/cloudflare/workers-sdk/tree/main/packages/wrangler) | A command line tool for building [Cloudflare Workers](https://workers.cloudflare.com/). | [Docs](https://developers.cloudflare.com/workers/wrangler/) |
| [`create-cloudflare` (C3)](https://github.com/cloudflare/workers-sdk/tree/main/packages/create-cloudflare) | A CLI for creating and deploying new applications to Cloudflare. | [Docs](https://developers.cloudflare.com/pages/get-started/c3/) |
| [`miniflare`](https://github.com/cloudflare/workers-sdk/tree/main/packages/miniflare) | A simulator for developing and testing Cloudflare Workers, powered by [workerd](https://github.com/cloudflare/workerd) | [Docs](https://miniflare.dev) |
| [`chrome-devtools-patches`](https://github.com/cloudflare/workers-sdk/tree/main/packages/chrome-devtools-patches) | Cloudflare's fork of Chrome DevTools for inspecting your local or remote Workers | |
| [`pages-shared`](https://github.com/cloudflare/workers-sdk/tree/main/packages/pages-shared) | Used internally to power Wrangler and Cloudflare Pages. It contains all the code that is shared between these clients. | |
## Beta releases
Beta releases are generated by the [pkg.pr.new](https://github.com/stackblitz-labs/pkg.pr.new) tool and are updated on every commit pushed to the `main` branch.
> [!Warning]
> These beta releases get updated over time, so they are ill suited to be used as stable versions for a project (and the proper npm released should be used instead). These should be used only for quick testing of not yet released features/fixes.
Available beta releases are listed below.
<details><summary><b>create-cloudflare</b></summary><p>
```
npm i https://pkg.pr.new/create-cloudflare@main
```
</p></details>
<details><summary><b>@cloudflare/kv-asset-handler</b></summary><p>
```
npm i https://pkg.pr.new/@cloudflare/kv-asset-handler@main
```
</p></details>
<details><summary><b>miniflare</b></summary><p>
```
npm i https://pkg.pr.new/miniflare@main
```
</p></details>
<details><summary><b>@cloudflare/pages-shared</b></summary><p>
```
npm i https://pkg.pr.new/@cloudflare/pages-shared@main
```
</p></details>
<details><summary><b>@cloudflare/unenv-preset</b></summary><p>
```
npm i https://pkg.pr.new/@cloudflare/unenv-preset@main
```
</p></details>
<details><summary><b>@cloudflare/vite-plugin</b></summary><p>
```
npm i https://pkg.pr.new/@cloudflare/vite-plugin@main
```
</p></details>
<details><summary><b>@cloudflare/vitest-pool-workers</b></summary><p>
```
npm i https://pkg.pr.new/@cloudflare/vitest-pool-workers@main
```
</p></details>
<details><summary><b>@cloudflare/workers-editor-shared</b></summary><p>
```
npm i https://pkg.pr.new/@cloudflare/workers-editor-shared@main
```
</p></details>
<details><summary><b>wrangler</b></summary><p>
```
npm i https://pkg.pr.new/wrangler@main
```
</p></details>
## Contributing
We welcome new contributors! Refer to the [`CONTRIBUTING.md`](/CONTRIBUTING.md) guide for details.
## Community
Join us in the official [Cloudflare Discord](https://discord.cloudflare.com/) to meet other developers, ask questions, or learn more in general.
## Links
- [Project Board](https://github.com/orgs/cloudflare/projects/1)
- [Discussions](https://github.com/cloudflare/workers-sdk/discussions)
+7
View File
@@ -0,0 +1,7 @@
# WeHub 来源说明
- 原始项目:`cloudflare/workers-sdk`
- 原始仓库:https://github.com/cloudflare/workers-sdk
- 导入方式:上游默认分支的最新快照
- 原作者、版权和许可证信息以原始仓库及本仓库 LICENSE 为准
- 本文件仅用于记录来源,不代表 WeHub 是原项目作者
+34
View File
@@ -0,0 +1,34 @@
# Review Guidelines
This file provides guidance when reviewing PRs in this repository. For general development guidelines, see [AGENTS.md](./AGENTS.md).
## Code Quality
- Avoid using global variables. Repository maintainers will reject code that introduces global variables.
- When a function needs to return multiple related objects, prefer changing the function signature to return an object with named properties (e.g., `return { wrangler, registry }`) rather than using `Object.defineProperty` to attach additional properties to one of the return values.
- When implementing multiple similar validation checks (like BOM detection in different formats), prefer using data structures like arrays of objects to reduce code duplication. For example, when checking for different byte patterns, use an array of objects with shape `{name: string, buffer: Buffer}` and loop over the array instead of writing separate conditional blocks for each pattern.
## CI Failures
- Test failures in CI are often flakes that can be rerun rather than actual issues requiring code fixes. If a test fails on one OS but passes on another, it is likely a flake and should be rerun at least twice before investigating further.
## Changesets
- Changesets should target users of the tools (e.g. Wrangler users) rather than maintainers. Avoid including implementation details like "moves X from hybridModules to nativeModules" or "removes polyfill implementation" or "adds comprehensive tests". Instead, focus on user-facing impact and benefits.
- Do NOT prefix changeset titles with a "type" (e.g. `fix:`, `feat:`, `chore:`). The changeset title should be a plain description without conventional commit prefixes.
### Semver Classification
- **Minor (new features):** Adding support for new frameworks (even in experimental mode), new commands/flags/options, new API capabilities or exports, behavior changes that add functionality.
- **Patch (bug fixes and improvements):** Fixing bugs where something was not working correctly, dependency updates, internal refactoring without user-facing changes, performance improvements, error message improvements.
- The description text matters less than the actual change. A changeset described as "Support X" is adding a new feature (minor), while "filters out invalid X" is fixing a bug (patch). Analyze what the change actually does for users rather than relying on keywords.
## Version Packages PR Review
When reviewing Version Packages PRs, use a structured two-pass review process:
1. **Pass 1 - Extract Facts:** For each changeset, identify filename/slug, package(s) affected, declared classification (from front-matter), description, and source PR number.
2. **Pass 2 - Analyze and Compare:** For each changeset, determine recommended classification, rationale, and whether it matches the declared classification.
3. **Pass 3 - Report Only Mismatches:** Only flag changesets where declared does not match recommended. State: "Currently classified as X, should be Y because..."
Use the semver classification guidelines above to determine correct classification.
+3
View File
@@ -0,0 +1,3 @@
# Reporting Security Vulnerabilities
Please see [this page](https://www.cloudflare.com/.well-known/security.txt) for information on how to report a vulnerability to Cloudflare. Thanks!
+158
View File
@@ -0,0 +1,158 @@
# Wrangler Styleguide
The aim of this guide is to help Wrangler contributors maintain consistent patterns throughout Wrangler and to provide end users with content that educates users on how to successfully complete tasks.
## Styleguide legend
- \*Text in between stars designates placeholder text that should be replaced.\*
- \<Text contained within angle brackets are placeholder commands, args or filepaths.\>
- #Text in between hashtags designates the user's input that must occur before the next line can display.#
## Wrangler syntax
- Commands should follow an object verb order, such as d1 create.
```sh
wrangler <object / noun> <verb>
```
- Subcommands should follow the main command with a space
```sh
wrangler <command> <subcommand> <arg> --<option>`
```
## Wrangler \<command\> --help
```sh
🧮 *Brief description of the product, the value it offers and how Wrangler can interact with it*
🔧 *Command is currently in open beta / command is experimental (if relevant)*
Commands:
wrangler <command> <subcommand> <arg> *Description of command*
Options:
-<o (option shorthand)>, --<option (option name)> *Option description* [*data_type*] [default: *true/false*]
--------------------
📣 *Announcement*
📃 To learn more, visit our documentation on *Product name*: https://developers.cloudflare.com/*productname*
--------------------
```
## Create success state with binding
```sh
🌀 Creating ___ with title "___"
✨ Success. *Add details of success and what the user can now do*
📣 *Optional announcement*
To start interacting with this ___ from a Worker, *If additional steps required, such as obtaining account ID from dash, add them here* \(then\) open your Workers config file and add the following binding configuration:
[[array]]
binding = "<VARIABLE_NAME>"
name = "___"
id = "___"
```
## General success state
```sh
🌀 *Action verb* *Object*. *details of what is currently happening if necessary*
🚧 *Updated additional details of the current status if necessary*
✨ Success. *Add details of and what the user can now do*
📣 *Announcement*
*Description of what the next steps the user can take to be successful. If there are predictable happy paths following a success state, make those paths clear to the user here.*
```
## Command related error
```sh
✘ ERROR *API error code if applicable*: *Concise description of what the error is*:
Error details:
*Description of what caused the error*
How to solve this error:
*Direction on how to resolve the error*
wrangler *example of full command user tried to run*
*Description of the commands purpose*
Positionals:
positional *Positional description*. [data-type] [required/optional]
Options:
-o, --option *Option description* [data type] [default: true/false]
If you think this is a bug then please create an issue at https://github.com/cloudflare/workers-sdk/issues/new/choose
🪵 Logs were written to <filepath>
```
## General error
```sh
✘ ERROR *Error code if applicable*: *Concise description of what the error is*
Error details:
*description of what caused the error*
How to solve this error:
*direction on how to resolve the error*
--------------------
To learn more about ___, read our documentation at https://developers.cloudflare.com/*productname*
If you think this is a bug then please create an issue at https://github.com/cloudflare/workers-sdk/issues/new/choose
--------------------
√ Would you like to report this error to Cloudflare? <y/n>
#User inputs y or n#
🪵 Logs were written to <filepath>
```
## Y/N choice
```sh
*choice description* <y/n>
#User inputs y or n#
```
## Wrangler prompts
### Written value prompt
```sh
wrangler <command>
<prompt request>
<defaut_value>
#User enters value then presses enter#
<response detailing what task/s have been performed and with what values where applicable OR continue to next prompt>
```
### Multiple choice prompt
```sh
Wrangler <command>
<prompt request>
◉ <Choice 1>
○ <Choice 2>
○ <Choice 3>
#User presses enter#
<response detailing what task/s have been performed and with what values where applicable OR continue to next prompt>
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

+28
View File
@@ -0,0 +1,28 @@
# Codeowners Plus - Advanced Configuration
# See: https://github.com/multimediallc/codeowners-plus
# See: CODEOWNERS.md for full documentation
# Directories to ignore (no ownership checks, reduces review noise)
ignore = []
# Show detailed file-to-owner mapping in PR comments
detailed_reviewers = true
# Don't dismiss stale reviews when new changes are pushed
disable_smart_dismissal = true
# Suppress warnings for intentionally unowned files (e.g. pnpm-lock.yaml)
suppress_unowned_warning = true
[enforcement]
# The bot (@workers-devprod) approves PRs when all ownership rules are satisfied,
# unblocking the native GitHub CODEOWNERS requirement in branch protection.
# The check still runs for visibility (PR comments, review requests) but does not
# fail — enforcement is via the bot's CODEOWNERS approval only.
approval = true
fail_check = false
[admin_bypass]
# Allow repo admins to bypass codeowner requirements in emergencies
# by submitting an approval review containing "Codeowners Bypass" text
enabled = true
+22
View File
@@ -0,0 +1,22 @@
{
"name": "@fixture/additional-modules",
"private": true,
"scripts": {
"build": "wrangler deploy --dry-run --outdir=dist",
"check:type": "tsc",
"deploy": "wrangler deploy",
"start": "wrangler dev",
"test:ci": "vitest run",
"test:watch": "vitest",
"type:tests": "tsc -p ./test/tsconfig.json"
},
"devDependencies": {
"@cloudflare/workers-tsconfig": "workspace:*",
"@cloudflare/workers-types": "catalog:default",
"@fixture/shared": "workspace:*",
"typescript": "catalog:default",
"undici": "catalog:default",
"vitest": "catalog:default",
"wrangler": "workspace:*"
}
}
@@ -0,0 +1 @@
module.exports = "common";
+1
View File
@@ -0,0 +1 @@
export default "bundled";
@@ -0,0 +1 @@
export default "dynamic";
@@ -0,0 +1 @@
SELECT * FROM users;
+32
View File
@@ -0,0 +1,32 @@
import common from "./common.cjs";
import dep from "./dep";
import sql from "./example.sql";
import text from "./text.txt";
export default <ExportedHandler>{
async fetch(request) {
const url = new URL(request.url);
if (url.pathname === "/dep") {
return new Response(dep);
}
if (url.pathname === "/text") {
return new Response(text);
}
if (url.pathname === "/sql") {
return new Response(sql);
}
if (url.pathname === "/common") {
return new Response(common);
}
if (url.pathname === "/dynamic") {
return new Response((await import("./dynamic.js")).default);
}
if (url.pathname.startsWith("/lang/")) {
// Build the path dynamically to ensure esbuild doesn't inline the import.
const language =
"./lang/" + url.pathname.substring("/lang/".length) + ".js";
return new Response((await import(language)).default.hello);
}
return new Response("Not Found", { status: 404 });
},
};
@@ -0,0 +1 @@
export default { hello: "hello" };
@@ -0,0 +1 @@
export default { hello: "bonjour" };
+9
View File
@@ -0,0 +1,9 @@
declare module "*.txt" {
const value: string;
export default value;
}
declare module "*.sql" {
const value: string;
export default value;
}

Some files were not shown because too many files have changed in this diff Show More