-
Notifications
You must be signed in to change notification settings - Fork 39
229 lines (205 loc) · 9.78 KB
/
Copy pathacceptance-tests.yml
File metadata and controls
229 lines (205 loc) · 9.78 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
name: Acceptance Tests
on:
push:
branches: [ dev/main ]
pull_request:
branches: [ dev/main ]
workflow_dispatch:
env:
# CI-only credentials for a throwaway database and Umbraco install.
MSSQL_SA_PASSWORD: 'Acceptance1234!'
UMBRACO_USER_LOGIN: 'acceptance@seotoolkit.local'
UMBRACO_USER_PASSWORD: 'SeoToolkitAcceptance1234!'
SITE_URL: 'https://localhost:5001'
# Kestrel needs a cert to serve HTTPS; Playwright ignores validation errors, so a
# throwaway dev cert is enough. See the "Umbraco backoffice auth requires HTTPS" note
# on the Start step.
DEV_CERT_PASSWORD: 'AcceptanceCert1234!'
jobs:
acceptance:
runs-on: ubuntu-latest
services:
sqlserver:
image: mcr.microsoft.com/mssql/server:2022-latest
env:
ACCEPT_EULA: 'Y'
MSSQL_SA_PASSWORD: 'Acceptance1234!'
ports:
- 1433:1433
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version-file: tests/SeoToolkit.AcceptanceTests/.nvmrc
cache: npm
cache-dependency-path: |
src/package-lock.json
tests/SeoToolkit.AcceptanceTests/package-lock.json
- name: Wait for SQL Server
run: |
# Probe the mapped port from the runner rather than docker exec-ing sqlcmd:
# the sqlcmd path inside the image differs between builds (mssql-tools vs
# mssql-tools18) and `docker ps --filter ancestor` is brittle. Bash /dev/tcp
# needs neither. SQL Server opens the port a moment before it accepts logins,
# hence the grace period.
for i in $(seq 1 60); do
if (exec 3<>/dev/tcp/localhost/1433) 2>/dev/null; then
echo "SQL Server is accepting connections."
sleep 15
exit 0
fi
sleep 5
done
echo "::error::SQL Server did not start within 300s."
docker ps -a
exit 1
# Must run BEFORE any dotnet build: the backoffice bundles are emitted into each
# feature project's wwwroot/entry, and .NET snapshots static web assets at build
# time. Build .NET first and the site boots with no SeoToolkit UI at all.
- name: Install frontend dependencies
working-directory: ./src
run: npm ci
- name: Build backoffice bundles
working-directory: ./src
run: npm run build:all
- name: Publish the test site
run: dotnet publish ./src/SeoToolkit.Umbraco.Site -c Release -o ./publish
- name: Verify backoffice bundles were published
run: |
# Exactly the paths the ManifestLoader.cs files register as backofficeEntryPoint.
missing=0
for entry in common metaFields notFound redirects robotstxt scriptManager siteAudit sitemap; do
file="./publish/wwwroot/App_Plugins/SeoToolkit/entry/$entry/$entry.js"
if [ ! -s "$file" ]; then
echo "::error::Missing backoffice bundle $file - did 'npm run build:all' run before 'dotnet publish'?"
missing=1
fi
done
exit $missing
- name: Create a development HTTPS certificate
run: dotnet dev-certs https -ep ${{ github.workspace }}/devcert.pfx -p "$DEV_CERT_PASSWORD"
- name: Start the site
# MUST run from inside the publish folder. ASP.NET Core resolves ContentRoot from
# the current working directory, NOT from the dll location, so starting this from
# the repo root gives a process that boots happily and answers /umbraco, but
# resolves wwwroot/, Views/ and umbraco/ against the repo root - every
# /App_Plugins/SeoToolkit/... request 404s and the readiness check never passes.
working-directory: ./publish
run: |
nohup dotnet ./SeoToolkit.Umbraco.Site.dll > "$GITHUB_WORKSPACE/site.log" 2>&1 &
echo $! > "$GITHUB_WORKSPACE/site.pid"
echo "Started site, pid $(cat "$GITHUB_WORKSPACE/site.pid")"
env:
# Umbraco backoffice auth REQUIRES HTTPS. The login flow relies on the
# __Host-umbPkceCode cookie, and the __Host- prefix plus the Secure flag mean
# the browser never returns it over plain HTTP - the helper package then fails
# in extractPKCECookie with "Cannot read properties of undefined".
ASPNETCORE_URLS: ${{ env.SITE_URL }}
# Unprefixed on purpose: Program.cs uses Host.CreateDefaultBuilder, whose app
# configuration binds plain environment variables. Whether ASPNETCORE_-prefixed
# names also reach the Kestrel section depends on host-config chaining, so do
# not rely on it.
Kestrel__Certificates__Default__Path: ${{ github.workspace }}/devcert.pfx
Kestrel__Certificates__Default__Password: ${{ env.DEV_CERT_PASSWORD }}
ConnectionStrings__umbracoDbDSN: "Server=localhost,1433;Database=SeoToolkitAcceptance;User Id=sa;Password=${{ env.MSSQL_SA_PASSWORD }};TrustServerCertificate=true;"
ConnectionStrings__umbracoDbDSN_ProviderName: 'Microsoft.Data.SqlClient'
Umbraco__CMS__Unattended__InstallUnattended: 'true'
Umbraco__CMS__Unattended__UnattendedUserName: 'Acceptance Tests'
Umbraco__CMS__Unattended__UnattendedUserEmail: ${{ env.UMBRACO_USER_LOGIN }}
Umbraco__CMS__Unattended__UnattendedUserPassword: ${{ env.UMBRACO_USER_PASSWORD }}
Umbraco__CMS__Unattended__PackageMigrationsUnattended: 'true'
# Umbraco only auto-creates the database for SQLite and LocalDB. Against a
# real SQL Server this must be explicit or boot fails on a connection error.
Umbraco__CMS__Global__InstallMissingDatabase: 'true'
- name: Wait for the site
run: |
# Umbraco writes its boot log to its own Serilog FILE sink. site.log (stdout)
# only ever carries unhandled exceptions, so it is empty for a slow or stuck
# boot - always look at the Umbraco logs first when this step fails.
umbraco_logs() {
echo "--- site.log (stdout/stderr) ---"
cat site.log 2>/dev/null || echo "(no site.log)"
echo "--- Umbraco trace logs ---"
if ls ./publish/umbraco/Logs/*.json >/dev/null 2>&1; then
tail -n 40 ./publish/umbraco/Logs/*.json
else
echo "(no Umbraco log files - the host never got far enough to create them)"
fi
echo "--- listening sockets ---"
ss -lntp 2>/dev/null || netstat -lntp 2>/dev/null || true
}
# First boot creates the database, runs every Umbraco migration and then the
# Clean starter kit package migration. That is far slower on a 2-core runner
# against a SQL Server container than it is locally.
deadline=$((SECONDS + 600))
last_report=0
while [ $SECONDS -lt $deadline ]; do
# Fail fast if the host died (bad connection string, port clash, migration
# crash) instead of burning the whole budget on a process that is gone.
if ! kill -0 "$(cat site.pid)" 2>/dev/null; then
echo "::error::The site process exited during startup."
umbraco_logs
exit 1
fi
backoffice_ok=$(curl -skf -o /dev/null "$SITE_URL/umbraco" && echo yes || echo no)
assets_ok=$(curl -skf -o /dev/null "$SITE_URL/App_Plugins/SeoToolkit/entry/common/common.js" && echo yes || echo no)
if [ "$backoffice_ok" = yes ] && [ "$assets_ok" = yes ]; then
echo "Backoffice is up and serving SeoToolkit assets after ${SECONDS}s."
exit 0
fi
# Report progress so a slow boot is visibly different from a stuck one.
if [ $((SECONDS - last_report)) -ge 30 ]; then
last_report=$SECONDS
echo "[${SECONDS}s] backoffice=$backoffice_ok assets=$assets_ok"
if ls ./publish/umbraco/Logs/*.json >/dev/null 2>&1; then
tail -n 2 ./publish/umbraco/Logs/*.json | tail -n 4
else
echo " (Umbraco has not written a log file yet)"
fi
fi
sleep 5
done
echo "::error::Site did not become ready within 600s."
umbraco_logs
exit 1
- name: Install acceptance test dependencies
working-directory: ./tests/SeoToolkit.AcceptanceTests
run: npm ci
- name: Install Playwright browsers
working-directory: ./tests/SeoToolkit.AcceptanceTests
run: npx playwright install --with-deps chromium
- name: Run acceptance tests
working-directory: ./tests/SeoToolkit.AcceptanceTests
run: npx playwright test
env:
URL: ${{ env.SITE_URL }}
STORAGE_STATE_PATH: 'playwright/.auth/user.json'
CONSOLE_ERRORS_PATH: 'console-errors.json'
- name: Upload Playwright report
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: |
tests/SeoToolkit.AcceptanceTests/playwright-report/
tests/SeoToolkit.AcceptanceTests/test-results/
tests/SeoToolkit.AcceptanceTests/console-errors.json
retention-days: 7
- name: Upload site and Umbraco logs
if: always()
uses: actions/upload-artifact@v4
with:
name: site-logs
# site.log is stdout only. The Umbraco trace logs are the useful ones for a
# boot failure - see the note in the "Wait for the site" step.
path: |
site.log
publish/umbraco/Logs/
if-no-files-found: warn
retention-days: 7