diff --git a/.changeset/pin-workerd-latest.md b/.changeset/pin-workerd-latest.md new file mode 100644 index 000000000000..0db069c7162c --- /dev/null +++ b/.changeset/pin-workerd-latest.md @@ -0,0 +1,13 @@ +--- +"miniflare": patch +"wrangler": patch +--- + +Update dependencies of "miniflare", "wrangler" + +The following dependency versions have been updated: + +| Dependency | From | To | +| ------------------------- | ------------ | ------------ | +| workerd | 1.20260305.0 | 1.20260226.1 | +| @cloudflare/workers-types | 4.20260305.0 | 4.20260226.1 | diff --git a/.changeset/remove-chalk-create-cloudflare.md b/.changeset/remove-chalk-create-cloudflare.md new file mode 100644 index 000000000000..78bfbef17e4b --- /dev/null +++ b/.changeset/remove-chalk-create-cloudflare.md @@ -0,0 +1,5 @@ +--- +"create-cloudflare": patch +--- + +Remove unused `chalk` dependency from create-cloudflare. diff --git a/.changeset/rude-ends-battle.md b/.changeset/rude-ends-battle.md new file mode 100644 index 000000000000..d0f88d689951 --- /dev/null +++ b/.changeset/rude-ends-battle.md @@ -0,0 +1,5 @@ +--- +"wrangler": patch +--- + +Fix `wrangler pipelines setup` failing for Data Catalog sinks on new buckets by using the correct R2 Catalog API error code (`40401`). diff --git a/.github/workflows/bonk.yml b/.github/workflows/bonk.yml deleted file mode 100644 index 52c5d47b55ea..000000000000 --- a/.github/workflows/bonk.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Bonk - -on: - issue_comment: - types: [created] - pull_request_review_comment: - types: [created] - -concurrency: - group: ${{ github.workflow }}-${{ github.event.issue.number || github.event.pull_request.number }} - 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 - permissions: - id-token: write - contents: write - issues: write - pull-requests: write - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 1 - - - name: Run Bonk - uses: ask-bonk/ask-bonk/github@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-5" - mentions: "/bonk,@ask-bonk" - permissions: write diff --git a/.github/workflows/changeset-review.yml b/.github/workflows/changeset-review.yml index 0950c9aa198c..1d26f703d7b4 100644 --- a/.github/workflows/changeset-review.yml +++ b/.github/workflows/changeset-review.yml @@ -20,10 +20,18 @@ jobs: runs-on: ubuntu-latest if: github.event.pull_request.head.repo.full_name == github.repository steps: - - name: Checkout PR branch + - name: Checkout changesets uses: actions/checkout@v4 with: fetch-depth: 0 + sparse-checkout: | + .changeset + .github/opencode.json + + - name: Install OpenCode + run: | + npm install -g opencode-ai + cp .github/opencode.json ./opencode.json - name: Get changed changeset files id: changed-changesets @@ -36,23 +44,24 @@ jobs: # Recover deleted files so Claude can read them (needed for Version Packages PRs) recover_deleted_files: ${{ github.event.pull_request.title == 'Version Packages' }} - - name: Review Changesets with Claude - id: claude-review + - 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 - uses: anthropics/claude-code-action@v1.0.22 - with: - anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} - allowed_bots: "devin-ai-integration[bot]" - use_sticky_comment: true - track_progress: true - prompt: | - Review the changeset files in this PR. + 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 run --print-logs \ + "Review the changeset files in this PR. - For "Version Packages" PRs, review: ${{ steps.changed-changesets.outputs.deleted_files }} - For regular PRs, review: ${{ steps.changed-changesets.outputs.added_files }} + For \"Version Packages\" PRs, review: ${DELETED_FILES} + For regular PRs, review: ${ADDED_FILES} - Read `.changeset/README.md` for guidelines, then validate: + 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) @@ -60,15 +69,20 @@ jobs: 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. + 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. + If there are issues, output \"⚠️ Issues found\" followed by the specific problems. + + Write your review to changeset-review.md." - If the user has attached an image, use the Read tool to view it. If and only if it's a cute animal, include in your comment a short cuteness report in the style of WeRateDogs (e.g., "This is Barkley. He's wearing a tiny hat and doesn't know why. 13/10"). If it's not an animal, silently ignore the image. If there is more than one image, check each one to find an animal. If you do not find an animal, do not include any reference to a cuteness report. - claude_args: | - --allowedTools "Read,Glob,Grep" + - name: Post review comment + if: steps.opencode-review.outcome == 'success' + uses: marocchino/sticky-pull-request-comment@v2 + 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 diff --git a/packages/create-cloudflare/e2e/tests/frameworks/test-config.ts b/packages/create-cloudflare/e2e/tests/frameworks/test-config.ts index 62c91442133a..3053d857b7f5 100644 --- a/packages/create-cloudflare/e2e/tests/frameworks/test-config.ts +++ b/packages/create-cloudflare/e2e/tests/frameworks/test-config.ts @@ -1,5 +1,6 @@ import { detectPackageManager } from "../../../src/helpers/packageManagers"; import { + CLOUDFLARE_API_TOKEN, frameworkToTestFilter, isExperimental, keys, @@ -985,6 +986,9 @@ function getExperimentalFrameworkTestConfig( testCommitMessage: true, unsupportedOSs: ["win32"], unsupportedPms: ["npm", "yarn"], + // this test creates an R2 bucket, so it requires a Cloudflare API token + // and needs to be skipped on forks + quarantine: !CLOUDFLARE_API_TOKEN, verifyDeploy: { route: "/", expectedText: "Generated by create next app", diff --git a/packages/create-cloudflare/package.json b/packages/create-cloudflare/package.json index 0d2e33f47c37..b95775d1159e 100644 --- a/packages/create-cloudflare/package.json +++ b/packages/create-cloudflare/package.json @@ -58,7 +58,6 @@ "@types/semver": "^7.5.1", "@types/which-pm-runs": "^1.0.0", "@types/yargs": "^17.0.22", - "chalk": "^5.2.0", "command-exists": "^1.2.9", "comment-json": "^4.5.0", "cross-spawn": "^7.0.3", diff --git a/packages/miniflare/package.json b/packages/miniflare/package.json index cfdee8e393c2..ab2feacbf632 100644 --- a/packages/miniflare/package.json +++ b/packages/miniflare/package.json @@ -50,7 +50,7 @@ "@cspotcode/source-map-support": "0.8.1", "sharp": "^0.34.5", "undici": "catalog:default", - "workerd": "1.20260305.0", + "workerd": "1.20260226.1", "ws": "catalog:default", "youch": "4.1.0-beta.10" }, diff --git a/packages/wrangler/package.json b/packages/wrangler/package.json index dbef7fdc81f9..c2fbe1066ece 100644 --- a/packages/wrangler/package.json +++ b/packages/wrangler/package.json @@ -73,7 +73,7 @@ "miniflare": "workspace:*", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", - "workerd": "1.20260305.0" + "workerd": "1.20260226.1" }, "devDependencies": { "@aws-sdk/client-s3": "^3.721.0", diff --git a/packages/wrangler/src/__tests__/pipelines-setup.test.ts b/packages/wrangler/src/__tests__/pipelines-setup.test.ts index 7db19d85eb4a..675bfee01420 100644 --- a/packages/wrangler/src/__tests__/pipelines-setup.test.ts +++ b/packages/wrangler/src/__tests__/pipelines-setup.test.ts @@ -365,7 +365,7 @@ describe("wrangler pipelines setup", () => { if (!exists) { return HttpResponse.json( createFetchResult(null, false, [ - { code: 10006, message: "catalog not found" }, + { code: 40401, message: "Warehouse not found" }, ]), { status: 404 } ); diff --git a/packages/wrangler/src/pipelines/cli/setup.ts b/packages/wrangler/src/pipelines/cli/setup.ts index 142aa8aac57d..0fd886d37a3e 100644 --- a/packages/wrangler/src/pipelines/cli/setup.ts +++ b/packages/wrangler/src/pipelines/cli/setup.ts @@ -130,7 +130,7 @@ async function ensureCatalogEnabled( const catalog = await getR2Catalog(config, accountId, bucketName); catalogEnabled = catalog.status === "active"; } catch (err) { - if (err instanceof APIError && err.code === 10006) { + if (err instanceof APIError && err.code === 40401) { // Catalog not enabled yet } else { throw err; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a2132827b440..92b1cabbd6fe 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,7 +10,7 @@ catalogs: specifier: ^0.10.11 version: 0.10.15 '@cloudflare/workers-types': - specifier: ^4.20260305.0 + specifier: ^4.20260226.1 version: 4.20260305.0 '@typescript-eslint/eslint-plugin': specifier: ^8.35.1 @@ -1853,9 +1853,6 @@ importers: '@types/yargs': specifier: ^17.0.22 version: 17.0.24 - chalk: - specifier: ^5.2.0 - version: 5.3.0 command-exists: specifier: ^1.2.9 version: 1.2.9 @@ -2206,8 +2203,8 @@ importers: specifier: catalog:default version: 7.18.2 workerd: - specifier: 1.20260305.0 - version: 1.20260305.0 + specifier: 1.20260226.1 + version: 1.20260226.1 ws: specifier: catalog:default version: 8.18.0 @@ -4172,8 +4169,8 @@ importers: specifier: 2.0.0-rc.24 version: 2.0.0-rc.24 workerd: - specifier: 1.20260305.0 - version: 1.20260305.0 + specifier: 1.20260226.1 + version: 1.20260226.1 optionalDependencies: fsevents: specifier: ~2.3.2 @@ -5269,8 +5266,8 @@ packages: cpu: [x64] os: [darwin] - '@cloudflare/workerd-darwin-64@1.20260305.0': - resolution: {integrity: sha512-chhKOpymo0Eh9J3nymrauMqKGboCc4uz/j0gA1G4gioMnKsN2ZDKJ+qjRZDnCoVGy8u2C4pxlmyIfsXCAfIzhQ==} + '@cloudflare/workerd-darwin-64@1.20260226.1': + resolution: {integrity: sha512-Yqx+GmA+c4jTg+Icx/xC4ietdBEgqBoLTMWDIPCYmvK8uG4okdNQOVK19H1GmlD8Cm+xHSxhOJA97nY2f6x8GA==} engines: {node: '>=16'} cpu: [x64] os: [darwin] @@ -5287,8 +5284,8 @@ packages: cpu: [arm64] os: [darwin] - '@cloudflare/workerd-darwin-arm64@1.20260305.0': - resolution: {integrity: sha512-K9aG2OQk5bBfOP+fyGPqLcqZ9OR3ra6uwnxJ8f2mveq2A2LsCI7ZeGxQiAj75Ti80ytH/gJffZIx4Np2JtU3aQ==} + '@cloudflare/workerd-darwin-arm64@1.20260226.1': + resolution: {integrity: sha512-wnkdfztvQZB9mppmQuXZtvI+As9E9LZCLy6cNXHqX7M7aDB3TjmMJ7Ses5hVWFRPF/v+J/DHIgXLUNtcz+JW6g==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] @@ -5305,8 +5302,8 @@ packages: cpu: [x64] os: [linux] - '@cloudflare/workerd-linux-64@1.20260305.0': - resolution: {integrity: sha512-tt7XUoIw/cYFeGbkPkcZ6XX1aZm26Aju/4ih+DXxOosbBeGshFSrNJDBfAKKOvkjsAZymJ+WWVDBU+hmNaGfwA==} + '@cloudflare/workerd-linux-64@1.20260226.1': + resolution: {integrity: sha512-2IXlV/4/1ofnIjG48DsyHV8R82EJBCnTwsgDZbMWgLbrkdlBAl7hCCbtVfOCJs7/iQ6l2PtjMO2PSNS7qZlTaA==} engines: {node: '>=16'} cpu: [x64] os: [linux] @@ -5323,8 +5320,8 @@ packages: cpu: [arm64] os: [linux] - '@cloudflare/workerd-linux-arm64@1.20260305.0': - resolution: {integrity: sha512-72QTkY5EzylmvCZ8ZTrnJ9DctmQsfSof1OKyOWqu/pv/B2yACfuPMikq8RpPxvVu7hhS0ztGP6ZvXz72Htq4Zg==} + '@cloudflare/workerd-linux-arm64@1.20260226.1': + resolution: {integrity: sha512-kV8CWLdC9JoxjJu0ap63Fi38xANfrXCFu5ldkc6DOW/VyTSaFShVkNH12X4P6aB5VXR1JHFc5jnZvoEImrfArA==} engines: {node: '>=16'} cpu: [arm64] os: [linux] @@ -5341,8 +5338,8 @@ packages: cpu: [x64] os: [win32] - '@cloudflare/workerd-windows-64@1.20260305.0': - resolution: {integrity: sha512-BA0uaQPOaI2F6mJtBDqplGnQQhpXCzwEMI33p/TnDxtSk9u8CGIfBFuI6uqo8mJ6ijIaPjeBLGOn2CiRMET4qg==} + '@cloudflare/workerd-windows-64@1.20260226.1': + resolution: {integrity: sha512-bOCPxafwcEi+ShDNQNbCQqiSsg2F/rykeX5+0SIY80DIacPLXztnd02+hqGbCd+sb5DMnW9ovvrCNxOr0aJJMw==} engines: {node: '>=16'} cpu: [x64] os: [win32] @@ -9385,6 +9382,7 @@ packages: basic-ftp@5.0.5: resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} engines: {node: '>=10.0.0'} + deprecated: Security vulnerability fixed in 5.2.0, please upgrade bcrypt-pbkdf@1.0.2: resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} @@ -14903,8 +14901,8 @@ packages: engines: {node: '>=16'} hasBin: true - workerd@1.20260305.0: - resolution: {integrity: sha512-JkhfCLU+w+KbQmZ9k49IcDYc78GBo7eG8Mir8E2+KVjR7otQAmpcLlsous09YLh8WQ3Bt3Mi6/WMStvMAPukeA==} + workerd@1.20260226.1: + resolution: {integrity: sha512-veMyr994Cq0ExD0QCRH4JL3fZAs4mMjdodWA9Sfzk1aUyKI2VNOPixyVg0GJmJJGzbapbvKHLRgMq9obGImotw==} engines: {node: '>=16'} hasBin: true @@ -16439,7 +16437,7 @@ snapshots: devalue: 5.3.2 miniflare: 4.20251210.0 semver: 7.7.3 - vitest: 3.2.3(@types/debug@4.1.12)(@types/node@20.19.9)(@vitest/ui@3.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(msw@2.12.0(@types/node@20.19.9)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.1) + vitest: 3.2.3(@types/debug@4.1.12)(@types/node@20.19.9)(@vitest/ui@3.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(msw@2.12.0(@types/node@20.19.9)(typescript@5.8.3))(supports-color@9.2.2)(tsx@4.21.0)(yaml@2.8.1) wrangler: 4.54.0(@cloudflare/workers-types@4.20260305.0) zod: 3.25.76 transitivePeerDependencies: @@ -16453,7 +16451,7 @@ snapshots: '@cloudflare/workerd-darwin-64@1.20260218.0': optional: true - '@cloudflare/workerd-darwin-64@1.20260305.0': + '@cloudflare/workerd-darwin-64@1.20260226.1': optional: true '@cloudflare/workerd-darwin-arm64@1.20251210.0': @@ -16462,7 +16460,7 @@ snapshots: '@cloudflare/workerd-darwin-arm64@1.20260218.0': optional: true - '@cloudflare/workerd-darwin-arm64@1.20260305.0': + '@cloudflare/workerd-darwin-arm64@1.20260226.1': optional: true '@cloudflare/workerd-linux-64@1.20251210.0': @@ -16471,7 +16469,7 @@ snapshots: '@cloudflare/workerd-linux-64@1.20260218.0': optional: true - '@cloudflare/workerd-linux-64@1.20260305.0': + '@cloudflare/workerd-linux-64@1.20260226.1': optional: true '@cloudflare/workerd-linux-arm64@1.20251210.0': @@ -16480,7 +16478,7 @@ snapshots: '@cloudflare/workerd-linux-arm64@1.20260218.0': optional: true - '@cloudflare/workerd-linux-arm64@1.20260305.0': + '@cloudflare/workerd-linux-arm64@1.20260226.1': optional: true '@cloudflare/workerd-windows-64@1.20251210.0': @@ -16489,7 +16487,7 @@ snapshots: '@cloudflare/workerd-windows-64@1.20260218.0': optional: true - '@cloudflare/workerd-windows-64@1.20260305.0': + '@cloudflare/workerd-windows-64@1.20260226.1': optional: true '@cloudflare/workers-editor-shared@0.1.1(@cloudflare/style-const@5.7.3(react@19.2.1))(@cloudflare/style-container@7.12.2(@cloudflare/style-const@5.7.3(react@19.2.1))(react@19.2.1))(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': @@ -26944,13 +26942,13 @@ snapshots: '@cloudflare/workerd-linux-arm64': 1.20260218.0 '@cloudflare/workerd-windows-64': 1.20260218.0 - workerd@1.20260305.0: + workerd@1.20260226.1: optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20260305.0 - '@cloudflare/workerd-darwin-arm64': 1.20260305.0 - '@cloudflare/workerd-linux-64': 1.20260305.0 - '@cloudflare/workerd-linux-arm64': 1.20260305.0 - '@cloudflare/workerd-windows-64': 1.20260305.0 + '@cloudflare/workerd-darwin-64': 1.20260226.1 + '@cloudflare/workerd-darwin-arm64': 1.20260226.1 + '@cloudflare/workerd-linux-64': 1.20260226.1 + '@cloudflare/workerd-linux-arm64': 1.20260226.1 + '@cloudflare/workerd-windows-64': 1.20260226.1 wrangler@4.54.0(@cloudflare/workers-types@4.20260305.0): dependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index f5c1497599d6..d7321b3025f2 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -32,8 +32,8 @@ catalog: "ws": "8.18.0" esbuild: "0.27.3" playwright-chromium: "^1.56.1" - "@cloudflare/workers-types": "^4.20260305.0" - workerd: "1.20260305.0" + "@cloudflare/workers-types": "^4.20260226.1" + workerd: "1.20260226.1" eslint: "^9.39.1" jsonc-parser: "^3.2.0" smol-toml: "^1.5.2" diff --git a/tools/e2e/__tests__/common.test.ts b/tools/e2e/__tests__/common.test.ts index d121945e071a..fce1877e6a07 100644 --- a/tools/e2e/__tests__/common.test.ts +++ b/tools/e2e/__tests__/common.test.ts @@ -4,11 +4,13 @@ import { deleteDatabase, deleteKVNamespace, deleteProject, + deleteR2Bucket, deleteWorker, listTmpDatabases, listTmpE2EProjects, listTmpE2EWorkers, listTmpKVNamespaces, + listTmpR2Buckets, } from "../common"; const originalAccountID = process.env.CLOUDFLARE_ACCOUNT_ID; @@ -346,3 +348,59 @@ describe("deleteWorker()", () => { await deleteWorker(MOCK_WORKER); }); }); + +describe("listTmpR2Buckets()", () => { + it("makes a REST request and returns a filtered list of R2 buckets", async () => { + agent + .get("https://api.cloudflare.com") + .intercept({ + path: `/client/v4/accounts/${MOCK_CLOUDFLARE_ACCOUNT_ID}/r2/buckets`, + method: "GET", + }) + .reply( + 200, + JSON.stringify({ + result: { + buckets: [ + { name: "my-bucket-1", creation_date: nowStr }, + { name: "my-bucket-2", creation_date: oldTimeStr }, + { + name: "tmp-e2e-abc123-next--workers-opennext-cache", + creation_date: nowStr, + }, + { + name: "tmp-e2e-def456-next--workers-opennext-cache", + creation_date: oldTimeStr, + }, + { name: "tmp-e2e-project-1", creation_date: nowStr }, + { name: "tmp-e2e-project-2", creation_date: oldTimeStr }, + ], + }, + success: true, + }) + ); + + const result = await listTmpR2Buckets(); + + expect(result.map((b) => b.name)).toMatchInlineSnapshot(` + [ + "tmp-e2e-def456-next--workers-opennext-cache", + "tmp-e2e-project-2", + ] + `); + }); +}); + +describe("deleteR2Bucket()", () => { + it("makes a REST request to delete the given R2 bucket", async () => { + const MOCK_BUCKET = "tmp-e2e-abc123-next--workers-opennext-cache"; + agent + .get("https://api.cloudflare.com") + .intercept({ + path: `/client/v4/accounts/${MOCK_CLOUDFLARE_ACCOUNT_ID}/r2/buckets/${MOCK_BUCKET}`, + method: "DELETE", + }) + .reply(200, JSON.stringify({ result: [] })); + await deleteR2Bucket(MOCK_BUCKET); + }); +}); diff --git a/tools/e2e/common.ts b/tools/e2e/common.ts index c425dd32bbd1..14546c230662 100644 --- a/tools/e2e/common.ts +++ b/tools/e2e/common.ts @@ -16,6 +16,11 @@ export type Project = { created_on: string; }; +export type R2Bucket = { + name: string; + creation_date: string; +}; + export type ContainerApplication = { created_at: string; id: string; @@ -266,6 +271,30 @@ export const deleteKVNamespace = async (id: string) => { ); }; +export const listTmpR2Buckets = async () => { + const response = await apiFetchResponse(`/r2/buckets`, { method: "GET" }); + if (!response || !response.ok) { + return []; + } + const json = (await response.json()) as { + result: { buckets: R2Bucket[] }; + }; + return json.result.buckets.filter( + (bucket) => + bucket.name.startsWith("tmp-e2e-") && + // Buckets are more than an hour old + Date.now() - new Date(bucket.creation_date).valueOf() > 1000 * 60 * 60 + ); +}; + +export const deleteR2Bucket = async (name: string) => { + return await apiFetch( + `/r2/buckets/${name}`, + "DELETE", + /* failSilently */ true + ); +}; + export const listTmpDatabases = async () => { return (await apiFetchList(`/d1/database`)).filter( (db) => diff --git a/tools/e2e/e2eCleanup.ts b/tools/e2e/e2eCleanup.ts index bd974df6162d..ba83a035cc9e 100644 --- a/tools/e2e/e2eCleanup.ts +++ b/tools/e2e/e2eCleanup.ts @@ -6,6 +6,7 @@ import { deleteHyperdriveConfig, deleteKVNamespace, deleteProject, + deleteR2Bucket, deleteWorker, listCertificates, listE2eContainerImages, @@ -15,6 +16,7 @@ import { listTmpE2EProjects, listTmpE2EWorkers, listTmpKVNamespaces, + listTmpR2Buckets, } from "./common"; if (!process.env.CLOUDFLARE_API_TOKEN) { @@ -38,6 +40,8 @@ async function run() { // so delete these first to avoid interrupting running e2e jobs (unless you are very very unlucky) await deleteKVNamespaces(); + await deleteR2Buckets(); + await deleteProjects(); await deleteWorkers(); @@ -174,3 +178,20 @@ async function deleteContainerApplications() { console.log(`Deleted ${container.name} (${container.id})`); } } + +async function deleteR2Buckets() { + const bucketsToDelete = await listTmpR2Buckets(); + + for (const bucket of bucketsToDelete) { + console.log("Deleting R2 bucket: " + bucket.name); + if (await deleteR2Bucket(bucket.name)) { + console.log(`Successfully deleted R2 bucket ${bucket.name}`); + } else { + console.log(`Failed to delete R2 bucket ${bucket.name}`); + } + } + + if (bucketsToDelete.length === 0) { + console.log(`No R2 buckets to delete.`); + } +}