From 6434a615f9ac48ed53c2154a7f000b5de36e4372 Mon Sep 17 00:00:00 2001 From: Martin Donadieu Date: Tue, 28 Apr 2026 11:39:45 +0200 Subject: [PATCH 1/5] fix(ci): replace background-action in tests workflow --- .github/scripts/start-background-service.sh | 79 ++++++++++++++++++++ .github/workflows/tests.yml | 80 ++++++++++++--------- 2 files changed, 127 insertions(+), 32 deletions(-) create mode 100644 .github/scripts/start-background-service.sh diff --git a/.github/scripts/start-background-service.sh b/.github/scripts/start-background-service.sh new file mode 100644 index 0000000000..052959f1c0 --- /dev/null +++ b/.github/scripts/start-background-service.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash + +set -euo pipefail + +service_name="${BACKGROUND_SERVICE_NAME:-}" +run_command="${BACKGROUND_RUN_COMMAND:-}" +wait_on_resources_raw="${BACKGROUND_WAIT_ON:-}" +log_path="${BACKGROUND_LOG_PATH:-}" +workdir="${BACKGROUND_WORKDIR:-$PWD}" +wait_timeout_ms="${BACKGROUND_WAIT_TIMEOUT_MS:-60000}" +wait_interval_ms="${BACKGROUND_WAIT_INTERVAL_MS:-500}" +tail_lines="${BACKGROUND_TAIL_LINES:-200}" +wait_on_version="${BACKGROUND_WAIT_ON_VERSION:-8.0.1}" + +if [ -z "${service_name}" ] || [ -z "${run_command}" ] || [ -z "${wait_on_resources_raw}" ] || [ -z "${log_path}" ]; then + echo "::error::BACKGROUND_SERVICE_NAME, BACKGROUND_RUN_COMMAND, BACKGROUND_WAIT_ON, and BACKGROUND_LOG_PATH are required." + exit 1 +fi + +mkdir -p "$(dirname "${log_path}")" +: > "${log_path}" + +wait_on_resources=() +while IFS= read -r resource; do + if [ -n "${resource}" ]; then + wait_on_resources+=("${resource}") + fi +done < <(printf '%s\n' "${wait_on_resources_raw}" | sed '/^[[:space:]]*$/d') + +if [ "${#wait_on_resources[@]}" -eq 0 ]; then + echo "::error::${service_name} is missing wait-on resources." + exit 1 +fi + +dump_log_tail() { + if [ ! -f "${log_path}" ]; then + echo "No log file found at ${log_path}" + return + fi + + echo "::group::${service_name} log tail" + tail -n "${tail_lines}" "${log_path}" || true + echo "::endgroup::" +} + +echo "::group::Start ${service_name}" +echo "Working directory: ${workdir}" +echo "Log file: ${log_path}" +printf 'Wait-on targets:\n' +printf ' - %s\n' "${wait_on_resources[@]}" +echo "::endgroup::" + +pushd "${workdir}" >/dev/null +nohup bash -lc "${run_command}" >"${log_path}" 2>&1 & +pid=$! +disown "${pid}" 2>/dev/null || true +popd >/dev/null + +if [ -n "${GITHUB_OUTPUT:-}" ]; then + { + echo "pid=${pid}" + echo "log_path=${log_path}" + } >> "${GITHUB_OUTPUT}" +fi + +sleep 1 +if ! kill -0 "${pid}" 2>/dev/null; then + echo "::error::${service_name} exited before it became ready." + dump_log_tail + exit 1 +fi + +if ! bunx "wait-on@${wait_on_version}" "${wait_on_resources[@]}" --timeout "${wait_timeout_ms}" --interval "${wait_interval_ms}" --log --verbose; then + echo "::error::${service_name} failed to become ready." + dump_log_tail + exit 1 +fi + +echo "::notice::${service_name} is ready (pid ${pid})." diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0903c31dec..7a68a3663d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -15,7 +15,7 @@ permissions: {} env: DENO_DIR: my_cache_directory - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true' + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" jobs: test_all: @@ -86,49 +86,65 @@ jobs: run: supabase test db - name: Lint SQL run: supabase db lint -s public --fail-on warning - - uses: JarvusInnovations/background-action@v1 + - id: bootstrap_edge_server name: Bootstrap Edge server - with: - run: supabase functions serve & - wait-on: | + env: + BACKGROUND_SERVICE_NAME: Bootstrap Edge server + BACKGROUND_RUN_COMMAND: supabase functions serve + BACKGROUND_WAIT_ON: | http-get://127.0.0.1:54321/functions/v1/ok - # IMPORTANT: to use environment variables in wait-on, you must use this form: ${{ env.VAR }} - # See wait-on section below for all resource types and prefixes - - tail: stderr,stdout # true = stderr,stdout - # This will allow you to monitor the progress live - - log-output-resume: stderr,stdout - # Eliminates previously output stderr log entries from post-run output - - wait-for: 1m - - log-output: stderr,stdout # same as true - - log-output-if: true - # failure = exit-early or timeout - - working-directory: . + BACKGROUND_LOG_PATH: ${{ runner.temp }}/bootstrap-edge-server.log + BACKGROUND_WAIT_TIMEOUT_MS: 60000 + run: bash .github/scripts/start-background-service.sh - name: Run all backend and CLI tests run: bun run test:all - - uses: JarvusInnovations/background-action@v1 + - name: Show Edge server logs after backend test failure + if: failure() && steps.bootstrap_edge_server.outputs.log_path != '' + run: | + echo "::group::Bootstrap Edge server log" + tail -n 200 "${{ steps.bootstrap_edge_server.outputs.log_path }}" || true + echo "::endgroup::" + - id: start_cloudflare_workers name: Start Cloudflare Workers for testing - with: - run: | + env: + BACKGROUND_SERVICE_NAME: Cloudflare Workers + BACKGROUND_RUN_COMMAND: | chmod +x scripts/start-cloudflare-workers.sh ./scripts/start-cloudflare-workers.sh - wait-on: | + BACKGROUND_WAIT_ON: | http-get://127.0.0.1:8787/ok http-get://127.0.0.1:8788/ok http-get://127.0.0.1:8789/ok - tail: stderr,stdout - log-output-resume: stderr,stdout - wait-for: 2m - log-output: stderr,stdout - log-output-if: true - working-directory: . + BACKGROUND_LOG_PATH: ${{ runner.temp }}/cloudflare-workers.log + BACKGROUND_WAIT_TIMEOUT_MS: 120000 + BACKGROUND_TAIL_LINES: 400 + run: bash .github/scripts/start-background-service.sh - name: Run Cloudflare Workers backend tests run: bun run test:cloudflare:backend + - name: Show Cloudflare Worker logs after Cloudflare test failure + if: failure() && steps.start_cloudflare_workers.outputs.log_path != '' + run: | + echo "::group::Cloudflare Worker log" + tail -n 400 "${{ steps.start_cloudflare_workers.outputs.log_path }}" || true + echo "::endgroup::" + - name: Upload background service logs + if: failure() + uses: actions/upload-artifact@v6 + with: + name: test-all-background-service-logs + path: | + ${{ steps.bootstrap_edge_server.outputs.log_path }} + ${{ steps.start_cloudflare_workers.outputs.log_path }} + if-no-files-found: ignore + retention-days: 7 + - name: Stop background services + if: always() + run: | + for pid in "${{ steps.bootstrap_edge_server.outputs.pid }}" "${{ steps.start_cloudflare_workers.outputs.pid }}"; do + if [ -n "${pid}" ] && kill -0 "${pid}" 2>/dev/null; then + kill "${pid}" 2>/dev/null || true + fi + done test_playwright: runs-on: ubuntu-latest From 1d5f074a18b9e5191e59dcb8444e336bfe5a8932 Mon Sep 17 00:00:00 2001 From: Martin Donadieu Date: Tue, 28 Apr 2026 11:52:49 +0200 Subject: [PATCH 2/5] fix(ci): send workflow error markers to stderr --- .github/scripts/start-background-service.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/scripts/start-background-service.sh b/.github/scripts/start-background-service.sh index 052959f1c0..e3be145726 100644 --- a/.github/scripts/start-background-service.sh +++ b/.github/scripts/start-background-service.sh @@ -13,7 +13,7 @@ tail_lines="${BACKGROUND_TAIL_LINES:-200}" wait_on_version="${BACKGROUND_WAIT_ON_VERSION:-8.0.1}" if [ -z "${service_name}" ] || [ -z "${run_command}" ] || [ -z "${wait_on_resources_raw}" ] || [ -z "${log_path}" ]; then - echo "::error::BACKGROUND_SERVICE_NAME, BACKGROUND_RUN_COMMAND, BACKGROUND_WAIT_ON, and BACKGROUND_LOG_PATH are required." + printf '%s\n' "::error::BACKGROUND_SERVICE_NAME, BACKGROUND_RUN_COMMAND, BACKGROUND_WAIT_ON, and BACKGROUND_LOG_PATH are required." >&2 exit 1 fi @@ -28,7 +28,7 @@ while IFS= read -r resource; do done < <(printf '%s\n' "${wait_on_resources_raw}" | sed '/^[[:space:]]*$/d') if [ "${#wait_on_resources[@]}" -eq 0 ]; then - echo "::error::${service_name} is missing wait-on resources." + printf '%s\n' "::error::${service_name} is missing wait-on resources." >&2 exit 1 fi @@ -65,13 +65,13 @@ fi sleep 1 if ! kill -0 "${pid}" 2>/dev/null; then - echo "::error::${service_name} exited before it became ready." + printf '%s\n' "::error::${service_name} exited before it became ready." >&2 dump_log_tail exit 1 fi if ! bunx "wait-on@${wait_on_version}" "${wait_on_resources[@]}" --timeout "${wait_timeout_ms}" --interval "${wait_interval_ms}" --log --verbose; then - echo "::error::${service_name} failed to become ready." + printf '%s\n' "::error::${service_name} failed to become ready." >&2 dump_log_tail exit 1 fi From d191d322bf67d97ac516e838468191080ad33772 Mon Sep 17 00:00:00 2001 From: Martin Donadieu Date: Tue, 28 Apr 2026 17:02:58 +0200 Subject: [PATCH 3/5] fix(ci): expose playwright service startup logs --- .github/workflows/tests.yml | 78 ++++++++++++++++++++++++++++++++++++- playwright.config.ts | 27 ++++++++----- 2 files changed, 95 insertions(+), 10 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7a68a3663d..0575a7a45c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -176,8 +176,76 @@ jobs: version: 2.84.2 - name: Link Supabase templates run: ln -sfn supabase/templates templates + - id: start_playwright_stripe_emulator + name: Start Stripe emulator for Playwright + env: + BACKGROUND_SERVICE_NAME: Stripe emulator + BACKGROUND_RUN_COMMAND: STRIPE_EMULATOR_PORT=4510 bun run stripe:emulator + BACKGROUND_WAIT_ON: | + tcp:127.0.0.1:4510 + BACKGROUND_LOG_PATH: ${{ runner.temp }}/playwright-stripe-emulator.log + BACKGROUND_WAIT_TIMEOUT_MS: 60000 + BACKGROUND_TAIL_LINES: 300 + run: bash .github/scripts/start-background-service.sh + - id: start_playwright_backend + name: Start Playwright backend + env: + BACKGROUND_SERVICE_NAME: Playwright backend + BACKGROUND_RUN_COMMAND: | + rm -f .context/playwright/gha-backend.ready + PLAYWRIGHT_READY_FILE=.context/playwright/gha-backend.ready \ + ENV=local \ + STRIPE_SECRET_KEY=sk_test_emulator \ + STRIPE_API_BASE_URL=http://host.docker.internal:4510 \ + STRIPE_WEBHOOK_SECRET=testsecret \ + WEBAPP_URL=http://localhost:5173 \ + bun run backend:playwright + BACKGROUND_WAIT_ON: | + file:${{ github.workspace }}/.context/playwright/gha-backend.ready + BACKGROUND_LOG_PATH: ${{ runner.temp }}/playwright-backend.log + BACKGROUND_WAIT_TIMEOUT_MS: 360000 + BACKGROUND_TAIL_LINES: 400 + run: bash .github/scripts/start-background-service.sh + - id: start_playwright_frontend + name: Start Playwright frontend + env: + BACKGROUND_SERVICE_NAME: Playwright frontend + BACKGROUND_RUN_COMMAND: CAPTCHA_KEY='' bun run serve:worktree + BACKGROUND_WAIT_ON: | + http-get://127.0.0.1:5173 + BACKGROUND_LOG_PATH: ${{ runner.temp }}/playwright-frontend.log + BACKGROUND_WAIT_TIMEOUT_MS: 360000 + BACKGROUND_TAIL_LINES: 300 + run: bash .github/scripts/start-background-service.sh - name: Run Playwright tests - run: bun run test:front + env: + SKIP_STRIPE_EMULATOR_START: "true" + SKIP_BACKEND_START: "true" + SKIP_FRONTEND_START: "true" + run: bunx playwright test + - name: Show Playwright service logs after failure + if: failure() + run: | + echo "::group::Stripe emulator log" + tail -n 200 "${{ steps.start_playwright_stripe_emulator.outputs.log_path }}" || true + echo "::endgroup::" + echo "::group::Playwright backend log" + tail -n 300 "${{ steps.start_playwright_backend.outputs.log_path }}" || true + echo "::endgroup::" + echo "::group::Playwright frontend log" + tail -n 200 "${{ steps.start_playwright_frontend.outputs.log_path }}" || true + echo "::endgroup::" + - name: Upload Playwright background service logs + if: failure() + uses: actions/upload-artifact@v6 + with: + name: playwright-background-service-logs + path: | + ${{ steps.start_playwright_stripe_emulator.outputs.log_path }} + ${{ steps.start_playwright_backend.outputs.log_path }} + ${{ steps.start_playwright_frontend.outputs.log_path }} + if-no-files-found: ignore + retention-days: 7 - name: Upload Playwright artifacts if: failure() uses: actions/upload-artifact@v6 @@ -188,3 +256,11 @@ jobs: test-results/ if-no-files-found: ignore retention-days: 7 + - name: Stop Playwright background services + if: always() + run: | + for pid in "${{ steps.start_playwright_frontend.outputs.pid }}" "${{ steps.start_playwright_backend.outputs.pid }}" "${{ steps.start_playwright_stripe_emulator.outputs.pid }}"; do + if [ -n "${pid}" ] && kill -0 "${pid}" 2>/dev/null; then + kill "${pid}" 2>/dev/null || true + fi + done diff --git a/playwright.config.ts b/playwright.config.ts index fd7f1607dd..8c4bfcd281 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -1,7 +1,10 @@ import type { PlaywrightTestConfig } from '@playwright/test' import { env } from 'node:process' import { defineConfig, devices } from '@playwright/test' -import { getPlaywrightStripeApiBaseUrl, getStripeEmulatorPort } from './scripts/playwright-stripe' +import { + getPlaywrightStripeApiBaseUrl, + getStripeEmulatorPort, +} from './scripts/playwright-stripe' import { getSupabaseWorktreeConfig } from './scripts/supabase-worktree-config' /** @@ -21,7 +24,8 @@ const webServer: PlaywrightTestConfig['webServer'] = [] const { ports: supabasePorts } = getSupabaseWorktreeConfig() const localSupabaseUrl = `http://127.0.0.1:${supabasePorts.api}` const localApiDomain = `127.0.0.1:${supabasePorts.api}/functions/v1` -const localSupabaseAnonKey = env.SUPABASE_ANON_KEY || 'sb_publishable_ACJWlzQHlZjBrEguHvfOxg_3BJgxAaH' +const localSupabaseAnonKey + = env.SUPABASE_ANON_KEY || 'sb_publishable_ACJWlzQHlZjBrEguHvfOxg_3BJgxAaH' const localStripeEmulatorPort = getStripeEmulatorPort(env) const localStripeApiBaseUrl = getPlaywrightStripeApiBaseUrl(env) @@ -50,13 +54,18 @@ else { console.log('Skipping backend server') } -webServer.push({ - command: `ENV=local SUPA_URL=${localSupabaseUrl} SUPA_ANON=${localSupabaseAnonKey} API_DOMAIN=${localApiDomain} CAPTCHA_KEY='' bun run serve:local`, - port: 5173, - timeout: webServerTimeout, - reuseExistingServer, - stdout: 'pipe', -}) +if (!env.SKIP_FRONTEND_START) { + webServer.push({ + command: `ENV=local SUPA_URL=${localSupabaseUrl} SUPA_ANON=${localSupabaseAnonKey} API_DOMAIN=${localApiDomain} CAPTCHA_KEY='' bun run serve:local`, + port: 5173, + timeout: webServerTimeout, + reuseExistingServer, + stdout: 'pipe', + }) +} +else { + console.log('Skipping frontend server') +} /** * See https://playwright.dev/docs/test-configuration. From 5d0a374bd87c10138176100298358addb34b0f36 Mon Sep 17 00:00:00 2001 From: Martin Donadieu Date: Tue, 28 Apr 2026 17:15:35 +0200 Subject: [PATCH 4/5] fix(ci): wait for playwright frontend on localhost --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0575a7a45c..4267be5598 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -212,7 +212,7 @@ jobs: BACKGROUND_SERVICE_NAME: Playwright frontend BACKGROUND_RUN_COMMAND: CAPTCHA_KEY='' bun run serve:worktree BACKGROUND_WAIT_ON: | - http-get://127.0.0.1:5173 + http-get://localhost:5173 BACKGROUND_LOG_PATH: ${{ runner.temp }}/playwright-frontend.log BACKGROUND_WAIT_TIMEOUT_MS: 360000 BACKGROUND_TAIL_LINES: 300 From 6e46229da2a7314f7d102bded171e389a19b26d6 Mon Sep 17 00:00:00 2001 From: Martin Donadieu Date: Tue, 28 Apr 2026 17:44:00 +0200 Subject: [PATCH 5/5] fix(ci): resolve sonarcloud findings --- .github/scripts/start-background-service.sh | 10 +++++----- playwright.config.ts | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/scripts/start-background-service.sh b/.github/scripts/start-background-service.sh index e3be145726..5566350ad9 100644 --- a/.github/scripts/start-background-service.sh +++ b/.github/scripts/start-background-service.sh @@ -12,7 +12,7 @@ wait_interval_ms="${BACKGROUND_WAIT_INTERVAL_MS:-500}" tail_lines="${BACKGROUND_TAIL_LINES:-200}" wait_on_version="${BACKGROUND_WAIT_ON_VERSION:-8.0.1}" -if [ -z "${service_name}" ] || [ -z "${run_command}" ] || [ -z "${wait_on_resources_raw}" ] || [ -z "${log_path}" ]; then +if [[ -z "${service_name}" || -z "${run_command}" || -z "${wait_on_resources_raw}" || -z "${log_path}" ]]; then printf '%s\n' "::error::BACKGROUND_SERVICE_NAME, BACKGROUND_RUN_COMMAND, BACKGROUND_WAIT_ON, and BACKGROUND_LOG_PATH are required." >&2 exit 1 fi @@ -22,18 +22,18 @@ mkdir -p "$(dirname "${log_path}")" wait_on_resources=() while IFS= read -r resource; do - if [ -n "${resource}" ]; then + if [[ -n "${resource}" ]]; then wait_on_resources+=("${resource}") fi done < <(printf '%s\n' "${wait_on_resources_raw}" | sed '/^[[:space:]]*$/d') -if [ "${#wait_on_resources[@]}" -eq 0 ]; then +if [[ "${#wait_on_resources[@]}" -eq 0 ]]; then printf '%s\n' "::error::${service_name} is missing wait-on resources." >&2 exit 1 fi dump_log_tail() { - if [ ! -f "${log_path}" ]; then + if [[ ! -f "${log_path}" ]]; then echo "No log file found at ${log_path}" return fi @@ -56,7 +56,7 @@ pid=$! disown "${pid}" 2>/dev/null || true popd >/dev/null -if [ -n "${GITHUB_OUTPUT:-}" ]; then +if [[ -n "${GITHUB_OUTPUT:-}" ]]; then { echo "pid=${pid}" echo "log_path=${log_path}" diff --git a/playwright.config.ts b/playwright.config.ts index 8c4bfcd281..fff4f1f299 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -54,7 +54,10 @@ else { console.log('Skipping backend server') } -if (!env.SKIP_FRONTEND_START) { +if (env.SKIP_FRONTEND_START) { + console.log('Skipping frontend server') +} +else { webServer.push({ command: `ENV=local SUPA_URL=${localSupabaseUrl} SUPA_ANON=${localSupabaseAnonKey} API_DOMAIN=${localApiDomain} CAPTCHA_KEY='' bun run serve:local`, port: 5173, @@ -63,9 +66,6 @@ if (!env.SKIP_FRONTEND_START) { stdout: 'pipe', }) } -else { - console.log('Skipping frontend server') -} /** * See https://playwright.dev/docs/test-configuration.