# Lint phase: ESLint, prettier --check, and script/check-censored. The tools # are invoked directly rather than through `make lint` or `script/lint`, which # are themselves a docker build and would recurse into a daemon that does not # exist in a build step. # # node:22-slim (22.x LTS), 2026-02-24 FROM node@sha256:5373f1906319b3a1f291da5d102f4ce5c77ccbe29eb637f072b6c7b70443fc36 AS lint WORKDIR /app COPY script/ script/ COPY package.json yarn.lock ./ RUN script/bootstrap COPY . . RUN yarn run lint RUN script/check-censored # Test phase, same shape and for the same reason: the jest suite (its worker # cap is in package.json), rerun verbose on failure, then # script/test-verify-build. # # node:22-slim (22.x LTS), 2026-02-24 FROM node@sha256:5373f1906319b3a1f291da5d102f4ce5c77ccbe29eb637f072b6c7b70443fc36 AS test WORKDIR /app COPY script/ script/ COPY package.json yarn.lock ./ RUN script/bootstrap COPY . . RUN timeout 90 yarn run test || \ { echo "--- Rerunning with --verbose for details ---"; \ timeout 90 yarn run test:verbose; exit 1; } RUN script/test-verify-build # Development environment with the extension built, and the last stage: a # plain `docker build .` names no target and so builds this one. Nothing is # wanted from the two phases above; the copies are what make BuildKit build # them first, so this image cannot be produced unless lint and test passed. A # stage appended after this one would drop all three out of a plain build. # # node:22-slim (22.x LTS), 2026-02-24 FROM node@sha256:5373f1906319b3a1f291da5d102f4ce5c77ccbe29eb637f072b6c7b70443fc36 WORKDIR /app COPY --from=lint /app/package.json /dev/null COPY --from=test /app/package.json /dev/null # script/bootstrap installs all prerequisites, git included. Manifests are # copied first so that layer stays cached until dependencies change. COPY script/ script/ COPY package.json yarn.lock ./ RUN script/bootstrap # A tar-stream context keeps the sender's file owners, which git refuses. RUN git config --system --add safe.directory /app COPY . . # The VERSION build arg when one is given, otherwise # `git describe --tags --always` on the .git in the build context. With .git # present, a version that is still empty, dev or unknown fails the build: git # is missing or could not read the checkout, and build.js, which stamps the # extension with the commit it was built from, would stamp "unknown". ARG VERSION RUN VERSION="${VERSION:-$(git describe --tags --always)}"; \ if [ -e .git ]; then \ case "$VERSION" in ""|dev|unknown) \ echo "version is '$VERSION' although .git is present" >&2; \ exit 1 ;; \ esac; \ fi; \ make build # A LABEL cannot run git, so it carries the build argument alone; a plain # `docker build .` leaves it empty. LABEL org.opencontainers.image.version="${VERSION}"