Repository navigation
133 lines (127 loc) · 6.14 KB
/
Copy pathdeploy.yml
File metadata and controls
133 lines (127 loc) · 6.14 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
name: Deploy
# Runs after the CI workflow has passed on main (never on a red commit), or by hand.
on:
workflow_run:
workflows: [CI]
types: [completed]
branches: [main]
workflow_dispatch:
concurrency:
group: deploy
cancel-in-progress: false
permissions:
contents: read
jobs:
deploy:
name: Build and upload to Bunny
if: >-
(github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main') ||
(github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'push' && github.event.workflow_run.head_branch == 'main' &&
github.event.workflow_run.head_repository.full_name == github.repository)
runs-on: ubuntu-latest
timeout-minutes: 15
environment: production
env:
PUBLIC_PLAUSIBLE_DOMAIN: ${{ vars.PLAUSIBLE_DOMAIN }}
steps:
- name: Verify this is still the current main commit
id: current
env:
GH_TOKEN: ${{ github.token }}
EXPECTED_SHA: ${{ github.event.workflow_run.head_sha || github.sha }}
run: |
set -euo pipefail
current=$(gh api "repos/$GITHUB_REPOSITORY/commits/main" --jq .sha)
if [ "$current" = "$EXPECTED_SHA" ]; then
echo "deploy=true" >> "$GITHUB_OUTPUT"
else
echo "deploy=false" >> "$GITHUB_OUTPUT"
echo "Skipping stale deployment of $EXPECTED_SHA; main is $current"
fi
- if: steps.current.outputs.deploy == 'true'
uses: actions/checkout@v7
with:
# deploy exactly the commit CI tested
ref: ${{ github.event.workflow_run.head_sha || github.sha }}
- if: steps.current.outputs.deploy == 'true'
uses: pnpm/action-setup@v6
- if: steps.current.outputs.deploy == 'true'
uses: actions/setup-node@v7
with:
node-version-file: .nvmrc
cache: pnpm
- if: steps.current.outputs.deploy == 'true'
run: pnpm install --frozen-lockfile
- if: steps.current.outputs.deploy == 'true'
run: pnpm build
# Before the upload, so the new pages never post to a path the zone does not forward yet.
# Opt-in: set the variable BUNNY_SYNC_EDGE_RULES to "true" once `pnpm edge-rules` output
# has been reviewed. Analytics posts to the proxy path, so it needs the sync.
- name: Sync Bunny edge rules (Plausible proxy)
if: steps.current.outputs.deploy == 'true'
env:
SYNC: ${{ vars.BUNNY_SYNC_EDGE_RULES }}
BUNNY_API_KEY: ${{ secrets.BUNNY_API_KEY }}
BUNNY_PULL_ZONE_ID: ${{ secrets.BUNNY_PULL_ZONE_ID }}
run: |
set -euo pipefail
if [ "$SYNC" != "true" ]; then
if [ -n "${PUBLIC_PLAUSIBLE_DOMAIN:-}" ]; then
echo "::error::PLAUSIBLE_DOMAIN is set but BUNNY_SYNC_EDGE_RULES is not true; without the proxy rule every visit would go uncounted"
exit 1
fi
echo "edge-rule sync is off"
exit 0
fi
pnpm edge-rules --apply
- name: Upload dist/ to the storage zone
if: steps.current.outputs.deploy == 'true'
env:
BUNNY_STORAGE_KEY: ${{ secrets.BUNNY_STORAGE_KEY }}
BUNNY_STORAGE_ZONE: ${{ secrets.BUNNY_STORAGE_ZONE }}
BUNNY_STORAGE_HOST: ${{ secrets.BUNNY_STORAGE_HOST }}
run: |
set -euo pipefail
: "${BUNNY_STORAGE_KEY:?missing}" "${BUNNY_STORAGE_ZONE:?missing}" "${BUNNY_STORAGE_HOST:?missing}"
# the host is a bare hostname such as storage.bunnycdn.com; keep only that part when a
# scheme or a path (such as the zone name) was pasted along with it
host="${BUNNY_STORAGE_HOST#https://}"; host="${host#http://}"; host="${host%%/*}"
case "$host" in
*"$BUNNY_STORAGE_ZONE"*) echo "::warning::BUNNY_STORAGE_HOST contained the zone name; using host '$host'" ;;
esac
base="https://$host/$BUNNY_STORAGE_ZONE"
# Probe once before uploading: listing the zone root needs the same key as a PUT.
code=$(curl -sS -o /dev/null -w "%{http_code}" -H "AccessKey: $BUNNY_STORAGE_KEY" "$base/" || echo "000")
case "$code" in
200) echo "storage zone reachable: $host/$BUNNY_STORAGE_ZONE" ;;
401) echo "::error::Bunny storage returned 401: BUNNY_STORAGE_KEY is not the password of zone '$BUNNY_STORAGE_ZONE'"; exit 1 ;;
404) echo "::error::Bunny storage returned 404: zone '$BUNNY_STORAGE_ZONE' does not exist on host '$host' (check BUNNY_STORAGE_ZONE and BUNNY_STORAGE_HOST)"; exit 1 ;;
000) echo "::error::could not connect to https://$host (check BUNNY_STORAGE_HOST)"; exit 1 ;;
*) echo "::error::Bunny storage returned $code when listing $base/"; exit 1 ;;
esac
cd dist
find . -type f | sed 's|^\./||' | sort > ../files.txt
echo "uploading $(wc -l < ../files.txt) files to $host/$BUNNY_STORAGE_ZONE"
# Storage API: one PUT per file, AccessKey header; 8 uploads in parallel.
export BUNNY_STORAGE_KEY base
xargs -a ../files.txt -P 8 -I{} sh -c '
code=$(curl -sS -o /dev/null -w "%{http_code}" -X PUT \
-H "AccessKey: $BUNNY_STORAGE_KEY" \
-H "Content-Type: application/octet-stream" \
--data-binary @"{}" \
"$base/{}" || echo "000")
case "$code" in 200|201) ;; *) echo "::error::upload failed ($code): {}"; exit 1;; esac'
echo "uploaded $(wc -l < ../files.txt) files"
- name: Purge the pull zone cache
if: steps.current.outputs.deploy == 'true'
env:
BUNNY_API_KEY: ${{ secrets.BUNNY_API_KEY }}
BUNNY_PULL_ZONE_ID: ${{ secrets.BUNNY_PULL_ZONE_ID }}
run: |
set -euo pipefail
: "${BUNNY_API_KEY:?missing}" "${BUNNY_PULL_ZONE_ID:?missing}"
code=$(curl -sS -o /dev/null -w "%{http_code}" -X POST \
-H "AccessKey: $BUNNY_API_KEY" -H "Content-Length: 0" \
"https://api.bunny.net/pullzone/$BUNNY_PULL_ZONE_ID/purgeCache")
case "$code" in 200|204) echo "purged";; *) echo "purge failed ($code)"; exit 1;; esac