wip: e2e in ci
Some checks failed
check / check (push) Successful in 53s
e2e / e2e-chrome (push) Failing after 10s
e2e / e2e-firefox (push) Failing after 12s

This commit is contained in:
2026-08-12 11:46:00 +00:00
parent d23d5df8d6
commit eb6ec80430
5 changed files with 126 additions and 36 deletions

43
.gitea/workflows/e2e.yml Normal file
View File

@@ -0,0 +1,43 @@
name: e2e
on: [push]
# The browser end-to-end suites, one job per browser, deliberately not part
# of the check workflow. REPO_POLICIES.md caps make test at 20 seconds and
# script/cibuild is a plain `docker build .` whose Dockerfile runs
# make check; folding a browser suite into either would break that cap and
# slow the local fast path. These jobs are the automatic run the suites
# never had: before this workflow, every browser-level guarantee in the
# repo held only when a human remembered to run it by hand.
#
# One job per browser rather than two steps in one job, so a Chrome failure
# does not hide the Firefox result.
#
# These jobs REPORT, they do not gate: nothing merges or refuses to merge on
# their result by itself. Gitea decides that in branch protection, which
# this repo does not set, so a failure here is a red mark the reviewer must
# account for rather than a hard block. Making e2e a required check is the
# right end state and is deliberately not done yet, because the Chrome
# suite is flaky today under load (see the tracker) and a gate that fails
# at random teaches people to merge past red.
#
# Nothing here is allowed to pass vacuously. There is no continue-on-error
# and no `|| true`: script/test-e2e and script/test-e2e-firefox both exit
# non-zero when docker is missing, when the extension build is absent, and
# when the browser fails to start, and the Chrome harness aborts the whole
# suite if its network interception is not in effect.
jobs:
e2e-chrome:
runs-on: ubuntu-latest
steps:
# actions/checkout v4.2.2, 2026-02-22
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- run: script/bootstrap
- run: make test-e2e
e2e-firefox:
runs-on: ubuntu-latest
steps:
# actions/checkout v4.2.2, 2026-02-22
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- run: script/bootstrap
- run: make test-e2e-firefox

View File

@@ -1,27 +0,0 @@
name: probe
on: [push]
jobs:
probe:
runs-on: ubuntu-latest
steps:
# actions/checkout v4.2.2, 2026-02-22
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- run: |
set -x
id
pwd
cat /etc/hostname
docker version || true
docker info --format '{{.Driver}} {{.ServerVersion}}' || true
echo "--- mountinfo ---"
cat /proc/self/mountinfo
echo "--- inspect self by hostname ---"
docker inspect "$(cat /etc/hostname)" --format '{{json .Mounts}}' || true
echo "--- bind mount probe ---"
docker run --rm -v "$PWD:/work" -w /work alpine:3 ls -la /work | head -20 || true
echo "--- named volume probe ---"
VOL=$(docker inspect "$(cat /etc/hostname)" --format '{{range .Mounts}}{{if eq .Destination "/workspace"}}{{.Name}}{{end}}{{end}}' || true)
echo "VOL=$VOL"
docker run --rm -v "$VOL:/workspace" -w "$PWD" alpine:3 ls -la . | head -20 || true
echo "--- shm in docker run default ---"
docker run --rm alpine:3 df -h /dev/shm || true

63
script/e2e-container Executable file
View File

@@ -0,0 +1,63 @@
#!/bin/sh
# script/e2e-container: run one command in a container with this repo at
# /work, and exit with that command's status. Our own extension to
# scripts-to-rule-them-all, used by script/test-e2e and
# script/test-e2e-firefox.
#
# It takes the arguments you would give `docker run`, minus the mount and
# the working directory:
#
# script/e2e-container --ipc=host -e HOME=/tmp "$IMAGE" node foo.js
#
# It exists because `docker run -v "$ROOT:/work"` cannot work under Gitea
# Actions. The runner executes the job inside a container and hands it the
# host's docker socket, so the source side of a -v is resolved by the host
# daemon and not inside the job. The job's checkout lives on a docker
# volume mounted at /workspace, which is not a host path at all: measured
# on this repo's runner, `docker run -v "$PWD:/work" ... ls -la /work`
# listed an empty directory. A suite started that way dies with "Cannot
# find module" rather than testing anything.
#
# So the repo is copied in rather than mounted. That is one mechanism for
# CI and for a laptop instead of two, which matters more than the copy:
# the path CI takes is the path a developer exercises on every local run.
# The copy costs about two seconds for this tree, against a suite that
# takes tens of seconds.
#
# Nothing is copied back out. Neither suite writes anything under the repo
# — the browser profile and every temp file live under HOME, which both
# callers point at /tmp inside the container.
set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
CID=""
cleanup() {
if [ -n "$CID" ]; then
docker rm -f "$CID" >/dev/null 2>&1 || true
fi
}
main() {
if [ "$#" -eq 0 ]; then
echo "e2e-container: usage: e2e-container <docker-run-arg>..." >&2
exit 2
fi
cd "$ROOT"
CID="$(docker create -w /work "$@")"
trap cleanup EXIT
trap 'cleanup; exit 130' INT TERM
# `docker cp <dir> <cid>:/work` with /work absent creates it and
# copies the contents of <dir> into it. A failure here is fatal under
# set -e, so the command below can never run against an empty /work.
docker cp . "$CID:/work"
# --attach propagates the container's exit status.
docker start --attach "$CID"
}
main "$@"

View File

@@ -6,10 +6,13 @@
# Deliberately NOT called by script/check or script/test: REPO_POLICIES.md
# caps make test at 20 seconds and a browser suite does not fit. Run it
# yourself before touching popup views; it is the only check that can see
# a used-but-not-imported identifier blow up at runtime.
# a used-but-not-imported identifier blow up at runtime. The e2e workflow
# in .gitea/workflows/e2e.yml runs it on every push, as a job separate
# from check so the 20-second cap stays intact.
set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)"
# mcr.microsoft.com/playwright:v1.56.0-noble, 2026-08-09
#
@@ -49,14 +52,16 @@ main() {
# claim in tests/e2e/network.js and README.md — not deleting the
# probe. The image is pinned by digest, so this can only ever bite
# on a deliberate bump.
docker run --rm \
#
# script/e2e-container is `docker run` with the repo placed at /work;
# it copies rather than mounts, because a bind mount does not resolve
# under Gitea Actions. See the comment at the top of that script.
"$SCRIPT_DIR/e2e-container" \
--ipc=host \
--user "$(id -u):$(id -g)" \
-e HOME=/tmp \
-e PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS=1 \
-e "E2E_TRACE_NETWORK=${E2E_TRACE_NETWORK:-0}" \
-v "$ROOT:/work" \
-w /work \
"$IMAGE" \
node tests/e2e/run.js
}

View File

@@ -5,7 +5,9 @@
#
# Deliberately NOT called by script/check or script/test, for the same
# reason as the Chrome suite: REPO_POLICIES.md caps make test at 20 seconds
# and a browser suite does not fit.
# and a browser suite does not fit. The e2e workflow in
# .gitea/workflows/e2e.yml runs it on every push, as a job separate from
# check so the 20-second cap stays intact.
#
# Unlike script/test-e2e this builds its image locally, because no
# published image carries both a pinned Firefox and a matching geckodriver.
@@ -49,13 +51,17 @@ main() {
# No --privileged. Firefox's sandbox logs
# "CanCreateUserNamespace() clone() failure: EPERM" on startup here;
# it is cosmetic and headless Firefox runs fine without it.
docker run --rm \
#
# script/e2e-container is `docker run` with the repo placed at /work;
# it copies rather than mounts, because a bind mount does not resolve
# under Gitea Actions. See the comment at the top of that script. The
# copy happens before the container starts, so --network none still
# covers everything the suite does.
"$SCRIPT_DIR/e2e-container" \
--shm-size=1g \
--network none \
--user "$(id -u):$(id -g)" \
-e HOME=/tmp \
-v "$ROOT:/work" \
-w /work \
"$IMAGE" \
node tests/e2e/firefox/run.js dist/firefox
}