From c78c84c3ca18e02fc7c7d020c70ec867faaa960b Mon Sep 17 00:00:00 2001 From: TheFox0x7 Date: Sun, 10 May 2026 11:16:02 +0200 Subject: [PATCH] test(e2e): run playwright via container (#37300) Enable running playwright tests on unsupported platforms as well Signed-off-by: wxiaoguang Signed-off-by: silverwind Co-authored-by: wxiaoguang Co-authored-by: silverwind Co-authored-by: Claude (Opus 4.7) --- Makefile | 5 +-- tools/test-e2e.sh | 101 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 102 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index bc343eee01..40bc202a54 100644 --- a/Makefile +++ b/Makefile @@ -468,12 +468,11 @@ migrations.individual.test\#%: .PHONY: playwright playwright: deps-frontend - @# on GitHub Actions VMs, playwright's system deps are pre-installed - @pnpm exec playwright install $(if $(GITHUB_ACTIONS),,--with-deps) chromium firefox $(PLAYWRIGHT_FLAGS) + @./tools/test-e2e.sh install .PHONY: test-e2e test-e2e: playwright frontend backend - @EXECUTABLE=$(EXECUTABLE) ./tools/test-e2e.sh $(GITEA_TEST_E2E_FLAGS) + @EXECUTABLE=$(EXECUTABLE) ./tools/test-e2e.sh run $(GITEA_TEST_E2E_FLAGS) .PHONY: build build: frontend backend ## build everything diff --git a/tools/test-e2e.sh b/tools/test-e2e.sh index 39405387b5..d6c053ff1f 100755 --- a/tools/test-e2e.sh +++ b/tools/test-e2e.sh @@ -1,13 +1,99 @@ #!/bin/bash set -euo pipefail +CONTAINER_RUNTIME="${CONTAINER_RUNTIME:-docker}" +CONTAINER_NAME="gitea-e2e-runner-$$" + +free_port() { + node -e "const s=require('net').createServer();s.listen(0,'127.0.0.1',()=>{process.stdout.write(String(s.address().port));s.close()})" +} + +detect_playwright_mode() { + if [ "${PLAYWRIGHT_MODE:-auto}" = "local" ] || [ "${PLAYWRIGHT_MODE:-auto}" = "container" ]; then + return + fi + + PLAYWRIGHT_MODE="local" + + if [ "$(uname -s)" = "Linux" ]; then + # playwright only supports ubuntu/debian officially + if ! grep -qE '^ID(_LIKE)?=.*(ubuntu|debian)' /etc/os-release 2>/dev/null; then + PLAYWRIGHT_MODE="container" + fi + fi +} + +wait_for_container() { + local max_wait=30 + local elapsed=0 + echo "Waiting for container to start..." + while ! (echo > "/dev/tcp/127.0.0.1/$PLAYWRIGHT_SERVER_PORT") 2>/dev/null; do + if [ "$("$CONTAINER_RUNTIME" inspect -f '{{.State.Running}}' "$CONTAINER_NAME" 2>/dev/null)" != "true" ]; then + echo "Error: container exited before becoming ready." >&2 + "$CONTAINER_RUNTIME" logs "$CONTAINER_NAME" >&2 || true + return 1 + fi + if [ "$elapsed" -ge "$max_wait" ]; then + echo "Error: container did not become ready after ${max_wait}s." >&2 + "$CONTAINER_RUNTIME" logs "$CONTAINER_NAME" >&2 || true + return 1 + fi + sleep 1 + elapsed=$((elapsed + 1)) + done + echo "Container is ready." +} + +CMD="${1:-run}" +if [ "$CMD" = "install" ] || [ "$CMD" = "run" ]; then + [ $# -gt 0 ] && shift +else + CMD="run" +fi + +detect_playwright_mode + +if [ "$PLAYWRIGHT_MODE" = "container" ]; then + if ! command -v "$CONTAINER_RUNTIME" >/dev/null 2>&1; then + echo "error: PLAYWRIGHT_MODE=container but '$CONTAINER_RUNTIME' is not installed." >&2 + echo "Install docker/podman or set CONTAINER_RUNTIME to an available runtime." >&2 + exit 1 + fi + PLAYWRIGHT_VERSION=$(sed -n 's/.*"@playwright\/test"[[:space:]]*:[[:space:]]*"[^[:digit:]]*\([^"]*\)".*/\1/p' package.json) + if ! [[ "$PLAYWRIGHT_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.]+)?$ ]]; then + echo "error: invalid @playwright/test version in package.json: '${PLAYWRIGHT_VERSION}'" >&2 + exit 1 + fi + PLAYWRIGHT_IMAGE="mcr.microsoft.com/playwright:v${PLAYWRIGHT_VERSION}-noble" +fi + +if [ "$CMD" = "install" ]; then + if [ "$PLAYWRIGHT_MODE" = "local" ]; then + # on GitHub Actions VMs, playwright's system deps are pre-installed + if [ -z "${GITHUB_ACTIONS:-}" ]; then + pnpm exec playwright install --with-deps chromium firefox ${PLAYWRIGHT_FLAGS:-} + else + pnpm exec playwright install chromium firefox ${PLAYWRIGHT_FLAGS:-} + fi + else + echo "Running playwright in container as host distro is not supported by playwright directly" + if ! "$CONTAINER_RUNTIME" image inspect "$PLAYWRIGHT_IMAGE" >/dev/null 2>&1; then + "$CONTAINER_RUNTIME" pull "$PLAYWRIGHT_IMAGE" + fi + fi + exit 0 +fi + # Create isolated work directory WORK_DIR=$(mktemp -d) # Find a random free port -FREE_PORT=$(node -e "const s=require('net').createServer();s.listen(0,'127.0.0.1',()=>{process.stdout.write(String(s.address().port));s.close()})") +FREE_PORT=$(free_port) cleanup() { + if [ "$PLAYWRIGHT_MODE" = "container" ]; then + "$CONTAINER_RUNTIME" stop "$CONTAINER_NAME" >/dev/null 2>&1 || true + fi if [ -n "${SERVER_PID:-}" ]; then kill "$SERVER_PID" 2>/dev/null || true wait "$SERVER_PID" 2>/dev/null || true @@ -16,6 +102,16 @@ cleanup() { } trap cleanup EXIT +if [ "$PLAYWRIGHT_MODE" = "container" ]; then + PLAYWRIGHT_SERVER_PORT=$(free_port) + # --network=host: container needs host loopback to reach gitea. + "$CONTAINER_RUNTIME" run --network=host --name "$CONTAINER_NAME" -d --rm --init --workdir /home/pwuser --user pwuser "$PLAYWRIGHT_IMAGE" /bin/sh -c "npx -y playwright@${PLAYWRIGHT_VERSION} run-server --port ${PLAYWRIGHT_SERVER_PORT} --host 0.0.0.0" + + if ! wait_for_container; then + exit 1 + fi +fi + # Write config file for isolated instance mkdir -p "$WORK_DIR/custom/conf" cat > "$WORK_DIR/custom/conf/app.ini" <