Deploy Cloudflare Workers using Wrangler v4's Versions API while attaching rich, commit-aware metadata to each deployment.
This Action:
- Uses
wrangler versions upload+wrangler versions deployinstead of plainwrangler deploy. - Lets you define custom deployment messages (and an optional tag string) using templates.
- Exposes the Worker Version ID and deployment URL as outputs for downstream steps.
- Is designed for workflows where you build in GitHub Actions and want clean, traceable deploys in Cloudflare.
- Monorepo-friendly behavior. Selective path based deployments.
- Custom build pipelines. Build on GitHub actions.
- Strong, composable metadata around each version and deployment:
- You want a quick, simple deploy with minimal control.
- You’re okay with less flexibility around build steps and monorepo layouts.
Minimal monorepo example setup. Be sure to add your CLOUDFLARE_API_TOKEN to your secrets.
on:
push:
branches:
- main
paths:
- apps/web/**
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Setup and build your application...
- name: Deploy with metadata via Wrangler Versions API
uses: mkcode/wrangler-version-deploy-action-with-metadata@v1
with:
api_token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
wrangler_command: "pnpx wrangler"
working_directory: apps/web- Built for Wrangler v4 and Workers Versions.
- Uploads a specific Worker Version and (by default) deploys that exact version.
- Customizable deployment message via
message_template. - Optional tag string via
tag_template(exposed as output for your own use). - Outputs:
version_id: The Worker Version ID that was uploaded/deployed.deployment_url: Best-effort deployment URL parsed from Wrangler output.message: The final rendered message.tag: The final rendered tag (if any).
only_uploadmode when you want to upload a version and deploy it separately.wrangler_commandlets you control exactly how Wrangler is invoked (e.g.wrangler,npx wrangler@4,pnpm dlx wrangler@4).working_directorylets you target a specific folder in a monorepo when running Wrangler commands.
Instead of calling:
wrangler deploy(which does not accept rich message/tag metadata),
this Action performs:
- Collect metadata from the GitHub Actions environment:
owner,reporef,branchsha,short_shaactorrun_id,run_numbercommit_message,short_commit_message
- Compute the deployment message:
- If
message_templateis provided, render it using the metadata. - Otherwise, generate a default:
branch@sha6: first-line-of-commit-message(truncated to 100 chars).
- If
- Optionally compute a tag:
- If
tag_templateis provided, render it using the same metadata. - By default, no tag is generated.
- Note: this tag is not currently sent directly to Cloudflare; it’s exposed as an Action output for your own use.
- If
- Run:
wrangler versions upload --config <config> [upload_args...] --message="<message>"
- Parse the Worker Version ID from the upload output.
- If
only_uploadisfalse(default):- Run:
wrangler versions deploy <versionId> -y --config <config> [deploy_args...] --message="<message>"
- Best-effort parse the deployment URL from the deploy output.
- Run:
- Expose:
version_id,deployment_url,message, andtagas outputs.
This upload-then-deploy flow is what enables meaningful messages to show up in Cloudflare’s deployment history.
You must:
- Use Wrangler v4 (Versions API support required).
- Ensure you have a reliable way to invoke Wrangler v4:
- You specify this explicitly via the
wrangler_commandinput. - Examples:
wranglernpx wrangler@4pnpm dlx wrangler@4
- No separate install step is required if your
wrangler_commandhandles it.
- You specify this explicitly via the
- Provide a Cloudflare API token:
- With appropriate permissions for your Worker.
- Passed via
secretsto theapi_tokeninput.
- Provide the path to your Wrangler config file via the
configinput:- Example:
wrangler.toml - Example:
dist/server/wrangler.json
- Example:
- Run this Action from (or pointing at) the correct Worker project:
- Use
configand the args inputs to ensure Wrangler targets the right project and environment.
- Use
This Action does NOT:
- Build your project.
- Install wrangler for you.
- Infer your Wrangler config automatically.
- Manage
account_idfor you (Wrangler should pick that up from config/env).
All inputs are strings (as per GitHub Actions) unless noted; booleans are passed as "true" / "false".
-
api_token(required)- Cloudflare API token.
- Recommended:
secrets.CLOUDFLARE_API_TOKEN. - Used to set
CLOUDFLARE_API_TOKENfor the Wrangler commands.
-
wrangler_command(required)- How to invoke Wrangler.
- This is the base command (and optional leading arguments) that will be used for both upload and deploy.
- Examples:
wranglernpx wranglerpnpm dlx wrangler@4
- Then runs:
<wrangler_command> versions upload ...<wrangler_command> versions deploy ...
-
working_directory(optional)- Working directory from which Wrangler commands will be executed.
- If set, it is used as the
cwdfor both:versions uploadversions deploy
- Useful for monorepos where your Worker lives in a subfolder.
-
config(optional)- Path to the Wrangler configuration file.
- When provided:
- Passed as
--config <config>to both:wrangler versions uploadwrangler versions deploy
- Passed as
- When omitted:
- Wrangler's default configuration resolution is used (for example, a
wrangler.tomlin the working directory).
- Wrangler's default configuration resolution is used (for example, a
-
upload_args(optional)- Extra arguments for:
wrangler versions upload
- Do NOT include
--confighere; that comes fromconfig. - Example:
--env production
- Example command shape:
wrangler versions upload --config <config> <upload_args...> --message="<message>"
- Extra arguments for:
-
deploy_args(optional)- Extra arguments for:
wrangler versions deploy
- Do NOT include
--confighere; that comes fromconfig. - Example:
--env production
- Example command shape:
wrangler versions deploy <versionId> -y --config <config> <deploy_args...> --message="<message>"
- Extra arguments for:
-
message_template(optional)- Template for the deployment message.
- If not provided:
- A default message based on branch, SHA, and commit message is used.
- The same message is applied to both upload and deploy.
- Supported placeholders:
{{owner}}{{repo}}{{ref}}{{branch}}{{sha}}{{short_sha}}{{actor}}{{run_id}}{{run_number}}{{commit_message}}{{short_commit_message}}{{deployment_url}}(only meaningful when used with outputs / in later steps){{version_id}}(only meaningful when used with outputs / in later steps)
-
tag_template(optional)- Template for a tag/label string derived from the same metadata.
- If not provided:
- No tag is generated (empty output).
- Note:
- Tags are NOT currently pushed directly into Cloudflare by this Action.
- The rendered tag is available via the
tagoutput for your own usage (e.g. PR comments, releases, logs).
-
only_upload(optional, default:"false")"false"(default):- Full flow:
wrangler versions upload ...- Parse
version_id(required). wrangler versions deploy <versionId> -y ...- Best-effort parse
deployment_url. - Fail if
version_idcannot be parsed.
- Full flow:
"true":- Upload-only flow:
wrangler versions upload ...- Try to parse
version_id:- If found → set
version_idoutput. - If not found → log and still succeed.
- If found → set
- Do NOT run
versions deploy. - Useful if:
- You deploy specific versions elsewhere, or want a manual approval step.
- Upload-only flow:
-
deployment_url- Best-effort detected URL from
wrangler versions deployoutput. - Empty when
only_upload: "true"or when no URL can be detected.
- Best-effort detected URL from
-
version_id- Worker Version ID parsed from
wrangler versions uploadoutput. - Required for success when
only_upload: "false". - Optional when
only_upload: "true".
- Worker Version ID parsed from
-
message- The final rendered deployment message used for upload (and deploy, if applicable).
-
tag- The final rendered tag string, if
tag_templatewas provided. - Empty if no
tag_templateis set.
- The final rendered tag string, if
Use this when you:
- Build your Worker in CI.
- Want to upload and immediately deploy with a descriptive message.
name: Deploy Worker (Production)
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Build your worker here
# - run: pnpm install
# - run: pnpm build
# Ensure Wrangler v4 is available
- run: pnpm dlx wrangler@4 --version
- name: Upload + deploy via Wrangler Versions with metadata
id: cf_deploy
uses: mkcode/wrangler-version-deploy-action-with-metadata@v1
with:
api_token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
wrangler_command: "pnpm dlx wrangler@4"
working_directory: "dist/server"
config: "wrangler.json"
upload_args: "--env production"
deploy_args: "--env production"
message_template: "Deployed {{repo}}@{{short_sha}} to {{branch}} by {{actor}} (run {{run_number}})"
- name: Report deployment
run: |
echo "Version ID: ${{ steps.cf_deploy.outputs.version_id }}"
echo "URL: ${{ steps.cf_deploy.outputs.deployment_url }}"
echo "Message: ${{ steps.cf_deploy.outputs.message }}"
echo "Tag: ${{ steps.cf_deploy.outputs.tag }}"Use this when you:
- Want to create a version with metadata.
- Plan to deploy that
version_idfrom another workflow or system.
name: Upload Worker Version Only
on:
workflow_dispatch:
jobs:
upload-version:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pnpm dlx wrangler@4 --version
- name: Upload Worker Version with metadata (no deploy)
id: cf_upload
uses: mkcode/wrangler-version-deploy-action-with-metadata@v1
with:
api_token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
wrangler_command: "pnpm dlx wrangler@4"
working_directory: "."
config: "wrangler.toml"
upload_args: "--env staging"
only_upload: "true"
message_template: "Staging candidate {{repo}}@{{short_sha}} on {{branch}} (run {{run_number}})"
- name: Use uploaded version ID
run: |
echo "Uploaded version: ${{ steps.cf_upload.outputs.version_id }}"You can combine outputs with other Actions to post deployment info back to PRs:
- name: Deploy with metadata
id: cf_deploy
uses: mkcode/wrangler-version-deploy-action-with-metadata
with:
api_token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
config: "wrangler.toml"
upload_args: "--env preview"
deploy_args: "--env preview"
message_template: "[preview] {{repo}}@{{short_sha}} on {{branch}} by {{actor}}"
tag_template: "preview-{{short_sha}}"
- name: Comment on PR with deployment info
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const url = "${{ steps.cf_deploy.outputs.deployment_url }}";
const version = "${{ steps.cf_deploy.outputs.version_id }}";
const msg = "${{ steps.cf_deploy.outputs.message }}";
const tag = "${{ steps.cf_deploy.outputs.tag }}";
github.rest.issues.createComment({
...context.repo,
issue_number: context.issue.number,
body: [
"🚀 Preview deployment created:",
url && `- URL: ${url}`,
version && `- Version: ${version}`,
msg && `- Message: ${msg}`,
tag && `- Tag: ${tag}`,
].filter(Boolean).join("\n")
});In a monorepo, you often want to:
- Only run builds/deploys when a specific app/package directory changes.
- Use a config file that lives within that directory.
This example:
- Triggers only when files under
apps/worker-app/change. - Uses the Wrangler config at
apps/worker-app/wrangler.toml. - Builds and deploys only that app.
name: Deploy Worker App (Monorepo)
on:
push:
branches: [main]
paths:
- "apps/worker-app/**"
jobs:
deploy-worker-app:
runs-on: ubuntu-latest
defaults:
run:
working-directory: apps/worker-app
steps:
- uses: actions/checkout@v4
# Install and build only this app
# - run: pnpm install
# - run: pnpm build
- name: Upload + deploy worker-app via Versions API with metadata
id: cf_deploy
uses: mkcode/wrangler-version-deploy-action-with-metadata@v1
with:
api_token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
wrangler_command: "pnpm dlx wrangler@4"
working_directory: "apps/worker-app"
config: "wrangler.toml"
upload_args: "--env production"
deploy_args: "--env production"
message_template: "worker-app: {{repo}}@{{short_sha}} on {{branch}} (run {{run_number}})"