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
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:
@@ -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.
|
||||
```
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"miniflare": minor
|
||||
---
|
||||
|
||||
Allow `listDurableObjectIds()` to accept Durable Object class names as well as binding names.
|
||||
@@ -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" });
|
||||
```
|
||||
@@ -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
@@ -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>
|
||||
@@ -0,0 +1,10 @@
|
||||
# https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
indent_style = tab
|
||||
|
||||
[*.{yml,yaml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
@@ -0,0 +1,4 @@
|
||||
# Refactor from spaces to tabs
|
||||
e5a6aca696108cda8c3890b8ce2ec44c6cc09a0e
|
||||
# Prettier 2 -> 3
|
||||
f6300a71adb83e5f92cd9c9ddd56a85132e03f98
|
||||
@@ -0,0 +1,3 @@
|
||||
* text=auto eol=lf
|
||||
api-extractor.json linguist-language=JSON-with-Comments
|
||||
tsconfig.emit.json linguist-language=JSON-with-Comments
|
||||
@@ -0,0 +1,4 @@
|
||||
version: 2
|
||||
secret:
|
||||
ignored-paths:
|
||||
- "packages/wrangler/src/__tests__/hyperdrive.test.ts"
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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();
|
||||
@@ -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"
|
||||
@@ -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];
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
organization: cloudflare
|
||||
stickers:
|
||||
- id: cltg4w6r612040fl2r7vweuvv
|
||||
alias: "ci:outstanding-contribution"
|
||||
|
||||
holobytes:
|
||||
- evolvingStickerId: cltg4rlqc39020fl51scnguhb
|
||||
alias: contribution
|
||||
in: true
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
}
|
||||
);
|
||||
@@ -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.
|
||||
-->
|
||||
@@ -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.
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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).
|
||||
@@ -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 }}
|
||||
@@ -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 }}
|
||||
@@ -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
|
||||
@@ -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)"
|
||||
@@ -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 }}
|
||||
@@ -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 }}
|
||||
@@ -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 }}
|
||||
@@ -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
|
||||
@@ -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 }}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 }}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 }}
|
||||
@@ -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 }}
|
||||
@@ -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 }}
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"MD010": false,
|
||||
"MD013": false
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
```
|
||||
@@ -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
@@ -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",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"recommendations": ["oxc.oxc-vscode"]
|
||||
}
|
||||
Vendored
+16
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+112
@@ -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"
|
||||
}
|
||||
@@ -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`
|
||||
@@ -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
|
||||
@@ -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
@@ -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)
|
||||
@@ -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
@@ -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`.
|
||||
- Don’t 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
@@ -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
@@ -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.
|
||||
@@ -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>
|
||||
<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>
|
||||
<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)
|
||||
@@ -0,0 +1,7 @@
|
||||
# WeHub 来源说明
|
||||
|
||||
- 原始项目:`cloudflare/workers-sdk`
|
||||
- 原始仓库:https://github.com/cloudflare/workers-sdk
|
||||
- 导入方式:上游默认分支的最新快照
|
||||
- 原作者、版权和许可证信息以原始仓库及本仓库 LICENSE 为准
|
||||
- 本文件仅用于记录来源,不代表 WeHub 是原项目作者
|
||||
@@ -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.
|
||||
@@ -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
@@ -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 Worker’s 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 command’s 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 |
@@ -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
|
||||
@@ -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";
|
||||
@@ -0,0 +1 @@
|
||||
export default "bundled";
|
||||
@@ -0,0 +1 @@
|
||||
export default "dynamic";
|
||||
@@ -0,0 +1 @@
|
||||
SELECT * FROM users;
|
||||
@@ -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
@@ -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
Reference in New Issue
Block a user