Skip to content

Commit 31ef8ca

Browse files
authored
feat(cloudflare): add a cf deploy mode to cloudflare-deploy.yml (#658)
With deploy-tool: cf (default stays wrangler) the workflow refreshes the Build Output with the project's own cf-wrangler delegate and deploys it through the cf CLI: cf deploy --prebuilt for production, cf previews deploy named after the PR or branch for previews. Version IDs and deployment URLs are extracted into the same job outputs, with the same fail-closed validation as the wrangler path. cf mode requires build-script, since the cf/wrangler CLIs come from the deployed package's devDependencies.
1 parent ca0b6c0 commit 31ef8ca

2 files changed

Lines changed: 53 additions & 1 deletion

File tree

‎.github/workflows/cloudflare-deploy.yml‎

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ on:
88
required: false
99
type: string
1010
default: preview
11+
deploy-tool:
12+
description: 'Deployment CLI: wrangler (default, reads a wrangler config) or cf (Cloudflare cf CLI; deploys the cf Build Output produced by cf-wrangler build).'
13+
required: false
14+
type: string
15+
default: wrangler
1116
draft-protection:
1217
description: 'Skip draft pull requests by default; set false to run deployments for drafts.'
1318
required: false
@@ -116,9 +121,17 @@ jobs:
116121
env:
117122
MODE: ${{ inputs.mode }}
118123
WRANGLER_VERSION: ${{ inputs.wrangler-version }}
124+
DEPLOY_TOOL: ${{ inputs.deploy-tool }}
125+
BUILD_SCRIPT: ${{ inputs.build-script }}
119126
run: |
120127
set -euo pipefail
121128
[[ "$MODE" == preview || "$MODE" == production ]]
129+
[[ "$DEPLOY_TOOL" == wrangler || "$DEPLOY_TOOL" == cf ]]
130+
[[ "$WRANGLER_VERSION" == local || "$WRANGLER_VERSION" == latest || "$WRANGLER_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$ ]]
131+
# The cf path runs the project's cf-wrangler delegate to refresh the
132+
# Build Output; that delegate comes from the app's own dependencies,
133+
# which only the build step installs.
134+
[[ "$DEPLOY_TOOL" != cf || -n "$BUILD_SCRIPT" ]]
122135
[[ "$WRANGLER_VERSION" == local || "$WRANGLER_VERSION" == latest || "$WRANGLER_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$ ]]
123136
- name: Set up Bun
124137
if: inputs.build-script != '' || inputs['turbo-filter'] != ''
@@ -262,8 +275,41 @@ jobs:
262275
WRANGLER_VERSION: ${{ inputs.wrangler-version }}
263276
MODE: ${{ inputs.mode }}
264277
DEPLOYMENT_URL: ${{ inputs.deployment-url }}
278+
DEPLOY_TOOL: ${{ inputs.deploy-tool }}
279+
PREVIEW_NAME: ${{ github.event.pull_request.number && format('pr-{0}', github.event.pull_request.number) || github.ref_name }}
265280
run: |
266281
set -euo pipefail
282+
if [ "$DEPLOY_TOOL" = cf ]; then
283+
# cf path: refresh the Build Output with the project's own
284+
# cf-wrangler delegate, then deploy/preview it with the cf CLI.
285+
# Both CLIs come from the app's devDependencies installed by the
286+
# build step; the Node launcher avoids the Bun-runtime trap.
287+
cfbin=(npx --no-install)
288+
"${cfbin[@]}" cf-wrangler build
289+
if [ "$MODE" = production ]; then
290+
out="$("${cfbin[@]}" cf deploy --prebuilt)"
291+
printf '%s\n' "$out"
292+
version_id="$(printf '%s\n' "$out" | sed -n 's/.*Current Version ID: *//p' | head -1 | tr -d '[:space:]')"
293+
value="${DEPLOYMENT_URL:-$(printf '%s\n' "$out" | grep -o 'https://[^[:space:]]*' | head -1)}"
294+
else
295+
"${cfbin[@]}" cf previews deploy "$PREVIEW_NAME" > "$RUNNER_TEMP/cf-preview.json"
296+
value="$(jq -r '.preview_urls[0] // empty' "$RUNNER_TEMP/cf-preview.json")"
297+
version_id="$(jq -r '.deployment_id // empty' "$RUNNER_TEMP/cf-preview.json")"
298+
fi
299+
if [[ ! "$version_id" =~ ^[0-9a-f-]{36}$ ]]; then
300+
echo 'cf deploy did not report a Worker version ID' >&2
301+
exit 1
302+
fi
303+
if [[ -z "$value" || "$value" != https://* || "$value" == *'@'* || "$value" == *'#'* || "$value" == *' '* ]]; then
304+
echo 'cf deploy did not report a valid deployment URL' >&2
305+
exit 1
306+
fi
307+
{
308+
echo "url=$value"
309+
echo "version-id=$version_id"
310+
} >> "$GITHUB_OUTPUT"
311+
exit 0
312+
fi
267313
if [ "$WRANGLER_VERSION" = local ]; then
268314
if [ -n "$BUILD_SCRIPT" ]; then
269315
wrangler=(npx --no-install wrangler)

‎docs/CONFIGURATION.md‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,13 @@ repository and configure environment reviewers separately.
301301
The legacy `cloudflare-deploy.yml` workflow remains available for direct
302302
(unverified) deployments. It runs `wrangler versions upload` for previews and
303303
`wrangler deploy` for production, records a GitHub deployment plus status, and
304-
respects `CI_BILLING_PAUSED`. Preview deployment records use the pull request
304+
respects `CI_BILLING_PAUSED`. With `deploy-tool: cf` it instead deploys through
305+
the Cloudflare cf CLI: production runs `cf-wrangler build` plus
306+
`cf deploy --prebuilt` (consuming the project's cf Build Output), previews run
307+
`cf previews deploy` named after the pull request or branch, and the cf/wrangler
308+
CLIs must be devDependencies of the deployed package (the build step installs
309+
them), so `build-script` is required. Deployment URLs and version IDs are
310+
extracted from the same job outputs either way. Preview deployment records use the pull request
305311
head SHA when called from a PR, which lets GitHub show the completed preview in
306312
the PR's Deployments section; direct pushes use the workflow SHA. Its
307313
legacy-compatible Wrangler default is `latest`; callers should prefer `local` or

0 commit comments

Comments
 (0)