Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/add-preview-output-entry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"wrangler": minor
---

Add `preview` output-file entries for `wrangler preview` deployments

`wrangler preview` now writes a `preview` entry to the Wrangler output file when `WRANGLER_OUTPUT_FILE_PATH` or `WRANGLER_OUTPUT_FILE_DIRECTORY` is configured. The entry includes the Worker name, preview metadata (`preview_id`, `preview_name`, `preview_slug`, `preview_urls`) and deployment metadata (`deployment_id`, `deployment_urls`).

This makes preview command runs machine-readable in the same output stream as other Wrangler commands, which helps CI integrations consume preview URLs and IDs directly.
32 changes: 32 additions & 0 deletions packages/wrangler/src/__tests__/output.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,38 @@ describe("writeOutput()", () => {
]);
});

it("should write preview outputs with separate preview and deployment URLs", () => {
const WRANGLER_OUTPUT_FILE_PATH = "output.json";
vi.stubEnv("WRANGLER_OUTPUT_FILE_DIRECTORY", "");
vi.stubEnv("WRANGLER_OUTPUT_FILE_PATH", WRANGLER_OUTPUT_FILE_PATH);
writeOutput({
type: "preview",
version: 1,
worker_name: "worker",
preview_id: "preview-id",
preview_name: "branch-name",
preview_slug: "branch-name",
preview_urls: ["https://branch-name.worker.cloudflare.app"],
deployment_id: "deployment-id",
deployment_urls: ["https://abc12345.worker.cloudflare.app"],
});

const outputFile = readFileSync(WRANGLER_OUTPUT_FILE_PATH, "utf8");
expect(outputFile).toContainEntries([
{
type: "preview",
version: 1,
worker_name: "worker",
preview_id: "preview-id",
preview_name: "branch-name",
preview_slug: "branch-name",
preview_urls: ["https://branch-name.worker.cloudflare.app"],
deployment_id: "deployment-id",
deployment_urls: ["https://abc12345.worker.cloudflare.app"],
},
]);
});

it("should write an error log when a handler throws an error", async () => {
vi.mock("../user/whoami", () => {
return {
Expand Down
34 changes: 31 additions & 3 deletions packages/wrangler/src/__tests__/preview.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import * as childProcess from "node:child_process";
import { mkdirSync, writeFileSync } from "node:fs";
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { stripVTControlCharacters } from "node:util";
import { defaultWranglerConfig } from "@cloudflare/workers-utils";
import { http, HttpResponse } from "msw";
import { afterAll, beforeEach, describe, test, vi } from "vitest";
import { afterAll, afterEach, beforeEach, describe, test, vi } from "vitest";
import { clearOutputFilePath } from "../output";
import { extractConfigBindings, getBranchName } from "../preview/shared";
import { mockAccountId, mockApiToken } from "./helpers/mock-account-id";
import { mockConsoleMethods } from "./helpers/mock-console";
Expand All @@ -14,6 +15,7 @@ import {
writeRedirectedWranglerConfig,
writeWranglerConfig,
} from "./helpers/write-wrangler-config";
import type { OutputEntry } from "../output";
import type { Config, PreviewsConfig } from "@cloudflare/workers-utils";

vi.mock("node:child_process", async () => {
Expand All @@ -37,6 +39,10 @@ describe("wrangler preview", () => {
runInTempDir();
mockApiToken();
mockAccountId();
afterEach(() => {
clearOutputFilePath();
});

describe("getBranchName", () => {
beforeEach(() => {
vi.unstubAllEnvs();
Expand Down Expand Up @@ -502,6 +508,7 @@ describe("wrangler preview", () => {
test("should output preview and deployment JSON with --json", async ({
expect,
}) => {
const outputFile = "./output.json";
msw.use(
http.get(
`*/accounts/:accountId/workers/workers/:workerId/previews/:previewId`,
Expand Down Expand Up @@ -554,14 +561,35 @@ describe("wrangler preview", () => {
)
);

await runWrangler("preview --name test-preview --json");
await runWrangler("preview --name test-preview --json", {
...process.env,
WRANGLER_OUTPUT_FILE_PATH: outputFile,
});

expect(std.out).toContain('"preview"');
expect(std.out).toContain('"deployment"');
expect(std.out).toContain('"id": "preview-id-json"');
expect(std.out).toContain('"id": "deployment-id-json"');
expect(std.out).not.toContain("Preview: test-preview");
expect(std.out).not.toContain("Deployment:");

const outputEntries = readFileSync(outputFile, "utf8")
.split("\n")
.filter(Boolean)
.map((line) => JSON.parse(line)) as OutputEntry[];

expect(outputEntries).toContainEqual(
expect.objectContaining({
type: "preview",
worker_name: "test-worker",
preview_id: "preview-id-json",
preview_name: "test-preview",
preview_slug: "test-preview",
preview_urls: ["https://test-preview.test-worker.cloudflare.app"],
deployment_id: "deployment-id-json",
deployment_urls: ["https://json123.test-worker.cloudflare.app"],
})
);
});

test("should build correctly when using a redirected config", async ({
Expand Down
19 changes: 19 additions & 0 deletions packages/wrangler/src/output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ interface OutputEntryBase<T extends string> {
export type OutputEntry =
| OutputEntrySession
| OutputEntryDeployment
| OutputEntryPreview
| OutputEntryPagesDeployment
| OutputEntryVersionUpload
| OutputEntryVersionDeployment
Expand Down Expand Up @@ -102,6 +103,24 @@ interface OutputEntryDeployment extends OutputEntryBase<"deploy"> {
wrangler_environment: string | undefined;
}

interface OutputEntryPreview extends OutputEntryBase<"preview"> {
version: 1;
/** The name of the Worker. */
worker_name: string | null;
/** The ID of the Preview resource. */
preview_id: string;
/** The human-readable name of the Preview resource. */
preview_name: string;
/** The slug of the Preview resource. */
preview_slug: string;
/** A list of URLs associated with the Preview resource. */
preview_urls: string[] | undefined;
/** The ID of the Preview deployment resource. */
deployment_id: string;
/** A list of URLs associated with the Preview deployment. */
deployment_urls: string[] | undefined;
}

interface OutputEntryAutoConfig extends OutputEntryBase<"autoconfig"> {
version: 1;
/** The command that triggered autoconfig */
Expand Down
13 changes: 13 additions & 0 deletions packages/wrangler/src/preview/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { loadSourceMaps } from "../deployment-bundle/source-maps";
import { confirm } from "../dialogs";
import { logger } from "../logger";
import { isNavigatorDefined } from "../navigator-user-agent";
import { writeOutput } from "../output";
import { requireAuth } from "../user";
import {
drawBox,
Expand Down Expand Up @@ -845,6 +846,18 @@ export async function handlePreviewCommand(
{ ignoreDefaults }
);

writeOutput({
type: "preview",
version: 1,
worker_name: workerName,
preview_id: preview.id,
preview_name: preview.name,
preview_slug: preview.slug,
preview_urls: preview.urls,
deployment_id: deployment.id,
deployment_urls: deployment.urls,
});

if (args.json) {
logger.log(JSON.stringify({ preview, deployment }, null, 2));
return;
Expand Down
Loading