-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcloudflare-deploy.sh
More file actions
executable file
·237 lines (202 loc) · 6.9 KB
/
cloudflare-deploy.sh
File metadata and controls
executable file
·237 lines (202 loc) · 6.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
#!/usr/bin/env bash
#
# cloudflare-deploy.sh — post-deploy GitHub integration for Cloudflare Workers/Pages
#
# Usage:
# cloudflare-deploy.sh comment Post or update a PR comment with the preview URL
# cloudflare-deploy.sh deployment Create a GitHub Deployment and job summary
#
# Required environment:
# WRANGLER_OUTPUT_FILE_DIRECTORY Directory where wrangler wrote its output artifacts
# GH_TOKEN GitHub token (usually secrets.GITHUB_TOKEN)
# GITHUB_REPOSITORY owner/repo (set automatically by Actions)
#
# For 'comment':
# PR_NUMBER Pull request number
#
# For 'deployment':
# GITHUB_HEAD_REF / GITHUB_REF_NAME Branch ref (set automatically by Actions)
# CLOUDFLARE_ACCOUNT_ID Cloudflare account ID (for dashboard link)
set -euo pipefail
die() {
echo "error: $*" >&2
exit 1
}
# Read the first wrangler output entry matching one of the supported types.
#
# Wrangler writes newline-delimited JSON files named
# wrangler-output-<timestamp>-<hex>.json into WRANGLER_OUTPUT_FILE_DIRECTORY.
# We read all files once and search in priority order:
# pages-deploy-detailed > deploy > version-upload
read_deploy_output() {
local dir="${WRANGLER_OUTPUT_FILE_DIRECTORY:?WRANGLER_OUTPUT_FILE_DIRECTORY must be set}"
# Gather all matching files. Use nullglob-safe find to avoid errors on
# empty directories.
local files
files=$(find "$dir" -maxdepth 1 -name 'wrangler-output-*.json' 2>/dev/null | sort)
if [[ -z "${files}" ]]; then
die "no wrangler output files found in ${dir}"
fi
# Slurp all lines from all output files into a single stream, then filter.
# This avoids re-reading the directory for each entry type.
local -a file_list
mapfile -t file_list <<< "${files}"
local all_entries
all_entries=$(cat "${file_list[@]}" 2>/dev/null)
local entry_type
local match
for entry_type in "pages-deploy-detailed" "deploy" "version-upload"; do
match=$(jq -c "select(.type == \"${entry_type}\")" <<< "${all_entries}" 2>/dev/null | head -n1)
if [[ -n "${match}" ]]; then
echo "${match}"
return
fi
done
die "no deployment output entry found in wrangler artifacts"
}
# Extract the deployment URL from whichever entry type we found.
extract_url() {
local entry="$1"
local entry_type
entry_type=$(jq -r '.type' <<< "${entry}")
case "${entry_type}" in
pages-deploy-detailed)
jq -r '.url // empty' <<< "${entry}"
;;
deploy)
jq -r '.targets[0] // empty' <<< "${entry}"
;;
version-upload)
jq -r '.preview_url // empty' <<< "${entry}"
;;
*)
die "unknown entry type: ${entry_type}"
;;
esac
}
# Post or update a PR comment with the preview URL.
cmd_comment() {
local pr="${PR_NUMBER:?PR_NUMBER must be set}"
local entry
entry=$(read_deploy_output)
local url
url=$(extract_url "${entry}")
[[ -z "${url}" ]] && die "could not extract deployment URL from wrangler output"
local body
body="**Cloudflare Preview**"$'\n\n'"🔗 ${url}"
# Include alias URL for Pages deployments.
local alias_url
alias_url=$(jq -r '.alias // empty' <<< "${entry}" 2>/dev/null)
if [[ -n "${alias_url}" ]]; then
body+=$'\n'"🔀 ${alias_url} (branch alias)"
fi
# Look for an existing comment to update (avoids spamming on repeated pushes).
local existing_comment
existing_comment=$(
gh api "repos/${GITHUB_REPOSITORY}/issues/${pr}/comments" \
--jq '.[] | select(.body | startswith("**Cloudflare Preview**")) | .id' \
2>/dev/null | head -n1
) || true
if [[ -n "${existing_comment}" ]]; then
gh api "repos/${GITHUB_REPOSITORY}/issues/comments/${existing_comment}" \
-X PATCH -f body="${body}" --silent
echo "Updated existing comment ${existing_comment}"
else
gh api "repos/${GITHUB_REPOSITORY}/issues/${pr}/comments" \
-f body="${body}" --silent
echo "Posted new comment on PR #${pr}"
fi
}
# Create a GitHub Deployment + status and write a job summary.
cmd_deployment() {
local entry
entry=$(read_deploy_output)
local url
url=$(extract_url "${entry}")
[[ -z "${url}" ]] && die "could not extract deployment URL from wrangler output"
local entry_type
entry_type=$(jq -r '.type' <<< "${entry}")
local ref="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME:?}}"
local environment="preview"
local log_url=""
# Pages deployments have richer metadata.
if [[ "${entry_type}" == "pages-deploy-detailed" ]]; then
environment=$(jq -r '.environment // "preview"' <<< "${entry}")
local project_name
project_name=$(jq -r '.pages_project // empty' <<< "${entry}")
local cf_deployment_id
cf_deployment_id=$(jq -r '.deployment_id // empty' <<< "${entry}")
local account_id="${CLOUDFLARE_ACCOUNT_ID:-}"
if [[ -n "${account_id}" && -n "${project_name}" && -n "${cf_deployment_id}" ]]; then
log_url="https://dash.cloudflare.com/${account_id}/pages/view/${project_name}/${cf_deployment_id}"
fi
fi
# Create the deployment.
# Passing an empty JSON array for required_contexts disables commit status
# checks on the deployment object. The gh cli -f flag cannot represent an
# empty array, so we pipe raw JSON via --input.
local gh_deployment_id
gh_deployment_id=$(
jq -n \
--arg ref "${ref}" \
--arg env "${environment}" \
--arg desc "Cloudflare Deploy" \
'{
ref: $ref,
environment: $env,
auto_merge: false,
description: $desc,
required_contexts: []
}' \
| gh api "repos/${GITHUB_REPOSITORY}/deployments" \
--method POST --input - --jq '.id'
)
if [[ -z "${gh_deployment_id}" ]]; then
die "failed to create GitHub deployment"
fi
# Set deployment status to success.
local status_body
status_body=$(
jq -n \
--arg env "${environment}" \
--arg url "${url}" \
--arg desc "Cloudflare Deploy" \
--arg log_url "${log_url}" \
'{
state: "success",
environment: $env,
environment_url: $url,
description: $desc,
auto_inactive: false
}
| if $log_url != "" then . + {log_url: $log_url} else . end'
)
gh api "repos/${GITHUB_REPOSITORY}/deployments/${gh_deployment_id}/statuses" \
--method POST --input - --silent <<< "${status_body}"
echo "Created GitHub deployment ${gh_deployment_id} → ${url}"
# Write job summary if the variable is available.
if [[ -n "${GITHUB_STEP_SUMMARY:-}" ]]; then
{
echo "### Cloudflare Deploy"
echo ""
echo "| | |"
echo "|---|---|"
echo "| **URL** | ${url} |"
echo "| **Environment** | ${environment} |"
if [[ -n "${log_url}" ]]; then
echo "| **Dashboard** | [View](${log_url}) |"
fi
} >> "${GITHUB_STEP_SUMMARY}"
fi
}
main() {
case "${1:-}" in
comment) cmd_comment ;;
deployment) cmd_deployment ;;
*)
echo "Usage: $(basename "$0") {comment|deployment}" >&2
exit 1
;;
esac
}
main "$@"