chore: import upstream snapshot with attribution
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) Has been cancelled
Semgrep OSS scan / semgrep-oss (push) Has been cancelled
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) Has been cancelled
Semgrep OSS scan / semgrep-oss (push) Has been cancelled
This commit is contained in:
@@ -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 }}
|
||||
Reference in New Issue
Block a user