Files
zkldi_Tachi/Dockerfile.dev
zk e363bd2532 docs: migrate from mkdocs to mdbook (#1558)
* docs: migrate from mkdocs to mdbook

- Rename old mkdocs docs/ to old-docs/ for reference
- Set up new docs/ with mdbook (book.toml + src/ tree)
- Mirror full nav structure from mkdocs.yml into SUMMARY.md
- Add Justfile-docs with docs-serve, docs-build, docs-check, docs-install recipes
- Import Justfile-docs from root Justfile
- Rewrite .github/workflows/docs.yml: build step uses taiki-e/install-action
  to install mdbook, split into separate build + deploy jobs, PR builds
  run the check step too

* ci(docs): pin actions to SHAs, install mdbook via release binary

* ci(docs): install mdbook from apt instead of curling a release binary

* dev: replace mkdocs python stack with mdbook in dev image

* ci(docs): apt only works on Debian; restore release binary install for Ubuntu CI

* docs: fix duplicate file entries in SUMMARY.md

* docs: remove docs-install recipe

* docs: remove site-url from book.toml to fix asset loading

* dev: install mdbook from upstream release binary, not Debian apt

The Debian package (0.4.x+ds) strips bundled font assets, leaving the
built site without fonts/fonts.css. Use the upstream tarball (same as CI)
so the theme is complete. Handles x86_64 and aarch64.

* docs: vendor mdbook tarballs in dev/mdbook/, install from there

Dockerfile.dev uses COPY + tar to install the right arch at build time.
CI extracts the x86_64 tarball directly from the checkout.
No network access required for either — and no stripped-fonts Debian package.

* fix: unwritten
2026-05-22 20:43:07 +01:00

141 lines
5.9 KiB
Docker

# syntax=docker/dockerfile:1.7
#
# Two stages live here:
#
# - `base`: minimal image used as the `container:` for every CI job. Just
# enough to run `bun install`, `bun run …`, `just`, `psql`, `rsync`, ssh.
# Published as `ghcr.io/zkldi/tachi-ci`.
# - `dev`: superset of `base` with all the devcontainer QoL (fish, neovim,
# gh, ripgrep, fd, bat, fzf, mdbook (upstream binary), minio-client, locales, sudo, the
# `tachi` user, ...). Published as `ghcr.io/zkldi/tachi-dev`.
#
# Always use `--no-install-recommends`. Debian's recommends-by-default pulls
# the full LLVM/Mesa/Vulkan/GCC chain in via various transitive deps and adds
# ~750MB of crap that nothing here actually needs.
# ---------------------------------------------------------------------------
# bun-src: just here so we can `COPY --from=` the binary into our stages.
#
# We deliberately track the same floating tag prod uses (see
# `docker/Dockerfile.server`, `FROM oven/bun:alpine`). The `debian` variant
# is used here because our base is `debian:13-slim` (glibc), not Alpine
# (musl). If/when prod pins a specific bun version, pin this to match.
# ---------------------------------------------------------------------------
FROM oven/bun:debian AS bun-src
# ---------------------------------------------------------------------------
# base: CI image
# ---------------------------------------------------------------------------
FROM debian:13-slim AS base
WORKDIR /tachi
# Add the official PostgreSQL APT repository so we can install client tools
# that match the server version (v18). The Debian-packaged postgresql-client
# only provides v17 on Debian 13 (trixie). `gnupg` is only needed to dearmor
# the keyring and is purged afterwards.
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
ca-certificates curl gnupg \
&& curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc \
| gpg --dearmor -o /etc/apt/trusted.gpg.d/pgdg.gpg \
&& echo "deb https://apt.postgresql.org/pub/repos/apt trixie-pgdg main" \
> /etc/apt/sources.list.d/pgdg.list \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
# essentials for running the workspace in CI
git jq unzip just \
# vitest spawns workers via node; bun runs vitest itself but the
# worker subprocesses are node. With --no-install-recommends this
# pulls libnode115 but skips npm + nodejs-doc.
nodejs \
# postgres client tools (psql, pg_dump, pg_restore, pg_isready, ...)
postgresql-client-18 \
# rsync + ssh client for the homepage deploy job
rsync openssh-client \
&& apt-get purge -y gnupg \
&& apt-get autoremove -y \
&& rm -rf /var/lib/apt/lists/*
# bun — copied from the official oven/bun image (see BUN_VERSION arg at top).
# `bunx` is a symlink to `bun` in the upstream image; we recreate it here.
COPY --from=bun-src /usr/local/bin/bun /usr/local/bin/bun
RUN ln -sf /usr/local/bin/bun /usr/local/bin/bunx
# expose the db CLI globally
RUN ln -sf /tachi/typescript/db-cli/src/index.ts /usr/local/bin/tachidb
# ---------------------------------------------------------------------------
# dev: devcontainer image
# ---------------------------------------------------------------------------
FROM base AS dev
# so apt doesn't complain about the lack of a dialog-like program
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
# shell + editor + admin
fish neovim sudo locales \
# search / files / network / parallel
ripgrep fd-find bat fzf parallel wget \
# github CLI for `gh auth login`-style flows
gh \
# MinIO CLI (binary is `minio-client`, not Midnight Commander's `mc`)
minio-client \
# `xdg-open` so the client vite plugin can launch a browser without
# crashing on missing binary (see typescript/client/vite.config.ts).
xdg-utils \
&& rm -rf /var/lib/apt/lists/*
ENV DEBIAN_FRONTEND=readline
# mdbook — installed from vendored tarballs in dev/mdbook/ rather than the
# Debian apt package, which strips bundled fonts (the +ds repackaging).
COPY dev/mdbook/ /tmp/mdbook/
RUN set -eu; \
case "$(uname -m)" in \
x86_64) tarball=mdbook-v0.5.3-x86_64-unknown-linux-gnu.tar.gz ;; \
aarch64) tarball=mdbook-v0.5.3-aarch64-unknown-linux-musl.tar.gz ;; \
*) echo "Unsupported arch: $(uname -m)" >&2; exit 1 ;; \
esac; \
tar -xz -C /usr/local/bin -f "/tmp/mdbook/${tarball}" \
&& rm -rf /tmp/mdbook
# `fd` is called `fdfind` on debian. Awesome.
RUN ln -s "$(which fdfind)" /usr/bin/fd
# setup locales
# https://stackoverflow.com/questions/28405902/how-to-set-the-locale-inside-a-debian-ubuntu-docker-container
RUN echo 'en_US.UTF-8 UTF-8' > /etc/locale.gen && locale-gen
ENV LANG=en_US.UTF-8 LANGUAGE=en_US:en LC_ALL=en_US.UTF-8
# https://github.com/python-babel/babel/issues/990
# it wouldn't be python without needing absurd global state manipulation to fix
# an incoherent error message
RUN rm -f /etc/localtime && ln -s /usr/share/zoneinfo/Etc/UTC /etc/localtime
# Fix locale issue perl repeatedly complains about
RUN echo "LC_ALL=en_US.UTF-8\nLANG=en_US.UTF-8" > /etc/default/locale
# create tachi user and give them sudo
RUN addgroup --gid 1000 tachi \
&& adduser --disabled-password --uid 1000 --gid 1000 --home /home/tachi tachi \
&& echo "tachi ALL=(ALL:ALL) NOPASSWD: ALL" >> /etc/sudoers
# Make the /tachi working directory owned by our user instead of root. Docker volumes
# are mounted as root UNLESS the folder already exists inside the host and has non-root
# ownership. This is the only way to declare a volume in docker has non-root ownership.
# Unbelievably obscure.
RUN chown -R tachi:tachi /tachi
USER tachi
# see above comment about non-root volumes
RUN mkdir node_modules
# keep container alive indefinitely
# Exclude .git from chown to avoid permission errors on macOS Docker mounts
# TODO(zk): What the fuck is this?
ENTRYPOINT ["sh", "-c", "sudo find /tachi -maxdepth 1 ! -name '.git' ! -name '/tachi' -exec chown -R tachi:tachi {} + 2>/dev/null || true; exec /bin/fish"]