-
Notifications
You must be signed in to change notification settings - Fork 9
219 lines (202 loc) · 10.1 KB
/
Copy pathtest-preview.yml
File metadata and controls
219 lines (202 loc) · 10.1 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
name: test-preview
# Manual only: provisions Drupal + Nuxt and exposes both over Cloudflare
# Quick Tunnels. Mirrors the manual `preview` job in .gitlab-ci.yml —
# this never runs on push/PR; regular CI for that lives in ci.yml.
#
# mode input: both (default) | dev | prod
# - prod: Nuxt build + start on :3000 - test built/bundled behaviour.
# - dev: Nuxt dev server on :3001 - HMR picks up edits live. Cloudflared
# proxies the port, not the process, so the tunnel URLs survive
# frontend restarts on the same port.
#
# code-server (VS Code in the browser) and the one-time Drupal login
# link are owner conveniences that only make sense with authentication:
# workflow logs on public repos are world-readable, so both are skipped
# unless the PREVIEW_CODE_PASSWORD secret is set. With it set,
# code-server requires that password (--auth password).
on:
workflow_dispatch:
inputs:
duration:
description: 'Seconds to keep the preview alive'
required: false
default: 3600
type: number
mode:
description: 'Frontend mode(s) to expose'
required: false
default: both
type: choice
options:
- both
- dev
- prod
permissions:
contents: read
concurrency:
group: test-preview
cancel-in-progress: true
jobs:
test_preview:
runs-on: ubuntu-latest
# GitHub's job cap; allows durations up to ~6h. Re-triggering the
# workflow cancels the running preview (concurrency group above).
timeout-minutes: 360
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.4'
extensions: mbstring, pdo_sqlite, intl, gd, xml, zip, opcache
- name: Setup Node
uses: actions/setup-node@v7
with:
node-version-file: .nvmrc
# setup-node v5 turned caching on by itself when package.json
# names a package manager, and keys it on the root lockfile
# alone. This job installs from nuxt/, so name both.
cache: npm
cache-dependency-path: |
package-lock.json
nuxt/package-lock.json
- name: Assemble, provision, and start the Drupal backend
working-directory: drupal
run: |
.devtools/assemble
.devtools/provision
.devtools/start
env:
WEBSERVER_HOST: 127.0.0.1
WEBSERVER_PORT: 8888
- name: Install frontend dependencies
working-directory: nuxt
run: npm install
- name: Build and start the Nuxt frontend (prod, :3000)
if: ${{ inputs.mode != 'dev' }}
working-directory: nuxt
run: |
npm run build
nohup npm start > /tmp/nuxt-prod.log 2>&1 &
# Nuxt 2's HMR client is an EventSource on the same origin
# (/__webpack_hmr), so hot updates flow through the tunnel.
- name: Start the Nuxt dev server (dev, :3001)
if: ${{ inputs.mode != 'prod' }}
working-directory: nuxt
run: nohup npm run dev -- -p 3001 > /tmp/nuxt-dev.log 2>&1 &
- name: Start Cloudflare tunnels
env:
# Pinned release + SHA-256 so a changed upstream artifact can
# never execute with this workflow's credentials.
CLOUDFLARED_VERSION: '2026.8.2'
CLOUDFLARED_SHA256: 'fcfb02b575a52ca1af2e3267af4e1517bcdeb30ac48c834b69abaed3c0576ad2'
run: |
set -euo pipefail
[ "$(dpkg --print-architecture)" = "amd64" ]
curl -sSL -o cloudflared "https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-linux-amd64"
echo "${CLOUDFLARED_SHA256} cloudflared" | sha256sum -c -
sudo install -m 0755 cloudflared /usr/local/bin/cloudflared
nohup cloudflared tunnel --url http://localhost:8888 > /tmp/tunnel-backend.log 2>&1 &
if [ "${{ inputs.mode }}" != "dev" ]; then
nohup cloudflared tunnel --url http://localhost:3000 > /tmp/tunnel-frontend.log 2>&1 &
fi
if [ "${{ inputs.mode }}" != "prod" ]; then
nohup cloudflared tunnel --url http://localhost:3001 > /tmp/tunnel-frontend-dev.log 2>&1 &
fi
# VS Code in the browser, for poking at the checkout live. Only
# started when PREVIEW_CODE_PASSWORD (secret) is set: without a
# password there is nothing between a public Quick Tunnel URL and
# a shell as the runner user.
- name: Start code-server (VS Code in the browser)
env:
PREVIEW_CODE_PASSWORD: ${{ secrets.PREVIEW_CODE_PASSWORD }}
CODE_SERVER_VERSION: 'v4.132.0'
run: |
set -euo pipefail
CODE_SERVER_ENABLED=false
if [ -n "${PREVIEW_CODE_PASSWORD:-}" ]; then
# Pinned .deb + SHA-256, not the mutable install.sh - the
# installer script runs with PREVIEW_CODE_PASSWORD in its
# environment, so a changed upstream script must never
# execute here. Update both together when bumping the version.
CODE_SERVER_DEB_SHA256="18e0e69920ab23b725cb219fb42bc045a908421448cf496a3124314e1a02bcf1"
curl -fsSL --max-time 120 "https://github.com/coder/code-server/releases/download/${CODE_SERVER_VERSION}/code-server_${CODE_SERVER_VERSION#v}_amd64.deb" -o /tmp/code-server.deb
echo "${CODE_SERVER_DEB_SHA256} /tmp/code-server.deb" | sha256sum -c - || { echo "code-server checksum mismatch" >&2; exit 1; }
sudo dpkg -i /tmp/code-server.deb
PASSWORD="$PREVIEW_CODE_PASSWORD" nohup code-server --auth password --bind-addr 0.0.0.0:8080 "$GITHUB_WORKSPACE" > /tmp/code-server.log 2>&1 &
nohup cloudflared tunnel --url http://localhost:8080 > /tmp/tunnel-code.log 2>&1 &
CODE_SERVER_ENABLED=true
fi
echo "CODE_SERVER_ENABLED=$CODE_SERVER_ENABLED" >> "$GITHUB_ENV"
- name: Wait for tunnels and print preview URLs
env:
MODE: ${{ inputs.mode }}
DURATION_INPUT: ${{ inputs.duration }}
run: |
set -euo pipefail
# Clamp the sleep: timeout-minutes must also cover provisioning.
# Sanitize first: GitHub does not strictly validate `type: number`,
# and bash arithmetic expands variable contents recursively - a
# crafted value like `1+$(...)` would execute inside $(( )).
# Same tr -cd guard the GitLab preview job already uses.
DURATION=$(printf '%s\n' "$DURATION_INPUT" | tr -cd '0-9')
DURATION=${DURATION:-3600}
[ "$DURATION" -le 18000 ] || DURATION=18000
echo "PREVIEW_LIVE_SECONDS=$DURATION" >> "$GITHUB_ENV"
MODE="${MODE:-both}"
for i in $(seq 1 30); do
BACKEND_URL=$(grep -oE 'https://[a-zA-Z0-9-]+\.trycloudflare\.com' /tmp/tunnel-backend.log 2>/dev/null | head -1 || true)
FRONTEND_URL=$(grep -oE 'https://[a-zA-Z0-9-]+\.trycloudflare\.com' /tmp/tunnel-frontend.log 2>/dev/null | head -1 || true)
FRONTEND_DEV_URL=$(grep -oE 'https://[a-zA-Z0-9-]+\.trycloudflare\.com' /tmp/tunnel-frontend-dev.log 2>/dev/null | head -1 || true)
CODE_URL=$(grep -oE 'https://[a-zA-Z0-9-]+\.trycloudflare\.com' /tmp/tunnel-code.log 2>/dev/null | head -1 || true)
READY=1
[ -n "$BACKEND_URL" ] || READY=0
[ "$MODE" = "dev" ] || [ -n "$FRONTEND_URL" ] || READY=0
[ "$MODE" = "prod" ] || [ -n "$FRONTEND_DEV_URL" ] || READY=0
[ "$CODE_SERVER_ENABLED" != "true" ] || [ -n "$CODE_URL" ] || READY=0
if [ "$READY" -eq 1 ]; then break; fi
sleep 1
done
# A required tunnel that never came up should fail the job,
# not leave it sleeping for the full preview duration.
FAILED=0
if [ -z "$BACKEND_URL" ]; then
echo "::error::backend tunnel not ready"; tail -n 20 /tmp/tunnel-backend.log || true; FAILED=1
fi
if [ "$MODE" != "dev" ] && [ -z "$FRONTEND_URL" ]; then
echo "::error::frontend (prod) tunnel not ready"; tail -n 20 /tmp/tunnel-frontend.log || true; FAILED=1
fi
if [ "$MODE" != "prod" ] && [ -z "$FRONTEND_DEV_URL" ]; then
echo "::error::frontend (dev) tunnel not ready"; tail -n 20 /tmp/tunnel-frontend-dev.log || true; FAILED=1
fi
if [ "$CODE_SERVER_ENABLED" = "true" ] && [ -z "$CODE_URL" ]; then
echo "::error::code-server tunnel not ready"; tail -n 20 /tmp/tunnel-code.log || true; FAILED=1
fi
[ "$FAILED" -eq 0 ] || exit 1
echo "=================================================="
if [ "$MODE" != "dev" ]; then
echo " Frontend (prod): ${FRONTEND_URL:-not ready - check /tmp/tunnel-frontend.log}"
fi
if [ "$MODE" != "prod" ]; then
echo " Frontend (dev): ${FRONTEND_DEV_URL:-not ready - check /tmp/tunnel-frontend-dev.log} <- HMR: edits appear live"
fi
echo " Backend: ${BACKEND_URL:-not ready - check /tmp/tunnel-backend.log}"
if [ "$CODE_SERVER_ENABLED" = "true" ]; then
# No login link in the log: `drush uli` output is a live
# credential, and job logs are readable by more people than
# you think even in owner mode. Get one from the code-server
# terminal instead - it never touches the log there.
echo " Login link: run \`make drush uli\` in a code-server terminal"
echo " Code: ${CODE_URL:-not ready} (password: your PREVIEW_CODE_PASSWORD secret)"
else
echo " Code: disabled - set the PREVIEW_CODE_PASSWORD secret to enable"
fi
echo " Note: tunnel URLs survive restarts on the same port."
echo " prod does NOT watch files - rebuild it from a"
echo " code-server terminal if you edit and want prod."
echo " Live for: ${DURATION}s (cancel this workflow to stop early)"
echo "=================================================="
- name: Keep the preview alive
run: sleep "$PREVIEW_LIVE_SECONDS"