|
| 1 | +# Wrangler Version Deploy Action with Metadata |
| 2 | + |
| 3 | +Deploy Cloudflare Workers using Wrangler v4's Versions API while attaching rich, commit-aware metadata to each deployment. |
| 4 | + |
| 5 | +This Action: |
| 6 | + |
| 7 | +- Uses `wrangler versions upload` + `wrangler versions deploy` instead of plain `wrangler deploy`. |
| 8 | +- Lets you define custom deployment messages (and an optional tag string) using templates. |
| 9 | +- Exposes the Worker Version ID and deployment URL as outputs for downstream steps. |
| 10 | +- Is designed for workflows where you build in GitHub Actions and want clean, traceable deploys in Cloudflare. |
| 11 | ++ |
| 12 | ++## When to use this instead of the official Cloudflare Action |
| 13 | ++ |
| 14 | ++Use this Action when: |
| 15 | ++ |
| 16 | ++- You want full control over how and where your Worker is built: |
| 17 | ++ - You build in GitHub Actions (pnpm, npm, turbo, etc.) and only need Wrangler for the final upload/deploy. |
| 18 | ++- You want rich, commit-aware metadata in Cloudflare: |
| 19 | ++ - You care about seeing branch, commit, actor, and custom messages in the Cloudflare Versions UI. |
| 20 | ++ - You want stable `version_id` outputs to link deploys back to code, PRs, or changelogs. |
| 21 | ++- You’re in a monorepo: |
| 22 | ++ - You only want to build and deploy when a specific app/package directory changes. |
| 23 | ++ - You want an Action that targets a specific `config` path and doesn’t assume a single-project repo. |
| 24 | ++- You prefer an explicit upload → deploy flow: |
| 25 | ++ - You want `wrangler versions upload` + `wrangler versions deploy <versionId>` semantics instead of a generic `wrangler deploy`. |
| 26 | ++ |
| 27 | ++The official Cloudflare Actions are great if: |
| 28 | ++ |
| 29 | ++- You want a quick, simple deploy with minimal control. |
| 30 | ++- You’re okay with less flexibility around build steps, metadata, and monorepo layouts. |
| 31 | ++ |
| 32 | ++Use this Action if you need: |
| 33 | ++ |
| 34 | ++- Monorepo-friendly behavior. |
| 35 | ++- Custom build pipelines. |
| 36 | ++- Strong, composable metadata around each deployment. |
| 37 | + |
| 38 | +## Features |
| 39 | + |
| 40 | +- Built for Wrangler v4 and Workers Versions. |
| 41 | +- Uploads a specific Worker Version and (by default) deploys that exact version. |
| 42 | +- Customizable deployment message via `message_template`. |
| 43 | +- Optional tag string via `tag_template` (exposed as output for your own use). |
| 44 | +- Outputs: |
| 45 | + - `version_id`: The Worker Version ID that was uploaded/deployed. |
| 46 | + - `deployment_url`: Best-effort deployment URL parsed from Wrangler output. |
| 47 | + - `message`: The final rendered message. |
| 48 | + - `tag`: The final rendered tag (if any). |
| 49 | +- `only_upload` mode when you want to upload a version and deploy it separately. |
| 50 | + |
| 51 | +## How it works |
| 52 | + |
| 53 | +Instead of calling: |
| 54 | + |
| 55 | +- `wrangler deploy` (which does not accept rich message/tag metadata), |
| 56 | + |
| 57 | +this Action performs: |
| 58 | + |
| 59 | +1. Collect metadata from the GitHub Actions environment: |
| 60 | + - `owner`, `repo` |
| 61 | + - `ref`, `branch` |
| 62 | + - `sha`, `short_sha` |
| 63 | + - `actor` |
| 64 | + - `run_id`, `run_number` |
| 65 | + - `commit_message`, `short_commit_message` |
| 66 | +2. Compute the deployment message: |
| 67 | + - If `message_template` is provided, render it using the metadata. |
| 68 | + - Otherwise, generate a default: |
| 69 | + - `branch@sha6: first-line-of-commit-message` (truncated to 100 chars). |
| 70 | +3. Optionally compute a tag: |
| 71 | + - If `tag_template` is provided, render it using the same metadata. |
| 72 | + - By default, no tag is generated. |
| 73 | + - Note: this tag is not currently sent directly to Cloudflare; it’s exposed as an Action output for your own use. |
| 74 | +4. Run: |
| 75 | + - `wrangler versions upload --config <config> [upload_args...] --message="<message>"` |
| 76 | +5. Parse the Worker Version ID from the upload output. |
| 77 | +6. If `only_upload` is `false` (default): |
| 78 | + - Run: |
| 79 | + - `wrangler versions deploy <versionId> -y --config <config> [deploy_args...] --message="<message>"` |
| 80 | + - Best-effort parse the deployment URL from the deploy output. |
| 81 | +7. Expose: |
| 82 | + - `version_id`, `deployment_url`, `message`, and `tag` as outputs. |
| 83 | + |
| 84 | +This upload-then-deploy flow is what enables meaningful messages to show up in Cloudflare’s deployment history. |
| 85 | + |
| 86 | +## Requirements |
| 87 | + |
| 88 | +You must: |
| 89 | + |
| 90 | +- Use Wrangler v4 (Versions API support required). |
| 91 | +- Ensure `wrangler` is available in your GitHub Actions runner: |
| 92 | + - Example: |
| 93 | + - `pnpm dlx wrangler@4 --version` |
| 94 | + - or `npx wrangler@4 --version` |
| 95 | + - or add Wrangler v4 as a dev dependency and use it from PATH. |
| 96 | +- Provide a Cloudflare API token: |
| 97 | + - With appropriate permissions for your Worker. |
| 98 | + - Passed via `secrets` to the `api_token` input. |
| 99 | +- Provide the path to your Wrangler config file via the `config` input: |
| 100 | + - Example: `wrangler.toml` |
| 101 | + - Example: `dist/server/wrangler.json` |
| 102 | +- Run this Action from (or pointing at) the correct Worker project: |
| 103 | + - Use `config` and the args inputs to ensure Wrangler targets the right project and environment. |
| 104 | + |
| 105 | +This Action does NOT: |
| 106 | + |
| 107 | +- Build your project. |
| 108 | +- Infer your Wrangler config automatically. |
| 109 | +- Manage `account_id` for you (Wrangler should pick that up from config/env). |
| 110 | + |
| 111 | +## Inputs |
| 112 | + |
| 113 | +All inputs are strings (as per GitHub Actions) unless noted; booleans are passed as `"true"` / `"false"`. |
| 114 | + |
| 115 | +- `api_token` (required) |
| 116 | + - Cloudflare API token. |
| 117 | + - Recommended: `secrets.CLOUDFLARE_API_TOKEN`. |
| 118 | + - Used to set `CLOUDFLARE_API_TOKEN` for the Wrangler commands. |
| 119 | + |
| 120 | +- `config` (required) |
| 121 | + - Path to the Wrangler configuration file. |
| 122 | + - Passed as: |
| 123 | + - `--config <config>` |
| 124 | + - Applied to both: |
| 125 | + - `wrangler versions upload` |
| 126 | + - `wrangler versions deploy` |
| 127 | + |
| 128 | +- `upload_args` (optional) |
| 129 | + - Extra arguments for: |
| 130 | + - `wrangler versions upload` |
| 131 | + - Do NOT include `--config` here; that comes from `config`. |
| 132 | + - Example: |
| 133 | + - `--env production` |
| 134 | + - Example command shape: |
| 135 | + - `wrangler versions upload --config <config> <upload_args...> --message="<message>"` |
| 136 | + |
| 137 | +- `deploy_args` (optional) |
| 138 | + - Extra arguments for: |
| 139 | + - `wrangler versions deploy` |
| 140 | + - Do NOT include `--config` here; that comes from `config`. |
| 141 | + - Example: |
| 142 | + - `--env production` |
| 143 | + - Example command shape: |
| 144 | + - `wrangler versions deploy <versionId> -y --config <config> <deploy_args...> --message="<message>"` |
| 145 | + |
| 146 | +- `message_template` (optional) |
| 147 | + - Template for the deployment message. |
| 148 | + - If not provided: |
| 149 | + - A default message based on branch, SHA, and commit message is used. |
| 150 | + - The same message is applied to both upload and deploy. |
| 151 | + - Supported placeholders: |
| 152 | + - `{{owner}}` |
| 153 | + - `{{repo}}` |
| 154 | + - `{{ref}}` |
| 155 | + - `{{branch}}` |
| 156 | + - `{{sha}}` |
| 157 | + - `{{short_sha}}` |
| 158 | + - `{{actor}}` |
| 159 | + - `{{run_id}}` |
| 160 | + - `{{run_number}}` |
| 161 | + - `{{commit_message}}` |
| 162 | + - `{{short_commit_message}}` |
| 163 | + - `{{deployment_url}}` (only meaningful when used with outputs / in later steps) |
| 164 | + - `{{version_id}}` (only meaningful when used with outputs / in later steps) |
| 165 | + |
| 166 | +- `tag_template` (optional) |
| 167 | + - Template for a tag/label string derived from the same metadata. |
| 168 | + - If not provided: |
| 169 | + - No tag is generated (empty output). |
| 170 | + - Note: |
| 171 | + - Tags are NOT currently pushed directly into Cloudflare by this Action. |
| 172 | + - The rendered tag is available via the `tag` output for your own usage (e.g. PR comments, releases, logs). |
| 173 | + |
| 174 | +- `only_upload` (optional, default: `"false"`) |
| 175 | + - `"false"` (default): |
| 176 | + - Full flow: |
| 177 | + - `wrangler versions upload ...` |
| 178 | + - Parse `version_id` (required). |
| 179 | + - `wrangler versions deploy <versionId> -y ...` |
| 180 | + - Best-effort parse `deployment_url`. |
| 181 | + - Fail if `version_id` cannot be parsed. |
| 182 | + - `"true"`: |
| 183 | + - Upload-only flow: |
| 184 | + - `wrangler versions upload ...` |
| 185 | + - Try to parse `version_id`: |
| 186 | + - If found → set `version_id` output. |
| 187 | + - If not found → log and still succeed. |
| 188 | + - Do NOT run `versions deploy`. |
| 189 | + - Useful if: |
| 190 | + - You deploy specific versions elsewhere, or want a manual approval step. |
| 191 | + |
| 192 | +## Outputs |
| 193 | + |
| 194 | +- `deployment_url` |
| 195 | + - Best-effort detected URL from `wrangler versions deploy` output. |
| 196 | + - Empty when `only_upload: "true"` or when no URL can be detected. |
| 197 | + |
| 198 | +- `version_id` |
| 199 | + - Worker Version ID parsed from `wrangler versions upload` output. |
| 200 | + - Required for success when `only_upload: "false"`. |
| 201 | + - Optional when `only_upload: "true"`. |
| 202 | + |
| 203 | +- `message` |
| 204 | + - The final rendered deployment message used for upload (and deploy, if applicable). |
| 205 | + |
| 206 | +- `tag` |
| 207 | + - The final rendered tag string, if `tag_template` was provided. |
| 208 | + - Empty if no `tag_template` is set. |
| 209 | + |
| 210 | +## Usage Examples |
| 211 | + |
| 212 | +### Example 1: Upload + Deploy to Production |
| 213 | + |
| 214 | +Use this when you: |
| 215 | + |
| 216 | +- Build your Worker in CI. |
| 217 | +- Want to upload and immediately deploy with a descriptive message. |
| 218 | + |
| 219 | +```yaml |
| 220 | +name: Deploy Worker (Production) |
| 221 | + |
| 222 | +on: |
| 223 | + push: |
| 224 | + branches: [main] |
| 225 | + |
| 226 | +jobs: |
| 227 | + deploy: |
| 228 | + runs-on: ubuntu-latest |
| 229 | + steps: |
| 230 | + - uses: actions/checkout@v4 |
| 231 | + |
| 232 | + # Build your worker here |
| 233 | + # - run: pnpm install |
| 234 | + # - run: pnpm build |
| 235 | + |
| 236 | + # Ensure Wrangler v4 is available |
| 237 | + - run: pnpm dlx wrangler@4 --version |
| 238 | + |
| 239 | + - name: Upload + deploy via Wrangler Versions with metadata |
| 240 | + id: cf_deploy |
| 241 | + uses: your-org/wrangler-version-deploy-action-with-metadata@v1 |
| 242 | + with: |
| 243 | + api_token: ${{ secrets.CLOUDFLARE_API_TOKEN }} |
| 244 | + config: "dist/server/wrangler.json" |
| 245 | + upload_args: "--env production" |
| 246 | + deploy_args: "--env production" |
| 247 | + message_template: "Deployed {{repo}}@{{short_sha}} to {{branch}} by {{actor}} (run {{run_number}})" |
| 248 | + |
| 249 | + - name: Report deployment |
| 250 | + run: | |
| 251 | + echo "Version ID: ${{ steps.cf_deploy.outputs.version_id }}" |
| 252 | + echo "URL: ${{ steps.cf_deploy.outputs.deployment_url }}" |
| 253 | + echo "Message: ${{ steps.cf_deploy.outputs.message }}" |
| 254 | + echo "Tag: ${{ steps.cf_deploy.outputs.tag }}" |
| 255 | +``` |
| 256 | +
|
| 257 | +### Example 2: Only Upload (Manual / External Deploy) |
| 258 | +
|
| 259 | +Use this when you: |
| 260 | +
|
| 261 | +- Want to create a version with metadata. |
| 262 | +- Plan to deploy that `version_id` from another workflow or system. |
| 263 | + |
| 264 | +```yaml |
| 265 | +name: Upload Worker Version Only |
| 266 | +
|
| 267 | +on: |
| 268 | + workflow_dispatch: |
| 269 | +
|
| 270 | +jobs: |
| 271 | + upload-version: |
| 272 | + runs-on: ubuntu-latest |
| 273 | + steps: |
| 274 | + - uses: actions/checkout@v4 |
| 275 | + - run: pnpm dlx wrangler@4 --version |
| 276 | +
|
| 277 | + - name: Upload Worker Version with metadata (no deploy) |
| 278 | + id: cf_upload |
| 279 | + uses: your-org/wrangler-version-deploy-action-with-metadata@v1 |
| 280 | + with: |
| 281 | + api_token: ${{ secrets.CLOUDFLARE_API_TOKEN }} |
| 282 | + config: "wrangler.toml" |
| 283 | + upload_args: "--env staging" |
| 284 | + only_upload: "true" |
| 285 | + message_template: "Staging candidate {{repo}}@{{short_sha}} on {{branch}} (run {{run_number}})" |
| 286 | +
|
| 287 | + - name: Use uploaded version ID |
| 288 | + run: | |
| 289 | + echo "Uploaded version: ${{ steps.cf_upload.outputs.version_id }}" |
| 290 | +``` |
| 291 | + |
| 292 | +### Example 3: Custom Templates and PR Comments |
| 293 | + |
| 294 | +You can combine outputs with other Actions to post deployment info back to PRs: |
| 295 | + |
| 296 | +```yaml |
| 297 | +- name: Deploy with metadata |
| 298 | + id: cf_deploy |
| 299 | + uses: your-org/wrangler-version-deploy-action-with-metadata@v1 |
| 300 | + with: |
| 301 | + api_token: ${{ secrets.CLOUDFLARE_API_TOKEN }} |
| 302 | + config: "wrangler.toml" |
| 303 | + upload_args: "--env preview" |
| 304 | + deploy_args: "--env preview" |
| 305 | + message_template: "[preview] {{repo}}@{{short_sha}} on {{branch}} by {{actor}}" |
| 306 | + tag_template: "preview-{{short_sha}}" |
| 307 | +
|
| 308 | +- name: Comment on PR with deployment info |
| 309 | + if: github.event_name == 'pull_request' |
| 310 | + uses: actions/github-script@v7 |
| 311 | + with: |
| 312 | + script: | |
| 313 | + const url = "${{ steps.cf_deploy.outputs.deployment_url }}"; |
| 314 | + const version = "${{ steps.cf_deploy.outputs.version_id }}"; |
| 315 | + const msg = "${{ steps.cf_deploy.outputs.message }}"; |
| 316 | + const tag = "${{ steps.cf_deploy.outputs.tag }}"; |
| 317 | + github.rest.issues.createComment({ |
| 318 | + ...context.repo, |
| 319 | + issue_number: context.issue.number, |
| 320 | + body: [ |
| 321 | + "🚀 Preview deployment created:", |
| 322 | + url && `- URL: ${url}`, |
| 323 | + version && `- Version: ${version}`, |
| 324 | + msg && `- Message: ${msg}`, |
| 325 | + tag && `- Tag: ${tag}`, |
| 326 | + ].filter(Boolean).join("\n") |
| 327 | + }); |
| 328 | +``` |
| 329 | ++ |
| 330 | ++### Example 4: Monorepo - Deploy Only When a Folder Changes |
| 331 | ++ |
| 332 | ++In a monorepo, you often want to: |
| 333 | ++ |
| 334 | ++- Only run builds/deploys when a specific app/package directory changes. |
| 335 | ++- Use a config file that lives within that directory. |
| 336 | ++ |
| 337 | ++This example: |
| 338 | ++ |
| 339 | ++- Triggers only when files under `apps/worker-app/` change. |
| 340 | ++- Uses the Wrangler config at `apps/worker-app/wrangler.toml`. |
| 341 | ++- Builds and deploys only that app. |
| 342 | ++ |
| 343 | ++```yaml |
| 344 | ++name: Deploy Worker App (Monorepo) |
| 345 | ++ |
| 346 | ++on: |
| 347 | ++ push: |
| 348 | ++ branches: [main] |
| 349 | ++ paths: |
| 350 | ++ - "apps/worker-app/**" |
| 351 | ++ |
| 352 | ++jobs: |
| 353 | ++ deploy-worker-app: |
| 354 | ++ runs-on: ubuntu-latest |
| 355 | ++ defaults: |
| 356 | ++ run: |
| 357 | ++ working-directory: apps/worker-app |
| 358 | ++ |
| 359 | ++ steps: |
| 360 | ++ - uses: actions/checkout@v4 |
| 361 | ++ |
| 362 | ++ # Install and build only this app |
| 363 | ++ # - run: pnpm install |
| 364 | ++ # - run: pnpm build |
| 365 | ++ |
| 366 | ++ - run: pnpm dlx wrangler@4 --version |
| 367 | ++ |
| 368 | ++ - name: Upload + deploy worker-app via Versions API with metadata |
| 369 | ++ id: cf_deploy |
| 370 | ++ uses: your-org/wrangler-version-deploy-action-with-metadata@v1 |
| 371 | ++ with: |
| 372 | ++ api_token: ${{ secrets.CLOUDFLARE_API_TOKEN }} |
| 373 | ++ config: "apps/worker-app/wrangler.toml" |
| 374 | ++ upload_args: "--env production" |
| 375 | ++ deploy_args: "--env production" |
| 376 | ++ message_template: "worker-app: {{repo}}@{{short_sha}} on {{branch}} (run {{run_number}})" |
| 377 | ++``` |
| 378 | + |
| 379 | +## Notes |
| 380 | + |
| 381 | +- This Action focuses on being: |
| 382 | + - Explicit about config (`config` input). |
| 383 | + - Clear about which args go to upload vs deploy. |
| 384 | + - Purpose-built for Wrangler v4’s Versions API. |
| 385 | +- If Wrangler’s output format changes: |
| 386 | + - `deployment_url` parsing is best-effort. |
| 387 | + - `version_id` parsing is based on the standard `Worker Version ID:` line. |
| 388 | +- For GitHub Marketplace: |
| 389 | + - Reference it as `your-org/wrangler-version-deploy-action-with-metadata@v1`. |
| 390 | + - Keep a `v1` tag pointing at the latest stable release. |
| 391 | + |
| 392 | +If you have real Wrangler output samples and want to harden the parsing further, you can refine this Action’s internals without changing the public interface described here. |
0 commit comments