From 136b13c99f31b3c207b8b3e25cd297cae4d7fa21 Mon Sep 17 00:00:00 2001 From: yomna Date: Sat, 1 Aug 2026 11:45:28 -0400 Subject: [PATCH 1/3] [wrangler] Add PR metadata to preview deployments --- packages/deploy-helpers/src/preview/api.ts | 10 + .../deploy-helpers/src/preview/preview.ts | 129 ++++++++- packages/deploy-helpers/src/preview/shared.ts | 160 +++++++++++ .../wrangler/src/__tests__/preview.test.ts | 259 +++++++++++++++++- 4 files changed, 545 insertions(+), 13 deletions(-) diff --git a/packages/deploy-helpers/src/preview/api.ts b/packages/deploy-helpers/src/preview/api.ts index 6359b250ee1..e968d8687ff 100644 --- a/packages/deploy-helpers/src/preview/api.ts +++ b/packages/deploy-helpers/src/preview/api.ts @@ -79,6 +79,13 @@ export interface DeploymentResource { limits?: CfUserLimits; placement?: CfPlacement; cache?: CacheOptions; + annotations?: { + "workers/message"?: string; + "workers/pull_request_number"?: string; + "workers/pull_request_url"?: string; + "workers/repository_url"?: string; + "workers/tag"?: string; + }; env?: EnvBindings; created_on: string; } @@ -102,6 +109,9 @@ export type CreatePreviewDeploymentRequestParams = { compatibility_flags?: string[]; annotations?: { "workers/message"?: string; + "workers/pull_request_number"?: string; + "workers/pull_request_url"?: string; + "workers/repository_url"?: string; "workers/tag"?: string; }; migrations?: CfWorkerInit["migrations"]; diff --git a/packages/deploy-helpers/src/preview/preview.ts b/packages/deploy-helpers/src/preview/preview.ts index 7ab05b13432..ae16ed99851 100644 --- a/packages/deploy-helpers/src/preview/preview.ts +++ b/packages/deploy-helpers/src/preview/preview.ts @@ -27,9 +27,12 @@ import { getBranchName, getHeadCommitMessage, getHeadCommitRef, + getPullRequestMetadata, + getRepositoryUrl, resolveWorkerName, shouldUseCIMetadataFallback, } from "./shared"; +import type { PullRequestMetadata } from "./shared"; import type { WorkerBuildResult } from "../shared/types"; import type { Binding, @@ -81,6 +84,10 @@ type MergedVersionLevel = { value: Config["cache"]; fromConfig: boolean; }; + pull_request?: { + value: string; + fromConfig: false; + }; assets?: { value: { directory?: string; @@ -240,6 +247,8 @@ async function assemblePreviewDeploymentSettings( options: { message?: string; tag?: string; + repositoryUrl?: string; + pullRequest?: PullRequestMetadata; assetsOptions?: PreviewAssetsOptions; } ): Promise { @@ -276,9 +285,16 @@ async function assemblePreviewDeploymentSettings( if (config.compatibility_flags && config.compatibility_flags.length > 0) { request.compatibility_flags = config.compatibility_flags; } - if (options.message || options.tag) { + const repositoryUrl = options.repositoryUrl; + const pullRequest = options.pullRequest; + if (options.message || options.tag || repositoryUrl || pullRequest) { request.annotations = { ...(options.message && { "workers/message": options.message }), + ...(pullRequest?.number && { + "workers/pull_request_number": pullRequest.number, + }), + ...(pullRequest?.url && { "workers/pull_request_url": pullRequest.url }), + ...(repositoryUrl && { "workers/repository_url": repositoryUrl }), ...(options.tag && { "workers/tag": options.tag }), }; } @@ -374,7 +390,9 @@ function buildMergedScriptLevel( function buildMergedVersionLevel( config: Config, - deployment: DeploymentResource + deployment: DeploymentResource, + repositoryUrl?: string, + pullRequest?: PullRequestMetadata ): MergedVersionLevel { const previews = config.previews as PreviewsConfig | undefined; const configBindingNames = new Set( @@ -429,6 +447,17 @@ function buildMergedVersionLevel( fromConfig: previews?.cache !== undefined || config.cache !== undefined, }; } + const deploymentPullRequestUrl = + deployment.annotations?.["workers/pull_request_url"] ?? pullRequest?.url; + const deploymentPullRequestNumber = + deployment.annotations?.["workers/pull_request_number"] ?? + pullRequest?.number; + if (deploymentPullRequestUrl || deploymentPullRequestNumber) { + result.pull_request = { + value: deploymentPullRequestUrl ?? `#${deploymentPullRequestNumber}`, + fromConfig: false, + }; + } if (config.assets) { result.assets = { value: { @@ -448,6 +477,49 @@ function buildMergedVersionLevel( return result; } +function hasPreviewMetadataAnnotations( + request: CreatePreviewDeploymentRequestParams +): boolean { + return !!( + request.annotations?.["workers/pull_request_number"] || + request.annotations?.["workers/pull_request_url"] || + request.annotations?.["workers/repository_url"] + ); +} + +function omitPreviewMetadataAnnotations( + request: CreatePreviewDeploymentRequestParams +): CreatePreviewDeploymentRequestParams { + const annotations = { + ...(request.annotations?.["workers/message"] && { + "workers/message": request.annotations["workers/message"], + }), + ...(request.annotations?.["workers/tag"] && { + "workers/tag": request.annotations["workers/tag"], + }), + }; + + return { + ...request, + annotations: + Object.keys(annotations).length > 0 ? annotations : undefined, + }; +} + +function isPreviewMetadataAnnotationsUnsupportedError(error: unknown): boolean { + if (typeof error !== "object" || error === null) { + return false; + } + + const message = error instanceof Error ? error.message : String(error); + + return ( + message.includes("annotations not allowed") && + (message.includes("workers/pull_request") || + message.includes("workers/repository_url")) + ); +} + function formatPreviewResource( previewResource: PreviewResource, scriptLevel: MergedScriptLevel, @@ -565,6 +637,13 @@ function formatDeploymentResource( versionLevel.cache.fromConfig, ]); } + if (versionLevel.pull_request !== undefined) { + settingsRows.push([ + "pull_request", + versionLevel.pull_request.value, + versionLevel.pull_request.fromConfig, + ]); + } if (settingsRows.length > 0) { lines.push(""); lines.push(...formatAlignedRows(settingsRows)); @@ -682,6 +761,8 @@ export async function preview( !args.message && shouldUseCIMetadataFallback() ? getHeadCommitMessage() : undefined; + const repositoryUrl = getRepositoryUrl(); + const pullRequest = getPullRequestMetadata(); let existingPreview: PreviewResource | null = null; try { @@ -732,17 +813,38 @@ export async function preview( { message: args.message ?? fallbackMessage, tag: args.tag ?? fallbackTag, + repositoryUrl, + pullRequest, assetsOptions, } ); - const deployment = await createPreviewDeployment( - config, - accountId, - workerName, - previewResource.id, - deploymentRequest, - { ignoreDefaults } - ); + let deployment: DeploymentResource; + try { + deployment = await createPreviewDeployment( + config, + accountId, + workerName, + previewResource.id, + deploymentRequest, + { ignoreDefaults } + ); + } catch (error) { + if ( + hasPreviewMetadataAnnotations(deploymentRequest) && + isPreviewMetadataAnnotationsUnsupportedError(error) + ) { + deployment = await createPreviewDeployment( + config, + accountId, + workerName, + previewResource.id, + omitPreviewMetadataAnnotations(deploymentRequest), + { ignoreDefaults } + ); + } else { + throw error; + } + } if (args.json) { logger.log( @@ -750,7 +852,12 @@ export async function preview( ); } else { const scriptLevel = buildMergedScriptLevel(config, previewResource); - const versionLevel = buildMergedVersionLevel(config, deployment); + const versionLevel = buildMergedVersionLevel( + config, + deployment, + repositoryUrl, + pullRequest + ); const configName = configFileName(config.configPath); logger.log( formatPreviewResource( diff --git a/packages/deploy-helpers/src/preview/shared.ts b/packages/deploy-helpers/src/preview/shared.ts index 19fc69786a8..b5c089ae4b1 100644 --- a/packages/deploy-helpers/src/preview/shared.ts +++ b/packages/deploy-helpers/src/preview/shared.ts @@ -1,4 +1,5 @@ import { execSync } from "node:child_process"; +import { readFileSync } from "node:fs"; import { configFileName, getWorkersCIBranchName, @@ -55,6 +56,165 @@ export function getHeadCommitMessage(): string | undefined { } } +function normalizeRepositoryUrl(repositoryUrl: string): string | undefined { + const trimmed = repositoryUrl.trim(); + if (!trimmed) { + return undefined; + } + + const scpLikeSshMatch = trimmed.match(/^git@([^:]+):(.+)$/); + if (scpLikeSshMatch) { + const [, host, pathname] = scpLikeSshMatch; + return `https://${host}/${pathname.replace(/\.git$/, "")}`; + } + + try { + const url = new URL(trimmed); + if (url.protocol === "ssh:" && url.username === "git") { + return `https://${url.host}${url.pathname.replace(/\.git$/, "")}`; + } + + if (url.protocol !== "https:" && url.protocol !== "http:") { + return undefined; + } + + url.username = ""; + url.password = ""; + url.search = ""; + url.hash = ""; + url.pathname = url.pathname.replace(/\.git$/, ""); + return url.toString().replace(/\/$/, ""); + } catch { + return undefined; + } +} + +export function getRepositoryUrl(): string | undefined { + const repositoryUrl = + process.env.CI_PROJECT_URL ?? + process.env.CI_REPOSITORY_URL ?? + process.env.CIRCLE_REPOSITORY_URL ?? + process.env.BUILDKITE_REPO ?? + process.env.BITBUCKET_GIT_HTTP_ORIGIN ?? + process.env.BITBUCKET_GIT_SSH_ORIGIN ?? + process.env.REPOSITORY_URL; + if (repositoryUrl) { + return normalizeRepositoryUrl(repositoryUrl); + } + + if (process.env.GITHUB_REPOSITORY) { + const githubServerUrl = process.env.GITHUB_SERVER_URL ?? "https://github.com"; + return normalizeRepositoryUrl( + `${githubServerUrl.replace(/\/$/, "")}/${process.env.GITHUB_REPOSITORY}` + ); + } + + try { + execSync(`git rev-parse --is-inside-work-tree`, { stdio: "ignore" }); + return normalizeRepositoryUrl( + execSync(`git config --get remote.origin.url`).toString() + ); + } catch { + return undefined; + } +} + +export type PullRequestMetadata = { + number?: string; + url?: string; +}; + +function normalizePullRequestNumber(number: string | number | undefined) { + if (number === undefined) { + return undefined; + } + + const normalizedNumber = String(number).trim(); + return normalizedNumber ? normalizedNumber : undefined; +} + +function getGitHubPullRequestMetadata(): PullRequestMetadata | undefined { + if (process.env.GITHUB_EVENT_PATH) { + try { + const event = JSON.parse( + readFileSync(process.env.GITHUB_EVENT_PATH, "utf8") + ) as { + pull_request?: { html_url?: string; number?: number }; + }; + const number = normalizePullRequestNumber(event.pull_request?.number); + const url = event.pull_request?.html_url + ? normalizeRepositoryUrl(event.pull_request.html_url) + : undefined; + if (number || url) { + return { number, url }; + } + } catch { + // Fall back to environment-derived metadata below. + } + } + + const refPullRequestNumber = process.env.GITHUB_REF?.match( + /^refs\/pull\/(\d+)\// + )?.[1]; + const number = normalizePullRequestNumber(refPullRequestNumber); + if (!number || !process.env.GITHUB_REPOSITORY) { + return undefined; + } + + const githubServerUrl = process.env.GITHUB_SERVER_URL ?? "https://github.com"; + return { + number, + url: normalizeRepositoryUrl( + `${githubServerUrl.replace(/\/$/, "")}/${process.env.GITHUB_REPOSITORY}/pull/${number}` + ), + }; +} + +function getGitLabPullRequestMetadata(): PullRequestMetadata | undefined { + const number = normalizePullRequestNumber(process.env.CI_MERGE_REQUEST_IID); + const projectUrl = + process.env.CI_MERGE_REQUEST_PROJECT_URL ?? process.env.CI_PROJECT_URL; + if (!number || !projectUrl) { + return undefined; + } + + const normalizedProjectUrl = projectUrl.replace(/\.git$/, "").replace(/\/$/, ""); + return { + number, + url: normalizeRepositoryUrl( + `${normalizedProjectUrl}/-/merge_requests/${number}` + ), + }; +} + +function getDirectPullRequestMetadata(): PullRequestMetadata | undefined { + const directUrl = + process.env.PULL_REQUEST_URL ?? + process.env.PR_URL ?? + process.env.CHANGE_URL ?? + process.env.CIRCLE_PULL_REQUEST; + const number = normalizePullRequestNumber( + process.env.PULL_REQUEST_NUMBER ?? + process.env.PR_NUMBER ?? + process.env.CHANGE_ID + ); + const url = directUrl ? normalizeRepositoryUrl(directUrl) : undefined; + + if (number || url) { + return { number, url }; + } + + return undefined; +} + +export function getPullRequestMetadata(): PullRequestMetadata | undefined { + return ( + getDirectPullRequestMetadata() ?? + getGitHubPullRequestMetadata() ?? + getGitLabPullRequestMetadata() + ); +} + export function resolveWorkerName( args: { workerName?: string; "worker-name"?: string }, config: Config diff --git a/packages/wrangler/src/__tests__/preview.test.ts b/packages/wrangler/src/__tests__/preview.test.ts index 6165c3785b7..2ece916087a 100644 --- a/packages/wrangler/src/__tests__/preview.test.ts +++ b/packages/wrangler/src/__tests__/preview.test.ts @@ -4,6 +4,8 @@ import { stripVTControlCharacters } from "node:util"; import { extractConfigBindings, getBranchName, + getPullRequestMetadata, + getRepositoryUrl, } from "@cloudflare/deploy-helpers"; import { defaultWranglerConfig } from "@cloudflare/workers-utils"; import { runInTempDir } from "@cloudflare/workers-utils/test-helpers"; @@ -37,6 +39,29 @@ function configWithPreviews(previews: PreviewsConfig): Config { }; } +function clearPreviewMetadataEnvs() { + vi.stubEnv("GITHUB_REPOSITORY", undefined); + vi.stubEnv("GITHUB_SERVER_URL", undefined); + vi.stubEnv("GITHUB_EVENT_PATH", undefined); + vi.stubEnv("GITHUB_REF", undefined); + vi.stubEnv("CI_PROJECT_URL", undefined); + vi.stubEnv("CI_REPOSITORY_URL", undefined); + vi.stubEnv("CI_MERGE_REQUEST_IID", undefined); + vi.stubEnv("CI_MERGE_REQUEST_PROJECT_URL", undefined); + vi.stubEnv("CIRCLE_REPOSITORY_URL", undefined); + vi.stubEnv("CIRCLE_PULL_REQUEST", undefined); + vi.stubEnv("BUILDKITE_REPO", undefined); + vi.stubEnv("BITBUCKET_GIT_HTTP_ORIGIN", undefined); + vi.stubEnv("BITBUCKET_GIT_SSH_ORIGIN", undefined); + vi.stubEnv("REPOSITORY_URL", undefined); + vi.stubEnv("PULL_REQUEST_URL", undefined); + vi.stubEnv("PULL_REQUEST_NUMBER", undefined); + vi.stubEnv("PR_URL", undefined); + vi.stubEnv("PR_NUMBER", undefined); + vi.stubEnv("CHANGE_URL", undefined); + vi.stubEnv("CHANGE_ID", undefined); +} + describe("wrangler preview", () => { const std = mockConsoleMethods(); runInTempDir(); @@ -46,13 +71,14 @@ describe("wrangler preview", () => { clearOutputFilePath(); }); - describe("getBranchName", () => { + describe("getBranchName", () => { beforeEach(() => { vi.unstubAllEnvs(); vi.stubEnv("WORKERS_CI_BRANCH", undefined); vi.stubEnv("GITHUB_REF_NAME", undefined); vi.stubEnv("GITHUB_HEAD_REF", undefined); vi.stubEnv("CI_COMMIT_REF_NAME", undefined); + clearPreviewMetadataEnvs(); }); afterAll(() => { @@ -84,6 +110,112 @@ describe("wrangler preview", () => { }); }); + describe("getRepositoryUrl", () => { + beforeEach(() => { + vi.unstubAllEnvs(); + clearPreviewMetadataEnvs(); + }); + + afterAll(() => { + vi.unstubAllEnvs(); + }); + + test("should use GitHub Actions repository env vars", ({ expect }) => { + vi.stubEnv("GITHUB_REPOSITORY", "cloudflare/workers-sdk"); + + expect(getRepositoryUrl()).toBe( + "https://github.com/cloudflare/workers-sdk" + ); + }); + + test("should use GitHub Enterprise server URL", ({ expect }) => { + vi.stubEnv("GITHUB_SERVER_URL", "https://github.example.com/"); + vi.stubEnv("GITHUB_REPOSITORY", "cloudflare/workers-sdk"); + + expect(getRepositoryUrl()).toBe( + "https://github.example.com/cloudflare/workers-sdk" + ); + }); + + test("should use GitLab project URL", ({ expect }) => { + vi.stubEnv( + "CI_PROJECT_URL", + "https://gitlab.example.com/cloudflare/workers-sdk.git" + ); + + expect(getRepositoryUrl()).toBe( + "https://gitlab.example.com/cloudflare/workers-sdk" + ); + }); + + test("should use and normalize git remote origin URL", ({ expect }) => { + vi.mocked(childProcess.execSync) + .mockImplementationOnce(() => Buffer.from("true")) + .mockImplementationOnce(() => + Buffer.from("git@git.example.com:acme/worker-project.git\n") + ); + + expect(getRepositoryUrl()).toBe( + "https://git.example.com/acme/worker-project" + ); + }); + }); + + describe("getPullRequestMetadata", () => { + beforeEach(() => { + vi.unstubAllEnvs(); + clearPreviewMetadataEnvs(); + }); + + afterAll(() => { + vi.unstubAllEnvs(); + }); + + test("should use direct pull request URL env vars", ({ expect }) => { + vi.stubEnv( + "PULL_REQUEST_URL", + "https://git.example.com/acme/worker-project/pulls/13" + ); + vi.stubEnv("PULL_REQUEST_NUMBER", "13"); + + expect(getPullRequestMetadata()).toEqual({ + number: "13", + url: "https://git.example.com/acme/worker-project/pulls/13", + }); + }); + + test("should use GitHub event pull request metadata", ({ expect }) => { + writeFileSync( + "github-event.json", + JSON.stringify({ + pull_request: { + number: 13, + html_url: "https://github.com/acme/worker-project/pull/13", + }, + }) + ); + vi.stubEnv("GITHUB_EVENT_PATH", "github-event.json"); + + expect(getPullRequestMetadata()).toEqual({ + number: "13", + url: "https://github.com/acme/worker-project/pull/13", + }); + }); + + test("should use GitLab merge request metadata", ({ expect }) => { + vi.stubEnv( + "CI_PROJECT_URL", + "https://gitlab.example.com/acme/worker-project" + ); + vi.stubEnv("CI_MERGE_REQUEST_IID", "13"); + + expect(getPullRequestMetadata()).toEqual({ + number: "13", + url: "https://gitlab.example.com/acme/worker-project/-/merge_requests/13", + }); + }); + }); + describe("extractConfigBindings", () => { test("should extract vars as plain_text bindings", ({ expect }) => { const config = configWithPreviews({ @@ -290,6 +422,7 @@ describe("wrangler preview", () => { describe("preview command", () => { beforeEach(() => { vi.stubEnv("CI", undefined); + clearPreviewMetadataEnvs(); mkdirSync("src", { recursive: true }); writeFileSync( "src/index.ts", @@ -2363,13 +2496,22 @@ describe("wrangler preview", () => { expect(deploymentRequestBody?.migrations?.old_tag).toBeUndefined(); }); - test("should include deployment annotations from message and tag args", async ({ + test("should include deployment annotations from metadata and args", async ({ expect, }) => { + vi.stubEnv( + "CI_PROJECT_URL", + "https://gitlab.example.com/acme/worker-project.git" + ); + vi.stubEnv("CI_MERGE_REQUEST_IID", "13"); + let deploymentRequestBody: | (Record & { annotations?: { "workers/message"?: string; + "workers/pull_request_number"?: string; + "workers/pull_request_url"?: string; + "workers/repository_url"?: string; "workers/tag"?: string; }; }) @@ -2435,9 +2577,122 @@ describe("wrangler preview", () => { ); expect(deploymentRequestBody?.annotations).toEqual({ + "workers/message": "preview note", + "workers/pull_request_number": "13", + "workers/pull_request_url": + "https://gitlab.example.com/acme/worker-project/-/merge_requests/13", + "workers/repository_url": "https://gitlab.example.com/acme/worker-project", + "workers/tag": "v1.2.3", + }); + expect(std.out).toContain("pull_request"); + expect(std.out).toContain( + "https://gitlab.example.com/acme/worker-project/-/merge_requests/13" + ); + expect(std.out).not.toContain("repository_url"); + }); + + test("should show pull request output when the API rejects preview metadata annotations", async ({ + expect, + }) => { + vi.stubEnv( + "PULL_REQUEST_URL", + "https://git.example.com/acme/worker-project/pulls/13" + ); + vi.stubEnv("PULL_REQUEST_NUMBER", "13"); + + let deploymentRequests = 0; + let fallbackRequestBody: + | (Record & { + annotations?: Record; + }) + | undefined; + + msw.use( + http.get( + `*/accounts/:accountId/workers/workers/:workerId/previews/:previewId`, + () => + HttpResponse.json( + { + success: false, + result: null, + errors: [{ code: 10025, message: "Preview not found" }], + }, + { status: 404 } + ) + ), + http.post( + `*/accounts/:accountId/workers/workers/:workerId/previews`, + () => + HttpResponse.json( + { + success: true, + result: { + id: "preview-id-annotations-fallback", + name: "test-preview", + slug: "test-preview", + urls: ["https://test-preview.test-worker.cloudflare.app"], + worker_name: "test-worker", + created_on: new Date().toISOString(), + }, + }, + { status: 201 } + ) + ), + http.post( + `*/accounts/:accountId/workers/workers/:workerId/previews/:previewId/deployments`, + async ({ request }) => { + deploymentRequests++; + const body = (await request.json()) as typeof fallbackRequestBody; + if (deploymentRequests === 1) { + return HttpResponse.json( + { + success: false, + result: null, + errors: [ + { + code: 10021, + message: + "annotations not allowed: workers/pull_request_number, workers/pull_request_url, workers/repository_url", + }, + ], + }, + { status: 400 } + ); + } + + fallbackRequestBody = body; + return HttpResponse.json( + { + success: true, + result: { + id: "deployment-id-annotations-fallback", + preview_id: "preview-id-annotations-fallback", + preview_name: "test-preview", + urls: ["https://fallback123.test-worker.cloudflare.app"], + compatibility_date: "2025-01-01", + env: {}, + created_on: new Date().toISOString(), + }, + }, + { status: 201 } + ); + } + ) + ); + + await runWrangler( + 'preview --name test-preview --tag v1.2.3 --message "preview note"' + ); + + expect(deploymentRequests).toBe(2); + expect(fallbackRequestBody?.annotations).toEqual({ "workers/message": "preview note", "workers/tag": "v1.2.3", }); + expect(std.out).toContain("pull_request"); + expect(std.out).toContain( + "https://git.example.com/acme/worker-project/pulls/13" + ); }); test("should fall back to HEAD commit metadata for annotations in CI", async ({ From 48af4570edac0e5bf7444f480456cb2efea5274c Mon Sep 17 00:00:00 2001 From: yomna Date: Sat, 1 Aug 2026 16:50:42 -0400 Subject: [PATCH 2/3] Handle preview metadata annotation errors --- .../deploy-helpers/src/preview/preview.ts | 41 +++++++++++++++++-- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/packages/deploy-helpers/src/preview/preview.ts b/packages/deploy-helpers/src/preview/preview.ts index ae16ed99851..11abbab4842 100644 --- a/packages/deploy-helpers/src/preview/preview.ts +++ b/packages/deploy-helpers/src/preview/preview.ts @@ -1,5 +1,6 @@ import path from "node:path"; import { + APIError, configFileName, getBindingTypeFriendlyName, UserError, @@ -511,15 +512,47 @@ function isPreviewMetadataAnnotationsUnsupportedError(error: unknown): boolean { return false; } - const message = error instanceof Error ? error.message : String(error); + const code = "code" in error ? error.code : undefined; + if (code !== undefined && code !== 10021) { + return false; + } + + const messages = [ + error instanceof Error ? error.message : undefined, + error instanceof APIError ? error.text : undefined, + ...getErrorNoteTexts(error), + ] + .filter(Boolean) + .join("\n"); return ( - message.includes("annotations not allowed") && - (message.includes("workers/pull_request") || - message.includes("workers/repository_url")) + messages.includes("annotations not allowed") && + (messages.includes("workers/pull_request") || + messages.includes("workers/repository_url")) ); } +function getErrorNoteTexts(error: unknown): string[] { + if (typeof error !== "object" || error === null || !("notes" in error)) { + return []; + } + + const notes = error.notes; + if (!Array.isArray(notes)) { + return []; + } + + return notes.flatMap((note) => [ + typeof note === "object" && + note !== null && + "text" in note && + typeof note.text === "string" + ? note.text + : undefined, + ...getErrorNoteTexts(note), + ]).filter((text): text is string => text !== undefined); +} + function formatPreviewResource( previewResource: PreviewResource, scriptLevel: MergedScriptLevel, From 1e2c674949d981369190665b143dfbca5aa19ec4 Mon Sep 17 00:00:00 2001 From: yomna Date: Sat, 1 Aug 2026 17:39:49 -0400 Subject: [PATCH 3/3] Isolate preview metadata tests from CI env --- .../wrangler/src/__tests__/preview.test.ts | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/wrangler/src/__tests__/preview.test.ts b/packages/wrangler/src/__tests__/preview.test.ts index 2ece916087a..92faf0c70de 100644 --- a/packages/wrangler/src/__tests__/preview.test.ts +++ b/packages/wrangler/src/__tests__/preview.test.ts @@ -40,26 +40,26 @@ function configWithPreviews(previews: PreviewsConfig): Config { } function clearPreviewMetadataEnvs() { - vi.stubEnv("GITHUB_REPOSITORY", undefined); - vi.stubEnv("GITHUB_SERVER_URL", undefined); - vi.stubEnv("GITHUB_EVENT_PATH", undefined); - vi.stubEnv("GITHUB_REF", undefined); - vi.stubEnv("CI_PROJECT_URL", undefined); - vi.stubEnv("CI_REPOSITORY_URL", undefined); - vi.stubEnv("CI_MERGE_REQUEST_IID", undefined); - vi.stubEnv("CI_MERGE_REQUEST_PROJECT_URL", undefined); - vi.stubEnv("CIRCLE_REPOSITORY_URL", undefined); - vi.stubEnv("CIRCLE_PULL_REQUEST", undefined); - vi.stubEnv("BUILDKITE_REPO", undefined); - vi.stubEnv("BITBUCKET_GIT_HTTP_ORIGIN", undefined); - vi.stubEnv("BITBUCKET_GIT_SSH_ORIGIN", undefined); - vi.stubEnv("REPOSITORY_URL", undefined); - vi.stubEnv("PULL_REQUEST_URL", undefined); - vi.stubEnv("PULL_REQUEST_NUMBER", undefined); - vi.stubEnv("PR_URL", undefined); - vi.stubEnv("PR_NUMBER", undefined); - vi.stubEnv("CHANGE_URL", undefined); - vi.stubEnv("CHANGE_ID", undefined); + vi.stubEnv("GITHUB_REPOSITORY", ""); + vi.stubEnv("GITHUB_SERVER_URL", ""); + vi.stubEnv("GITHUB_EVENT_PATH", ""); + vi.stubEnv("GITHUB_REF", ""); + vi.stubEnv("CI_PROJECT_URL", ""); + vi.stubEnv("CI_REPOSITORY_URL", ""); + vi.stubEnv("CI_MERGE_REQUEST_IID", ""); + vi.stubEnv("CI_MERGE_REQUEST_PROJECT_URL", ""); + vi.stubEnv("CIRCLE_REPOSITORY_URL", ""); + vi.stubEnv("CIRCLE_PULL_REQUEST", ""); + vi.stubEnv("BUILDKITE_REPO", ""); + vi.stubEnv("BITBUCKET_GIT_HTTP_ORIGIN", ""); + vi.stubEnv("BITBUCKET_GIT_SSH_ORIGIN", ""); + vi.stubEnv("REPOSITORY_URL", ""); + vi.stubEnv("PULL_REQUEST_URL", ""); + vi.stubEnv("PULL_REQUEST_NUMBER", ""); + vi.stubEnv("PR_URL", ""); + vi.stubEnv("PR_NUMBER", ""); + vi.stubEnv("CHANGE_URL", ""); + vi.stubEnv("CHANGE_ID", ""); } describe("wrangler preview", () => {