Skip to content
Open
Show file tree
Hide file tree
Changes from 5 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
5 changes: 5 additions & 0 deletions .changeset/workers-previews.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wrangler-action": minor
---

Add support for Workers Previews, including Preview artifact parsing, Preview outputs, GitHub Deployments, and job summaries for `wrangler preview`.
74 changes: 74 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,80 @@ jobs:
command: versions upload
```

### Deploy a Workers Preview

Workers Previews let you test non-production branches with their own URLs, variables, secrets, and bindings. Use `command: preview` instead of the default `deploy` command.

Before using this workflow, add your Cloudflare credentials as GitHub Actions secrets:

```sh
gh auth login
gh secret set CLOUDFLARE_API_TOKEN
gh secret set CLOUDFLARE_ACCOUNT_ID
```

GitHub Actions provides `${{ secrets.GITHUB_TOKEN }}` automatically. You do not need to create it yourself.

If you prefer plain YAML without this action, run Wrangler directly:

```yaml
name: Preview

on: [pull_request]

jobs:
preview:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- run: npx wrangler preview --json
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
```

Add install or build steps before `npx wrangler preview --json` if your Worker needs them. To use `wrangler-action` instead, use this workflow:

```yaml
name: Preview

on:
pull_request:
types: [opened, synchronize, reopened, closed]

permissions:
contents: read
deployments: write

jobs:
preview:
if: github.event.action != 'closed'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Deploy preview
id: preview
uses: cloudflare/wrangler-action@v4
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: preview
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
```

The action sets the following outputs for preview commands:

| Output | Description |
| ------------------------ | -------------------------------------------------- |
| `deployment-url` | The stable preview URL (same as `preview-url`) |
| `preview-url` | The stable Preview URL for the branch |
| `preview-deployment-url` | The immutable URL for this specific deployment |
| `preview-name` | The Preview name (defaults to the git branch name) |
| `preview-id` | The Preview resource ID |
| `preview-deployment-id` | The deployment ID within the Preview |

When `gitHubToken` is provided, the action creates a GitHub Deployment with the Preview URL linked as the environment URL and writes a job summary. For the plain `wrangler preview --json` workflow, PR comments, and cleanup examples, refer to [Automation examples](https://developers.cloudflare.com/workers/previews/automation-examples/).

## Advanced Usage

### Setting A Worker Secret for A Specific Environment
Expand Down
10 changes: 10 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,13 @@ outputs:
description: "If the command was a Pages deployment, this will be the ID of the deployment - needs wrangler >= 3.81.0"
pages-environment:
description: "If the command was a Pages deployment, this will be the environment of the deployment - needs wrangler >= 3.81.0"
preview-url:
description: "If the command was a Workers Preview deployment, this will be the stable Preview URL for the branch"
preview-deployment-url:
description: "If the command was a Workers Preview deployment, this will be the immutable deployment URL"
preview-name:
description: "If the command was a Workers Preview deployment, this will be the Preview name (typically the branch name)"
preview-id:
description: "If the command was a Workers Preview deployment, this will be the Preview resource ID"
preview-deployment-id:
description: "If the command was a Workers Preview deployment, this will be the deployment ID within the Preview"
41 changes: 40 additions & 1 deletion src/commandOutputParsing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@ import {
getOutputEntry,
OutputEntryDeployment,
OutputEntryPagesDeployment,
OutputEntryPreview,
OutputEntryVersionUpload,
} from "./wranglerArtifactManager";
import { createGitHubDeploymentAndJobSummary } from "./service/github";
import {
createGitHubDeploymentAndJobSummary,
createPreviewGitHubDeploymentAndJobSummary,
} from "./service/github";

// fallback to trying to extract the deployment-url and pages-deployment-alias-url from stdout for wranglerVersion < 3.81.0
function extractDeploymentUrlsFromStdout(stdOut: string): {
Expand Down Expand Up @@ -114,6 +118,27 @@ function handleVersionsUploadOutputEntry(
setOutput("deployment-url", versionsOutputEntry.preview_url);
}

async function handlePreviewOutputEntry(
config: WranglerActionConfig,
previewOutputEntry: OutputEntryPreview,
) {
const previewUrl = previewOutputEntry.preview_urls?.[0] ?? undefined;
const deploymentUrl = previewOutputEntry.deployment_urls?.[0] ?? undefined;

// Set the primary deployment-url to the preview URL (stable branch URL)
setOutput("deployment-url", previewUrl);

// Set preview-specific outputs
setOutput("preview-url", previewUrl);
setOutput("preview-deployment-url", deploymentUrl);
setOutput("preview-name", previewOutputEntry.preview_name);
setOutput("preview-id", previewOutputEntry.preview_id);
setOutput("preview-deployment-id", previewOutputEntry.deployment_id);

// Create GitHub Deployment and Job Summary for the preview
await createPreviewGitHubDeploymentAndJobSummary(config, previewOutputEntry);
}

/**
* If no wrangler output file found, log a message stating deployment-url will be unavailable for output.
* @deprecated Use {@link handleVersionsOutputEntry} instead.
Expand Down Expand Up @@ -150,6 +175,17 @@ function handleDeprectatedStdoutParsing(
handleVersionsOutputCommand(config);
return;
}

// Check if this command is a preview deployment
if (command.startsWith("preview")) {
info(
config,
"Unable to find a WRANGLER_OUTPUT_DIR, preview outputs will be unavailable. Have you updated wrangler to the latest version?",
);
const { deploymentUrl } = extractDeploymentUrlsFromStdout(stdOut);
setOutput("deployment-url", deploymentUrl);
return;
}
}

export async function handleCommandOutputParsing(
Expand All @@ -176,5 +212,8 @@ export async function handleCommandOutputParsing(
case "version-upload":
handleVersionsUploadOutputEntry(outputEntry);
break;
case "preview":
await handlePreviewOutputEntry(config, outputEntry);
break;
}
}
108 changes: 107 additions & 1 deletion src/service/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import { summary } from "@actions/core";
import { context, getOctokit } from "@actions/github";
import { env } from "process";
import { info, warn } from "../utils";
import { OutputEntryPagesDeployment } from "../wranglerArtifactManager";
import {
OutputEntryPagesDeployment,
OutputEntryPreview,
} from "../wranglerArtifactManager";
import { WranglerActionConfig } from "../wranglerAction";

type Octokit = ReturnType<typeof getOctokit>;
Expand Down Expand Up @@ -126,3 +129,106 @@ export async function createGitHubDeploymentAndJobSummary(
}
}
}

export async function createPreviewJobSummary({
previewName,
previewUrl,
deploymentUrl,
workerName,
}: {
previewName: string;
previewUrl?: string;
deploymentUrl?: string;
workerName: string | null;
}) {
await summary
.addRaw(
`
# Workers Preview Deployment

| Name | Result |
| ----------------------- | - |
| **Worker:** | ${workerName ?? "unknown"} |
| **Preview:** | ${previewName} |
| **Preview URL**: | ${previewUrl ?? "N/A"} |
| **Deployment URL**: | ${deploymentUrl ?? "N/A"} |
`,
)
.write();
}

/**
* Create GitHub deployment and job summary for a Workers Preview, if GITHUB_TOKEN is present
*/
export async function createPreviewGitHubDeploymentAndJobSummary(
config: WranglerActionConfig,
previewFields: OutputEntryPreview,
) {
const previewUrl = previewFields.preview_urls?.[0];
const deploymentUrl = previewFields.deployment_urls?.[0];

if (config.GITHUB_TOKEN) {
const octokit = getOctokit(config.GITHUB_TOKEN);
const githubBranch = env.GITHUB_HEAD_REF || env.GITHUB_REF_NAME;
const environmentName = `preview: ${previewFields.preview_name}`;

const [createDeploymentRes, createSummaryRes] = await Promise.allSettled([
(async () => {
const deployment = await octokit.rest.repos.createDeployment({
owner: context.repo.owner,
repo: context.repo.repo,
ref: githubBranch || context.ref,
auto_merge: false,
description: "Cloudflare Workers Preview",
required_contexts: [],
environment: environmentName,
production_environment: false,
});

if (deployment.status !== 201) {
info(config, "Error creating GitHub deployment for preview");
return;
}

await octokit.rest.repos.createDeploymentStatus({
owner: context.repo.owner,
repo: context.repo.repo,
deployment_id: deployment.data.id,
environment: environmentName,
environment_url: previewUrl,
production_environment: false,
log_url: `https://dash.cloudflare.com/${config.CLOUDFLARE_ACCOUNT_ID}/workers/services/view/${previewFields.worker_name}`,
Comment thread
WillTaylorDev marked this conversation as resolved.
Outdated
description: "Cloudflare Workers Preview",
state: "success",
auto_inactive: false,
});
})(),
createPreviewJobSummary({
previewName: previewFields.preview_name,
previewUrl,
deploymentUrl,
workerName: previewFields.worker_name,
}),
]);

if (createDeploymentRes.status === "rejected") {
warn(config, "Creating Github Deployment for preview failed");
}

if (createSummaryRes.status === "rejected") {
warn(config, "Creating Github Job summary for preview failed");
}
} else {
// Still create job summary even without GitHub token
try {
await createPreviewJobSummary({
previewName: previewFields.preview_name,
previewUrl,
deploymentUrl,
workerName: previewFields.worker_name,
});
} catch {
warn(config, "Creating Github Job summary for preview failed");
}
}
}
4 changes: 3 additions & 1 deletion src/wranglerAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,9 @@ async function wranglerCommands(

if (
config["VARS"].length &&
(command.startsWith("deploy") || command.startsWith("publish")) &&
(command.startsWith("deploy") ||
command.startsWith("publish") ||
command.startsWith("preview")) &&
Comment thread
WillTaylorDev marked this conversation as resolved.
Outdated
!command.includes("--var")
) {
args.push("--var");
Expand Down
Loading
Loading