chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
# The check runner as a single container. Build from the repository root:
#
# docker build -f packages/git-ci/Dockerfile -t pdsjs-git-ci .
#
# Built from workspace source rather than the published packages, so the
# image matches the commit it was built from.
#
# The steps a workflow names run in this container, as this container's
# user, with this container's environment. That is the isolation boundary:
# watch only repositories whose contributors you trust.
FROM node:22-trixie-slim AS builder
# corepack resolves the pnpm version from package.json's packageManager
# field, and prompts before downloading it, which would hang this build.
ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0
RUN corepack enable
WORKDIR /app
# @pdsjs/git-ci reaches only @pdsjs/core, @pdsjs/git and pako, none of which
# compile anything. The workspace root carries better-sqlite3, whose install
# script compiles; --ignore-scripts skips it, and the daemon never loads it,
# so the build needs no toolchain.
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
COPY packages packages
RUN pnpm install --frozen-lockfile --prod --ignore-scripts \
--filter @pdsjs/git-ci...
# trixie (glibc 2.41), not bookworm (2.36): a workflow that installs the
# repository under test loads its native addons here, and better-sqlite3's
# prebuilt binary requires glibc >= 2.38.
FROM node:22-trixie-slim AS runtime
WORKDIR /app
# git clones the repository under test from the PDS over https. The
# toolchain is for the repository's own dependencies: a package with a
# native addon and no prebuild for this platform compiles on install, and a
# workflow that cannot install cannot run.
# docker-cli and docker-buildx are the client alone. A workflow that builds an
# image talks to the daemon on the host through the socket the deployment
# mounts; nothing runs a daemon in here. buildx is what serves a Dockerfile
# using BuildKit syntax, which the classic builder rejects.
#
# Three installs rather than one. Each RUN is one image layer, a registry
# takes one request per layer, and a Cloudflare Worker refuses a request
# over 100 MB on the lowest plan. The whole toolchain in one layer is 155 MB
# compressed; split this way the largest layer stays near 80 MB. g++ is the
# bulk, so it stands alone.
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
git ca-certificates gosu python3 make \
&& rm -rf /var/lib/apt/lists/*
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
g++ \
&& rm -rf /var/lib/apt/lists/*
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
docker-cli docker-buildx \
&& rm -rf /var/lib/apt/lists/*
# Workflows that install dependencies reach for the package manager their
# lockfile names, so corepack resolves it from the repository under test.
ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0
RUN corepack enable
# The remote helper, for a repository smart HTTP cannot serve. `git clone
# atproto://...` finds it here by name.
RUN printf '#!/bin/sh\nexec node /app/packages/git/src/cli.js "$@"\n' \
> /usr/local/bin/git-remote-atproto \
&& chmod 755 /usr/local/bin/git-remote-atproto
# Runs unprivileged. The node image ships a "node" user for exactly this.
# The entrypoint starts as root only to take the state mount, then drops.
RUN mkdir -p /data && chown node:node /data
COPY --from=builder --chown=node:node /app /app
COPY --chmod=755 packages/git-ci/docker-entrypoint.sh /usr/local/bin/
ENV NODE_ENV=production
# The cursor and the ref map, which decide what replays after a restart.
# Mount a volume at /data to keep them. There is no VOLUME instruction: some
# hosts reject one, and `docker run -v` needs no declaration here. Without a
# mount, or with one the daemon cannot write, it still runs; a restart then
# replays from the live head.
ENV PDSJS_CI_STATE=/data/state.json
# PDSJS_CI_IDENTIFIER, PDSJS_CI_PASSWORD and PDSJS_CI_REPOS are deliberately
# unset. The CLI prints its usage and exits rather than inventing them.
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["node", "packages/git-ci/src/cli.js", "watch"]