11#! /usr/bin/env bash
22set -euo pipefail
33
4+ trap ' echo "Termination signal received — failing the job."; exit 1' TERM INT
5+
46PLATFORM=${1:? Expected platform to be provided as first parameter}
57ARCH=${2:? Expected architecture to be provided as second parameter}
68
@@ -9,7 +11,39 @@ if .buildkite/commands/should-skip-job.sh --job-type validation; then
911fi
1012
1113echo ' --- :package: Install deps'
12- bash .buildkite/commands/install-node-dependencies.sh
14+ if [ " $PLATFORM " = " linux" ]; then
15+ # Linux runs inside a Debian Node container on the shared `default` queue.
16+ # The a8c-ci-toolkit cache helpers (hash_file, restore_cache) only exist on
17+ # the host, so install-node-dependencies.sh can't run here. Also install
18+ # Electron's runtime libraries plus xvfb (CI agents are headless) and the
19+ # browser dependencies Playwright would otherwise fetch via `npx playwright
20+ # install-deps` — same set, but apt is faster than letting Playwright shell
21+ # out to it again.
22+ apt-get -o Acquire::Retries=3 update
23+ apt-get install -y --no-install-recommends \
24+ xvfb \
25+ xauth \
26+ libcap2-bin \
27+ libnss3 \
28+ libatk1.0-0 \
29+ libatk-bridge2.0-0 \
30+ libcups2 \
31+ libdrm2 \
32+ libgtk-3-0 \
33+ libgbm1 \
34+ libasound2 \
35+ libxkbcommon0 \
36+ libxcomposite1 \
37+ libxdamage1 \
38+ libxfixes3 \
39+ libxrandr2 \
40+ libpango-1.0-0 \
41+ libcairo2 \
42+ libxss1
43+ npm ci --unsafe-perm --no-audit --no-progress --maxsockets 1
44+ else
45+ bash .buildkite/commands/install-node-dependencies.sh
46+ fi
1347
1448export IS_DEV_BUILD=true
1549
@@ -21,6 +55,9 @@ case "$PLATFORM" in
2155 windows)
2256 FORGE_PLATFORM=" win32"
2357 ;;
58+ linux)
59+ FORGE_PLATFORM=" linux"
60+ ;;
2461 * )
2562 echo " Unknown platform: $PLATFORM "
2663 exit 1
@@ -33,10 +70,122 @@ esac
3370echo " --- :package: Package app for testing ($PLATFORM -$ARCH )"
3471npm -w studio-app run package -- --arch=" $ARCH " --platform=" $FORGE_PLATFORM "
3572
73+ if [ " $PLATFORM " = " linux" ]; then
74+ # The packaged app under apps/studio/out was created by electron-forge
75+ # running as root with restrictive default perms — the node user (which
76+ # runs Playwright below) gets EACCES on scandir without this. `X` (capital)
77+ # keeps directories traversable and existing executables executable,
78+ # without making every regular file executable.
79+ echo ' --- :wrench: Open packaged app perms for non-root test user'
80+ chmod -R go+rX apps/studio/out
81+
82+ # Chromium's setuid sandbox helper (`chrome-sandbox`) requires real-root
83+ # ownership + setuid bit. Under Docker user-namespace remapping, the
84+ # container's "root" isn't real-root, so the kernel doesn't honor setuid
85+ # and Chromium aborts on the misconfigured helper. Removing the helper
86+ # makes Chromium skip the SUID path and fall through to the user-
87+ # namespace sandbox, which doesn't need setuid.
88+ rm -f " apps/studio/out/Studio-linux-${ARCH} /chrome-sandbox"
89+
90+ # Grant cap_net_bind_service to the bundled node so the proxy daemon can
91+ # listen on privileged ports 80/443 without running as root — mirrors the
92+ # DEB postinst hook (apps/studio/installers/linux/postinst.sh), which
93+ # doesn't run for `electron-forge package` output. Without this, custom-
94+ # domain HTTP/HTTPS tests fail to bind in the non-root test process.
95+ BUNDLED_NODE=" apps/studio/out/Studio-linux-${ARCH} /resources/bin/node"
96+ if [ -x " $BUNDLED_NODE " ]; then
97+ echo ' --- :shield: Grant cap_net_bind_service to bundled node'
98+ setcap ' cap_net_bind_service=+ep' " $BUNDLED_NODE " || \
99+ echo " warning: setcap failed on $BUNDLED_NODE ; privileged-port tests may fail to bind." >&2
100+ fi
101+ fi
102+
103+ echo ' --- :mag: Verify CLI build artifacts'
104+ CLI_DIST=" apps/cli/dist/cli"
105+ missing=()
106+ for f in reprint-child.mjs main.mjs wp-files/reprint/reprint.phar; do
107+ [ -f " $CLI_DIST /$f " ] || missing+=(" $f " )
108+ done
109+ if [ ${# missing[@]} -gt 0 ]; then
110+ echo " Missing CLI build artifacts in $CLI_DIST : ${missing[*]} "
111+ exit 1
112+ fi
113+ echo " All required CLI build artifacts present."
114+
36115echo ' --- :playwright: Run End To End Tests'
37116
38- echo ' Installing Playwright browsers...'
39- npx playwright install
117+ # Electron needs a display server to launch. CI Linux agents are headless,
118+ # so wrap with xvfb-run to provide a virtual display.
119+ if [ " $PLATFORM " = " linux" ]; then
120+ # Run Playwright as the non-root `node` user (uid 1000, present in
121+ # node:bookworm). Electron's main delegate aborts with a FATAL on
122+ # `geteuid() == 0` in every helper subprocess; --no-sandbox from the
123+ # parent argv doesn't reliably propagate to all helper types, so the
124+ # only robust workaround is to not be root for the test invocation.
125+ #
126+ # We can't chown /workdir because Docker user-namespace remapping on
127+ # this agent denies cross-namespace chown even for container root.
128+ # Instead, redirect Playwright's output to /tmp (which node owns) and
129+ # copy results back to /workdir as root afterwards so the Buildkite
130+ # artifact uploader finds them under the configured artifact_paths.
131+ rm -rf /tmp/test-results
132+ mkdir -p /tmp/test-results
133+ chown node:node /tmp/test-results
134+
135+ # TODO(rsm-2593): --max-failures=1 is a temporary debug aid so the suite
136+ # bails on the first failing test instead of retrying through all of them
137+ # (~40 min on a broken Electron launch). Remove before merging.
138+ # Install only Chromium, not Firefox/Webkit. Our tests drive Electron via
139+ # Playwright's `_electron` API, but `test()` from `@playwright/test` still
140+ # auto-spins a Chromium-headless-shell for the default `page` fixture even
141+ # when the test body never touches it — so a Chromium install is required.
142+ # Skipping Firefox/Webkit avoids the noisy "missing dependencies"
143+ # validation against libs (gstreamer/flite/gtk-4/libavif/etc.) we don't
144+ # need. Run inside the `su node` block so the browser lands in the same
145+ # `~/.cache/ms-playwright` that the test process reads from.
146+ test_exit=0
147+ su -s /bin/bash node -c '
148+ set -euo pipefail
149+ cd /workdir
150+ echo "Installing Playwright Chromium..."
151+ npx playwright install chromium
152+ echo "Running Playwright tests..."
153+ # Explicit screen size: xvfb-run defaults to 1280x1024, which can leave
154+ # right-edge content (e.g. the preferences Save button) below the fold
155+ # for the split-pane settings layout. 1920x1080 matches a typical
156+ # desktop and avoids relying on scroll-into-view.
157+ inner_exit=0
158+ xvfb-run -a -s "-screen 0 1920x1080x24" \
159+ npx playwright test --max-failures=1 --output=/tmp/test-results || inner_exit=$?
160+ # On failure, collect daemon logs into /tmp/test-results (copied to the
161+ # artifact dir below). $HOME here is the node user that ran the tests.
162+ if [ "$inner_exit" -ne 0 ] && [ -d "$HOME/.studio/daemon/logs" ]; then
163+ cp -r "$HOME/.studio/daemon/logs" /tmp/test-results/daemon-logs || true
164+ fi
165+ exit "$inner_exit"
166+ ' || test_exit=$?
167+
168+ if [ -d /tmp/test-results ]; then
169+ echo ' --- :file_folder: Copy test results for artifact upload'
170+ mkdir -p test-results
171+ cp -r /tmp/test-results/. test-results/ || true
172+ fi
173+
174+ exit " $test_exit "
175+ else
176+ echo ' Installing Playwright browsers...'
177+ npx playwright install
40178
41- echo ' Running Playwright tests...'
42- npx playwright test
179+ echo ' Running Playwright tests...'
180+ # Capture the exit code so a failure doesn't trip `set -e` before we collect
181+ # the daemon logs (~/.studio/daemon/logs) for artifact upload.
182+ test_exit=0
183+ npx playwright test || test_exit=$?
184+
185+ if [ " $test_exit " -ne 0 ] && [ -d " $HOME /.studio/daemon/logs" ]; then
186+ mkdir -p test-results/daemon-logs
187+ cp -r " $HOME /.studio/daemon/logs/." test-results/daemon-logs/ || true
188+ fi
189+
190+ exit " $test_exit "
191+ fi
0 commit comments