76879b27ec
* docker testing rewrite: bake the image, drop the allowlist, kill the quoting - new `pnpm gha <script>` wrapper. one entry point for running any node script in the GHA-like container; replaces the runtime apt-get + useradd + chown ceremony in `action/utils/docker.ts`. - `action/Dockerfile` bakes ubuntu:24.04 + node 24 + gh + jq + sudo + testuser at uid 1000. `action/docker-entrypoint.sh` remaps to the host uid/gid and `exec`s the requested command — no `bash -c` nesting, no `escapeForDoubleQuotes`. - env passthrough: full `process.env` (+ `.env` via dotenv) flows through `--env-file`, multi-line values via `-e` fallback. drops `EnvFilterMode` / `testEnvAllowList`. - image rebuild is content-hash gated on Dockerfile + entrypoint; volume is versioned by hash so a stale `node_modules` cache from an old image can't poison a new one. - `action/play.ts` slimmed to a CLI; `run()` extracted to `action/utils/runFixture.ts`. drops the `--local` / `PLAY_LOCAL` dual mode in favor of explicit `play:local` / `runtest:local` scripts. - `action/test/run.ts` no longer self-relaunches into docker — that's `gha`'s job now. - `action/test/coverage.ts` `ALWAYS_RUN_ALL` updated to track the new files. - `wiki/docker.md` rewritten (243 → 105 lines). `wiki/action-tests.md`, `wiki/billing.md`, `wiki/adversarial.md`, `README.md`, `AGENTS.md` all updated to drop `--local` / `PLAY_LOCAL` references. verified end-to-end: `pnpm play` runs the default fixture against pullfrog/scratch, exit 0; `sudo unshare --pid` still works inside the container; `pnpm runtest` boots through the wrapper. * gha: address review feedback + 3 related issues found locally review-flagged: - bare `pnpm gha --build` now builds the image and exits 0 (was printing help and exiting 1 — docs claimed it was a valid standalone) - `initVolumeOwnership` skipped when the named volume already exists; saves the ~240ms `docker run … chown` on every warm invocation - `GIT_SSH_COMMAND` gate widened to any `id_*` private key (was hard- coded to `id_rsa`, leaving ed25519-only linux contributors with the default ssh config). dropped `-i` so ssh picks whichever key exists - new `action/.dockerignore` — partial mitigation noted: BuildKit (default since docker 23) only sends files referenced by the Dockerfile (~42B in practice), so the perf concern is mostly hypothetical. file is still worth keeping for `DOCKER_BUILDKIT=0` fallback and as documented intent for future `COPY . .` additions related issues found while validating locally: - `parseArgs` now stops flag-parsing at the first positional (or literal `--`); `pnpm gha test/run.ts --build` previously intercepted `--build` as a gha flag instead of forwarding to `test/run.ts` - new `pnpm gha --clean` command prunes orphan `pullfrog-gha:*` images and `pullfrog-gha-node-modules-*` volumes whose hash doesn't match the current Dockerfile (each Dockerfile/entrypoint edit creates a fresh hash and orphans the prior pair, ~600MB + ~200MB each — without a cleaner they accumulate silently) - `--shell` without a TTY now fails fast with an actionable message before docker is invoked, instead of producing the confusing `the input device is not a TTY` from docker run wiki updated: documents `--clean`, the parseArgs passthrough rule, and a new "Reclaiming disk" section. * gha: fidelity, flexibility, and signal-safety improvements investigated local fidelity vs the real GHA ubuntu-24.04 runner and addressed the gaps that have actually bitten contributors or could. fidelity (image now matches GHA closer): - bake build-essential, wget, xz-utils, file alongside the existing toolset. gh, jq, git, python3, sudo, ssh, build-essential, wget, xz, file, unzip, curl all present. native module builds (node-gyp, any package missing arm64 prebuilts) now work; common agent shell calls don't hit ENOENT - `host.docker.internal:host-gateway` flag wires the host into the container's DNS on linux (macOS Docker Desktop bakes it in). lets scripts that hit a local dev server use `API_URL=http://host.docker. internal:3100` and work identically on both platforms - `--init` makes tini PID 1, fixing signal forwarding during the pre-exec warmup window (Ctrl-C was previously taking up to 10s to tear down because bash-as-PID-1 swallowed the signal) - pnpm version is correctly pinned via the workspace's `packageManager` field — corepack resolves it at install time; verified via the new `--doctor` command flexibility (new affordances): - `pnpm gha --doctor` runs an inside-the-container fidelity audit: os + arch + node/pnpm/python versions, version snapshots of every baked tool, env vars (CI, HOME, TMPDIR), uid/gid, and the host.docker.internal resolution. useful for "works in CI fails locally" or vice versa - `pnpm gha --build --no-cache` busts the docker layer cache when an apt mirror, base image, or external download has changed upstream - entrypoint's `pnpm install` warmup is now wrapped in a `flock` on a file in the shared node_modules volume — concurrent `pnpm gha` invocations (e.g. play in one terminal, runtest in another) serialize their install instead of racing docs: - new "Gaps (known)" section in wiki/docker.md explicitly calling out the things this system can't do yet, including the missing `uses: ./action` semantics gap that `.github/workflows/action-gha-e2e-adhoc.yml` currently fills via GHA only (designing a local `pnpm gha-action <fixture>` is on the roadmap), service containers, parallel-run sharing, and arch differences (arm64 vs amd64) * docs: audit + corrections after testing fronts self-audit pass for stale references and incomplete pointers: - wiki/browser.md: `Docker (node:24)` → `pnpm gha container (ubuntu:24.04)`. the substance was right (chrome not preinstalled) but the base image reference was stale. - wiki/docker.md: the "Permission errors" troubleshooting line claimed the node_modules volume is chowned on every run; now correctly says "owned by the host uid on first creation; warm runs skip the chown" to match the actual behavior after the initVolumeOwnership fix. - wiki/action-tests.md: `API_URL` env-var doc now mentions BOTH paths (`localhost:` from play:local, `host.docker.internal:` from inside the container). Proxy/router recipe now shows both invocations side-by-side instead of saying "must use play:local". - wiki/billing.md: same dual-recipe update for the loop-including-the- action proxy walkthrough. - gha.ts header: expanded the usage block to include --clean / --doctor / --no-cache / --shell-TTY, added the host.docker.internal note, and pointed at wiki/docker.md for design rationale. self-document check: a future agent landing on this code can answer "how do I run a fixture / debug in shell / add a tool / diagnose fidelity / reach a local dev server" purely from gha.ts header + wiki/docker.md without spelunking through the entrypoint or git history.
79 lines
3.0 KiB
Docker
79 lines
3.0 KiB
Docker
# pullfrog GHA-like test container.
|
|
#
|
|
# baked once at image build time, used by `pnpm gha`. all runtime cost
|
|
# (apt-get, useradd, sudoers wiring) is paid here so each `gha` invocation
|
|
# is a single `docker run` with no in-container setup.
|
|
#
|
|
# rebuild is content-hash gated by gha.ts (Dockerfile + docker-entrypoint.sh).
|
|
# bump anything in this file or the entrypoint and the next `pnpm gha` rebuilds.
|
|
|
|
FROM ubuntu:24.04
|
|
|
|
ENV DEBIAN_FRONTEND=noninteractive
|
|
|
|
# core toolset matching what GHA `ubuntu-24.04` runners ship: gh, jq, git,
|
|
# python3, ssh client, plus the compression + build-essential surface that
|
|
# `pnpm install` / `node-gyp` / agent shell calls regularly need. keeps
|
|
# test-time invocations of these tools honest (no "works on the runner,
|
|
# breaks in the local container").
|
|
RUN apt-get update -qq \
|
|
&& apt-get install -qq -y --no-install-recommends \
|
|
build-essential \
|
|
ca-certificates \
|
|
curl \
|
|
file \
|
|
git \
|
|
gnupg \
|
|
jq \
|
|
openssh-client \
|
|
python3 \
|
|
sudo \
|
|
unzip \
|
|
wget \
|
|
xz-utils \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# node 24 from nodesource + corepack (provides pnpm without a global install).
|
|
RUN curl -fsSL https://deb.nodesource.com/setup_24.x | bash - \
|
|
&& apt-get install -y nodejs \
|
|
&& rm -rf /var/lib/apt/lists/* \
|
|
&& corepack enable
|
|
|
|
# gh cli (matches GHA pre-installed tooling).
|
|
RUN mkdir -p /etc/apt/keyrings \
|
|
&& curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
|
|
| gpg --dearmor -o /etc/apt/keyrings/githubcli-archive-keyring.gpg \
|
|
&& chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \
|
|
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
|
|
> /etc/apt/sources.list.d/github-cli.list \
|
|
&& apt-get update -qq \
|
|
&& apt-get install -qq -y gh \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# ubuntu:24.04 ships a default `ubuntu` user at uid 1000 — remove it so we
|
|
# can place `testuser` at 1000 (the typical macOS dev uid). the entrypoint
|
|
# remaps to the host uid/gid at runtime if they differ.
|
|
RUN userdel -r ubuntu 2>/dev/null || true \
|
|
&& groupadd -g 1000 testuser \
|
|
&& useradd -u 1000 -g 1000 -m -s /bin/bash testuser \
|
|
&& echo "testuser ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/testuser \
|
|
&& chmod 0440 /etc/sudoers.d/testuser
|
|
|
|
# layout matching the bind mount + named volume targets in gha.ts.
|
|
RUN mkdir -p /app/action /app/action/node_modules /tmp/home/.config /tmp/home/.cache \
|
|
&& chown -R testuser:testuser /app /tmp/home
|
|
|
|
# CI=true is critical: `shell.ts` PID-namespace sandbox keys off it. baking
|
|
# it ensures security tests can't pass vacuously because someone forgot the
|
|
# flag.
|
|
ENV HOME=/tmp/home \
|
|
TMPDIR=/tmp \
|
|
CI=true \
|
|
COREPACK_ENABLE_DOWNLOAD_PROMPT=0
|
|
|
|
COPY docker-entrypoint.sh /entrypoint.sh
|
|
RUN chmod +x /entrypoint.sh
|
|
|
|
WORKDIR /app/action
|
|
ENTRYPOINT ["/entrypoint.sh"]
|