diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b5774b6..1ae0d44 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -323,6 +323,11 @@ jobs: # ${PGADMIN_PASSWORD:?} check fires and aborts the compose call. # Provide a dummy value so parsing succeeds — pgadmin is never started. PGADMIN_PASSWORD: ci-unused + # Tell test-server.mjs not to spin up its own postgres-test container + # — the e2epg job service is already running and reachable. Without + # this, test-server tries to publish 5432 on the QNAP host, which + # collides with Gitea's core postgres. + PLAYWRIGHT_USE_EXTERNAL_DB: "true" NEXTAUTH_URL: ${{ env.CI_AUTH_URL }} AUTH_URL: ${{ env.CI_AUTH_URL }} NEXTAUTH_SECRET: ${{ env.CI_AUTH_SECRET }} @@ -491,9 +496,13 @@ jobs: node-version: "20" - name: Install Playwright and Chromium + # Install locally (not -g) so the CJS require() in playwright.ci.config.ts + # resolves @playwright/test by walking up from apps/web/. A global + # install puts it in /opt/hostedtoolcache/.../lib/node_modules which + # Node's resolver doesn't check. run: | - npm install -g @playwright/test@1.49 - playwright install chromium --with-deps + npm install --no-save --no-package-lock @playwright/test@1.49 + npx playwright install chromium --with-deps - name: Run smoke tests env: diff --git a/apps/web/e2e/test-server.mjs b/apps/web/e2e/test-server.mjs index 76a225e..d4114bc 100644 --- a/apps/web/e2e/test-server.mjs +++ b/apps/web/e2e/test-server.mjs @@ -334,9 +334,18 @@ if (!playwrightDatabaseUrl) { throw new Error("PLAYWRIGHT_DATABASE_URL or DATABASE_URL_TEST must be configured for E2E runs."); } -const requestedTestDbPort = Number(new URL(playwrightDatabaseUrl).port || "5434"); -const selectedTestDbPort = await selectAvailablePort(requestedTestDbPort); -playwrightDatabaseUrl = replaceDatabasePort(playwrightDatabaseUrl, selectedTestDbPort); +// CI mode: use an externally-provided postgres (e.g. a GitHub Actions service +// container) instead of spinning up our own compose-managed postgres-test. +// In that mode we trust PLAYWRIGHT_DATABASE_URL as-is — no port rebinding, +// no compose up. +const useExternalDb = process.env.PLAYWRIGHT_USE_EXTERNAL_DB === "true"; + +let selectedTestDbPort; +if (!useExternalDb) { + const requestedTestDbPort = Number(new URL(playwrightDatabaseUrl).port || "5434"); + selectedTestDbPort = await selectAvailablePort(requestedTestDbPort); + playwrightDatabaseUrl = replaceDatabasePort(playwrightDatabaseUrl, selectedTestDbPort); +} const playwrightDatabaseName = parseDatabaseName(playwrightDatabaseUrl); @@ -348,7 +357,9 @@ if (!/(^|_)(test|e2e|ci)$/u.test(playwrightDatabaseName)) { process.env.DATABASE_URL = playwrightDatabaseUrl; process.env.PLAYWRIGHT_DATABASE_URL = playwrightDatabaseUrl; -process.env.POSTGRES_TEST_PORT = String(selectedTestDbPort); +if (selectedTestDbPort !== undefined) { + process.env.POSTGRES_TEST_PORT = String(selectedTestDbPort); +} process.env.CAPAKRAKEN_EXPECTED_DB_NAME = playwrightDatabaseName; process.env.ALLOW_DESTRUCTIVE_DB_TOOLS = "true"; process.env.CONFIRM_DESTRUCTIVE_DB_NAME = playwrightDatabaseName; @@ -378,8 +389,10 @@ writeManagedWebEnv(rootEnv); process.on("exit", restoreWebEnvOnce); try { - await cleanupStaleE2eArtifacts(); - await ensureE2eDatabaseContainer(); + if (!useExternalDb) { + await cleanupStaleE2eArtifacts(); + await ensureE2eDatabaseContainer(); + } await run("pnpm", ["--filter", "@capakraken/db", "db:push"], workspaceRoot); await run("pnpm", ["--filter", "@capakraken/db", "db:seed"], workspaceRoot); await run("pnpm", ["--filter", "@capakraken/db", "db:seed:holidays"], workspaceRoot);