Skip to content

Deploy to Cloudflare Workers #6

Deploy to Cloudflare Workers

Deploy to Cloudflare Workers #6

name: Deploy to Cloudflare Workers
on:
workflow_dispatch:
inputs:
environment:
description: Deployment environment name
required: false
default: production
worker_name:
description: Override worker name
required: false
default: ""
d1_name:
description: Override D1 database name
required: false
default: ""
kv_name:
description: Override KV namespace name
required: false
default: ""
concurrency:
group: deploy-cloudflare-workers-${{ github.ref }}
cancel-in-progress: false
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: read
env:
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CF_ENVIRONMENT: ${{ github.event.inputs.environment || 'production' }}
CF_WORKER_NAME: ${{ github.event.inputs.worker_name || format('grok2api-{0}', github.repository_owner) }}
CF_D1_NAME: ${{ github.event.inputs.d1_name || format('grok2api-{0}-db', github.repository_owner) }}
CF_KV_NAME: ${{ github.event.inputs.kv_name || format('grok2api-{0}-kv', github.repository_owner) }}
CF_WRANGLER_TEMPLATE: cloudflare/wrangler.cloudflare.jsonc
CF_WRANGLER_OUTPUT: .wrangler.generated.jsonc
CF_RESOURCES_OUTPUT: cloudflare/resources.json
CF_DEPLOY_LOG: cloudflare/deploy-output.log
CF_DEPLOY_META: cloudflare/deploy-meta.json
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.13'
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Validate required secrets
shell: bash
run: |
set -euo pipefail
test -n "${CLOUDFLARE_ACCOUNT_ID}"
test -n "${CLOUDFLARE_API_TOKEN}"
test -f "${CF_WRANGLER_TEMPLATE}"
test -f "cloudflare/worker-entry.js"
- name: Validate deployment names
shell: bash
run: |
set -euo pipefail
python - <<'PY'
import os, re
pattern = re.compile(r'^[A-Za-z0-9][A-Za-z0-9._-]{1,62}[A-Za-z0-9]$')
for key in ('CF_WORKER_NAME', 'CF_D1_NAME', 'CF_KV_NAME'):
value = os.environ.get(key, '').strip()
if not value:
raise SystemExit(f'{key} is empty')
if not pattern.fullmatch(value):
raise SystemExit(f'{key} contains invalid characters: {value!r}')
PY
- name: Install Wrangler CLI
run: npm install --global wrangler@4
- name: Show Wrangler version
run: wrangler --version
- name: Prepare Cloudflare resources
shell: bash
run: |
python scripts/cf_prepare_resources.py --output "${CF_RESOURCES_OUTPUT}"
- name: Render Wrangler config
shell: bash
run: |
python scripts/cf_render_wrangler.py \
--template "${CF_WRANGLER_TEMPLATE}" \
--resources "${CF_RESOURCES_OUTPUT}" \
--output "${CF_WRANGLER_OUTPUT}"
- name: Show rendered Wrangler config path
shell: bash
run: |
echo "Using Wrangler config: ${CF_WRANGLER_OUTPUT}"
test -f "${CF_WRANGLER_OUTPUT}"
- name: Deploy Worker
shell: bash
run: |
set -euo pipefail
mkdir -p cloudflare
wrangler deploy --config "${CF_WRANGLER_OUTPUT}" 2>&1 | tee "${CF_DEPLOY_LOG}"
- name: Extract deploy metadata
shell: bash
run: |
set -euo pipefail
python - <<'PY'
import json
import os
import re
from pathlib import Path
log_path = Path(os.environ['CF_DEPLOY_LOG'])
out_path = Path(os.environ['CF_DEPLOY_META'])
text = log_path.read_text(encoding='utf-8', errors='replace') if log_path.exists() else ''
url_match = re.search(r'https://[A-Za-z0-9._/-]+\.workers\.dev(?:/[A-Za-z0-9._~:/?#\[\]@!$&\'\(\)\*\+,;=%-]*)?', text)
data = {
'worker_url': url_match.group(0).rstrip('/') if url_match else ''
}
out_path.write_text(json.dumps(data, indent=2), encoding='utf-8')
print(json.dumps(data, indent=2))
PY
- name: Health check
shell: bash
run: |
set -euo pipefail
worker_url=$(python - <<'PY'
import json
import os
from pathlib import Path
path = Path(os.environ['CF_DEPLOY_META'])
data = json.loads(path.read_text(encoding='utf-8')) if path.exists() else {}
print(data.get('worker_url', '').rstrip('/'))
PY
)
if [ -z "$worker_url" ]; then
echo "Unable to determine deployed workers.dev URL from deploy output" >&2
echo "Deploy log:" >&2
cat "${CF_DEPLOY_LOG}" >&2
exit 1
fi
worker_url="${worker_url}/health"
echo "Checking ${worker_url}"
curl --fail --silent --show-error --retry 5 --retry-all-errors --retry-delay 2 "$worker_url"
- name: Post deploy summary
shell: bash
run: |
worker_url=$(python - <<'PY'
import json
import os
from pathlib import Path
path = Path(os.environ['CF_DEPLOY_META'])
data = json.loads(path.read_text(encoding='utf-8')) if path.exists() else {}
print(data.get('worker_url', '').rstrip('/'))
PY
)
python scripts/cf_post_deploy.py \
--resources "${CF_RESOURCES_OUTPUT}" \
--worker-name "${CF_WORKER_NAME}" \
--environment "${CF_ENVIRONMENT}" \
--worker-url "$worker_url"