From e8804922b567b87afe870a90222b82363749fe44 Mon Sep 17 00:00:00 2001 From: Steve Cliff Date: Fri, 8 May 2026 21:36:09 +0100 Subject: [PATCH] e2e: extract Playwright report via docker cp instead of bind mount When the runner job runs inside a container, compose's relative `./playwright/playwright-report` resolves to a path that exists only inside the runner container, so the host's docker daemon silently bind-mounts an empty dir and the report never lands anywhere we can read. Drop the bind mounts; keep the playwright container around (--name e2e-pw, no --rm); after the test, `docker cp` the report and traces out into the runner's workspace volume so upload-artifact has something real to upload. The new test-results directory (Playwright traces, screenshots, videos) is also included so failure post-mortem doesn't need a re-run. --- .gitea/workflows/e2e.yml | 27 +++++++++++++++++++-------- e2e/compose.e2e.yml | 13 +++++++++---- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/.gitea/workflows/e2e.yml b/.gitea/workflows/e2e.yml index 0cb1b49..f8e9e54 100644 --- a/.gitea/workflows/e2e.yml +++ b/.gitea/workflows/e2e.yml @@ -79,15 +79,22 @@ jobs: - name: Start the agent run: docker compose -f e2e/compose.e2e.yml up -d agent - - name: Prepare report mounts - run: | - mkdir -p e2e/playwright/playwright-report e2e/playwright/test-results - chmod -R a+rwX e2e/playwright/playwright-report e2e/playwright/test-results - - name: Run Playwright tests + id: playwright env: RM_BOOTSTRAP_TOKEN: ${{ env.RM_BOOTSTRAP_TOKEN }} - run: docker compose -f e2e/compose.e2e.yml run --rm playwright + # --name pins a stable container ID so the next step can + # docker cp out of it before tear-down. We deliberately + # drop --rm so the container survives the test exit; the + # tear-down step removes it. + run: docker compose -f e2e/compose.e2e.yml run --name e2e-pw playwright + + - name: Extract Playwright report + if: always() && steps.playwright.outcome != 'skipped' + run: | + mkdir -p e2e/playwright/playwright-report e2e/playwright/test-results + docker cp e2e-pw:/work/playwright-report/. e2e/playwright/playwright-report/ || true + docker cp e2e-pw:/work/test-results/. e2e/playwright/test-results/ || true - name: Compose logs (on failure) if: failure() @@ -101,9 +108,13 @@ jobs: uses: actions/upload-artifact@v3 with: name: playwright-report - path: e2e/playwright/playwright-report + path: | + e2e/playwright/playwright-report + e2e/playwright/test-results retention-days: 7 - name: Tear down if: always() - run: docker compose -f e2e/compose.e2e.yml down -v + run: | + docker rm -f e2e-pw 2>/dev/null || true + docker compose -f e2e/compose.e2e.yml down -v diff --git a/e2e/compose.e2e.yml b/e2e/compose.e2e.yml index 7b94d27..da3d710 100644 --- a/e2e/compose.e2e.yml +++ b/e2e/compose.e2e.yml @@ -64,10 +64,18 @@ services: networks: [rmnet] # Playwright test runner. Profile-gated so `compose up` doesn't - # start it; CI runs it via `compose run --rm playwright`. Lives on + # start it; CI invokes it via `compose run` and `docker cp`s the + # report+traces out (see .gitea/workflows/e2e.yml). Lives on # rmnet so it can reach the server via its compose-network DNS # name rather than depending on host port-publish (which doesn't # work on Gitea's container-based runners). + # + # Reports are NOT bind-mounted: when the runner job itself runs + # inside a container, `./playwright/...` resolves to a path that + # only exists inside the runner container, so the host docker + # daemon would silently mount an empty dir. Instead the report + # stays inside the playwright container and the workflow extracts + # it via `docker cp` before tearing down. playwright: profiles: [test] build: @@ -76,9 +84,6 @@ services: environment: RM_BASE_URL: "http://server:8080" RM_BOOTSTRAP_TOKEN: "${RM_BOOTSTRAP_TOKEN:-}" - volumes: - - ./playwright/playwright-report:/work/playwright-report - - ./playwright/test-results:/work/test-results depends_on: - server - agent