From d96a33f6db2f64d9adca768811e7be3421232f47 Mon Sep 17 00:00:00 2001 From: sneak Date: Thu, 5 Nov 2026 13:53:02 +0000 Subject: [PATCH 01/11] Add prettier (closes #1) Install prettier globally beside wrangler and wrangler's peers, and check it in the smoke test. prettier ships no distro package for Ubuntu, so npm global is the standard install. Model: deepseek-v4-pro --- README.md | 1 + bin/test | 1 + build.sh | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b729601..f9cd87f 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ Image: `git.eeqj.de/sneak/baseimage`, built for `linux/amd64` and | corepack | `npm install -g corepack` | nvm LTS global bin | | yarn, pnpm | corepack, cache pre-populated | `/usr/local/corepack` | | wrangler | `npm install -g wrangler` | nvm LTS global bin | +| prettier | `npm install -g prettier` | nvm LTS global bin | | Python (distro) | apt `python3`, `pip`, `venv` | `/usr/bin/python3` | | pyenv | git tag | `/usr/local/pyenv` | | Python (latest) | `pyenv install` | `/usr/local/pyenv/versions/*` | diff --git a/bin/test b/bin/test index a00d8c7..193b750 100755 --- a/bin/test +++ b/bin/test @@ -20,6 +20,7 @@ docker run --rm "$img" bash -ec ' go version; node --version; python3 --version; hugo version | head -1 COREPACK_ENABLE_NETWORK=0 yarn --version COREPACK_ENABLE_NETWORK=0 pnpm --version + prettier --version ' echo "== $img: runit service lifecycle" diff --git a/build.sh b/build.sh index 3825f33..989f16c 100755 --- a/build.sh +++ b/build.sh @@ -61,7 +61,7 @@ nvm install --lts nvm alias default "lts/*" nvm use default set -ux -npm install -g npm@latest corepack wrangler +npm install -g npm@latest corepack wrangler prettier corepack enable # Pre-populate the corepack cache so projects that pin a package manager # do not download at run time. The last yarn installed becomes the default. @@ -102,6 +102,7 @@ npm --version COREPACK_ENABLE_NETWORK=0 yarn --version COREPACK_ENABLE_NETWORK=0 pnpm --version wrangler --version +prettier --version python3 --version pip --version virtualenv --version -- 2.54.0 From 759616e8249461f3c4297b5dbc85f6a34da223fc Mon Sep 17 00:00:00 2001 From: sneak Date: Thu, 5 Nov 2026 13:53:21 +0000 Subject: [PATCH 02/11] Add shellcheck (closes #2) Install shellcheck from apt, which ships a current 0.11 release, and check it in the smoke test. Model: deepseek-v4-pro --- README.md | 1 + bin/test | 1 + build.sh | 4 +++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f9cd87f..6e54728 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ Image: `git.eeqj.de/sneak/baseimage`, built for `linux/amd64` and | Go (distro) | apt `golang-go` | `/usr/lib/go-*` | | Go (latest) | go.dev tarball | `/usr/local/go` | | golangci-lint | GitHub release | `/usr/local/bin/golangci-lint` | +| shellcheck | apt | `/usr/bin/shellcheck` | | Hugo (extended) | GitHub release `.deb` | `/usr/local/bin/hugo` | | Node (distro) | apt `nodejs`, `npm` | `/usr/bin/node` | | nvm | git tag | `/usr/local/nvm` | diff --git a/bin/test b/bin/test index 193b750..b9bb40c 100755 --- a/bin/test +++ b/bin/test @@ -21,6 +21,7 @@ docker run --rm "$img" bash -ec ' COREPACK_ENABLE_NETWORK=0 yarn --version COREPACK_ENABLE_NETWORK=0 pnpm --version prettier --version + shellcheck --version ' echo "== $img: runit service lifecycle" diff --git a/build.sh b/build.sh index 989f16c..ba86760 100755 --- a/build.sh +++ b/build.sh @@ -25,7 +25,8 @@ apt-get -y install --no-install-recommends \ python3 python3-dev python3-pip python3-venv virtualenv \ libbz2-dev libffi-dev libgdbm-dev liblzma-dev libncurses-dev \ libreadline-dev libsqlite3-dev libssl-dev libxml2-dev libxmlsec1-dev \ - tk-dev uuid-dev zlib1g-dev + tk-dev uuid-dev zlib1g-dev \ + shellcheck # --- Go (latest) ------------------------------------------------------------- gover="$(curl -fsSL 'https://go.dev/VERSION?m=text' | head -n1)" @@ -96,6 +97,7 @@ rm -rf /root/.cache /root/.npm /root/.config /root/go /tmp/* /var/tmp/* # --- sanity ------------------------------------------------------------------ go version golangci-lint version +shellcheck --version hugo version node --version npm --version -- 2.54.0 From 51175e6c821d256fa8d989ccffaed29b223a6551 Mon Sep 17 00:00:00 2001 From: sneak Date: Thu, 5 Nov 2026 13:53:53 +0000 Subject: [PATCH 03/11] Add govulncheck (closes #3) Install govulncheck with go install into /usr/local/bin, before the cleanup step, and check it in the smoke test. Model: deepseek-v4-pro --- README.md | 1 + bin/test | 1 + build.sh | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/README.md b/README.md index 6e54728..7819a43 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ Image: `git.eeqj.de/sneak/baseimage`, built for `linux/amd64` and | Go (latest) | go.dev tarball | `/usr/local/go` | | golangci-lint | GitHub release | `/usr/local/bin/golangci-lint` | | shellcheck | apt | `/usr/bin/shellcheck` | +| govulncheck | `go install` (latest) | `/usr/local/bin/govulncheck` | | Hugo (extended) | GitHub release `.deb` | `/usr/local/bin/hugo` | | Node (distro) | apt `nodejs`, `npm` | `/usr/bin/node` | | nvm | git tag | `/usr/local/nvm` | diff --git a/bin/test b/bin/test index b9bb40c..5dde42f 100755 --- a/bin/test +++ b/bin/test @@ -22,6 +22,7 @@ docker run --rm "$img" bash -ec ' COREPACK_ENABLE_NETWORK=0 pnpm --version prettier --version shellcheck --version + govulncheck -version ' echo "== $img: runit service lifecycle" diff --git a/build.sh b/build.sh index ba86760..0499192 100755 --- a/build.sh +++ b/build.sh @@ -47,6 +47,9 @@ gl="$(latest_tag golangci/golangci-lint)" curl -fsSL "https://github.com/golangci/golangci-lint/releases/download/${gl}/golangci-lint-${gl#v}-linux-${arch}.tar.gz" | tar -C /usr/local/bin -xz --strip-components=1 "golangci-lint-${gl#v}-linux-${arch}/golangci-lint" +# --- govulncheck (Go tool) --------------------------------------------------- +GOBIN=/usr/local/bin go install golang.org/x/vuln/cmd/govulncheck@latest + # --- Hugo (extended) --------------------------------------------------------- hv="$(latest_tag gohugoio/hugo)" curl -fsSL -o /tmp/hugo.deb \ @@ -96,6 +99,7 @@ rm -rf /root/.cache /root/.npm /root/.config /root/go /tmp/* /var/tmp/* # --- sanity ------------------------------------------------------------------ go version +govulncheck -version golangci-lint version shellcheck --version hugo version -- 2.54.0 From 4261fa690879e74bca87d37ed8c3fc34ed19fce2 Mon Sep 17 00:00:00 2001 From: sneak Date: Thu, 5 Nov 2026 13:54:08 +0000 Subject: [PATCH 04/11] Add tzdata (closes #4) Install tzdata from apt so the time zone database is present at run time, and check a zone lookup in the smoke test. Model: deepseek-v4-pro --- README.md | 1 + bin/test | 1 + build.sh | 4 +++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7819a43..d8e6ec6 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ Image: `git.eeqj.de/sneak/baseimage`, built for `linux/amd64` and | pyenv | git tag | `/usr/local/pyenv` | | Python (latest) | `pyenv install` | `/usr/local/pyenv/versions/*` | | virtualenv | apt, and pip in pyenv Python | `virtualenv` on `PATH` | +| tzdata | apt | `/usr/share/zoneinfo` | Plus the usual odds and ends: `git`, `curl`, `wget`, `jq`, `rsync`, `openssh-client`, `zip`/`unzip`, `xz-utils`, `bzip2`, and the `-dev` diff --git a/bin/test b/bin/test index 5dde42f..3ae4415 100755 --- a/bin/test +++ b/bin/test @@ -23,6 +23,7 @@ docker run --rm "$img" bash -ec ' prettier --version shellcheck --version govulncheck -version + test -e /usr/share/zoneinfo/America/New_York ' echo "== $img: runit service lifecycle" diff --git a/build.sh b/build.sh index 0499192..cc7afea 100755 --- a/build.sh +++ b/build.sh @@ -26,7 +26,8 @@ apt-get -y install --no-install-recommends \ libbz2-dev libffi-dev libgdbm-dev liblzma-dev libncurses-dev \ libreadline-dev libsqlite3-dev libssl-dev libxml2-dev libxmlsec1-dev \ tk-dev uuid-dev zlib1g-dev \ - shellcheck + shellcheck \ + tzdata # --- Go (latest) ------------------------------------------------------------- gover="$(curl -fsSL 'https://go.dev/VERSION?m=text' | head -n1)" @@ -102,6 +103,7 @@ go version govulncheck -version golangci-lint version shellcheck --version +test -e /usr/share/zoneinfo/America/New_York hugo version node --version npm --version -- 2.54.0 From dffdd952304d55645f50a64af2e35da576e17186 Mon Sep 17 00:00:00 2001 From: sneak Date: Thu, 5 Nov 2026 13:54:50 +0000 Subject: [PATCH 05/11] Add zstd and the sqlite3 CLI (closes #5) Install zstd and sqlite3 from apt and check both in the smoke test. Model: deepseek-v4-pro --- README.md | 2 ++ bin/test | 2 ++ build.sh | 5 ++++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d8e6ec6..1458a8a 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,8 @@ Image: `git.eeqj.de/sneak/baseimage`, built for `linux/amd64` and | Python (latest) | `pyenv install` | `/usr/local/pyenv/versions/*` | | virtualenv | apt, and pip in pyenv Python | `virtualenv` on `PATH` | | tzdata | apt | `/usr/share/zoneinfo` | +| zstd | apt | `/usr/bin/zstd` | +| sqlite3 (CLI) | apt | `/usr/bin/sqlite3` | Plus the usual odds and ends: `git`, `curl`, `wget`, `jq`, `rsync`, `openssh-client`, `zip`/`unzip`, `xz-utils`, `bzip2`, and the `-dev` diff --git a/bin/test b/bin/test index 3ae4415..9f69f9a 100755 --- a/bin/test +++ b/bin/test @@ -24,6 +24,8 @@ docker run --rm "$img" bash -ec ' shellcheck --version govulncheck -version test -e /usr/share/zoneinfo/America/New_York + zstd --version + sqlite3 --version ' echo "== $img: runit service lifecycle" diff --git a/build.sh b/build.sh index cc7afea..87965ca 100755 --- a/build.sh +++ b/build.sh @@ -27,7 +27,8 @@ apt-get -y install --no-install-recommends \ libreadline-dev libsqlite3-dev libssl-dev libxml2-dev libxmlsec1-dev \ tk-dev uuid-dev zlib1g-dev \ shellcheck \ - tzdata + tzdata \ + zstd sqlite3 # --- Go (latest) ------------------------------------------------------------- gover="$(curl -fsSL 'https://go.dev/VERSION?m=text' | head -n1)" @@ -104,6 +105,8 @@ govulncheck -version golangci-lint version shellcheck --version test -e /usr/share/zoneinfo/America/New_York +zstd --version +sqlite3 --version hugo version node --version npm --version -- 2.54.0 From e8db9b9ab81c0ec20a6dbe6ecad6b79a27572aa8 Mon Sep 17 00:00:00 2001 From: sneak Date: Thu, 5 Nov 2026 13:55:06 +0000 Subject: [PATCH 06/11] Add libvips and libheif with development headers (closes #6) Install libvips-dev, libvips-tools, libheif-dev and libheif-plugin-aomenc from apt. libvips-tools provides the vips CLI, which the smoke test uses to confirm libheif is linked as a loader. Model: deepseek-v4-pro --- README.md | 2 ++ bin/test | 2 ++ build.sh | 5 ++++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1458a8a..9c6fe78 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,8 @@ Image: `git.eeqj.de/sneak/baseimage`, built for `linux/amd64` and | tzdata | apt | `/usr/share/zoneinfo` | | zstd | apt | `/usr/bin/zstd` | | sqlite3 (CLI) | apt | `/usr/bin/sqlite3` | +| libvips | apt `libvips-dev`, `libvips-tools` | `vips` on `PATH` | +| libheif | apt `libheif-dev`, `libheif-plugin-aomenc` | HEIF/AVIF codec | Plus the usual odds and ends: `git`, `curl`, `wget`, `jq`, `rsync`, `openssh-client`, `zip`/`unzip`, `xz-utils`, `bzip2`, and the `-dev` diff --git a/bin/test b/bin/test index 9f69f9a..4dcbd0a 100755 --- a/bin/test +++ b/bin/test @@ -26,6 +26,8 @@ docker run --rm "$img" bash -ec ' test -e /usr/share/zoneinfo/America/New_York zstd --version sqlite3 --version + vips --version + vips -l | grep -i heif > /dev/null ' echo "== $img: runit service lifecycle" diff --git a/build.sh b/build.sh index 87965ca..70658c5 100755 --- a/build.sh +++ b/build.sh @@ -28,7 +28,8 @@ apt-get -y install --no-install-recommends \ tk-dev uuid-dev zlib1g-dev \ shellcheck \ tzdata \ - zstd sqlite3 + zstd sqlite3 \ + libvips-dev libvips-tools libheif-dev libheif-plugin-aomenc # --- Go (latest) ------------------------------------------------------------- gover="$(curl -fsSL 'https://go.dev/VERSION?m=text' | head -n1)" @@ -107,6 +108,8 @@ shellcheck --version test -e /usr/share/zoneinfo/America/New_York zstd --version sqlite3 --version +vips --version +vips -l | grep -i heif > /dev/null hugo version node --version npm --version -- 2.54.0 From 43ecbc9736892033a731092bdcf58dde62917f17 Mon Sep 17 00:00:00 2001 From: sneak Date: Thu, 5 Nov 2026 13:55:27 +0000 Subject: [PATCH 07/11] Add ImageMagick and Ghostscript (closes #7) Install imagemagick (ImageMagick 7, so the CLI is magick) and ghostscript from apt and check both in the smoke test. Model: deepseek-v4-pro --- README.md | 2 ++ bin/test | 2 ++ build.sh | 5 ++++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9c6fe78..febf1c6 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,8 @@ Image: `git.eeqj.de/sneak/baseimage`, built for `linux/amd64` and | sqlite3 (CLI) | apt | `/usr/bin/sqlite3` | | libvips | apt `libvips-dev`, `libvips-tools` | `vips` on `PATH` | | libheif | apt `libheif-dev`, `libheif-plugin-aomenc` | HEIF/AVIF codec | +| ImageMagick | apt | `magick` | +| Ghostscript | apt | `gs` | Plus the usual odds and ends: `git`, `curl`, `wget`, `jq`, `rsync`, `openssh-client`, `zip`/`unzip`, `xz-utils`, `bzip2`, and the `-dev` diff --git a/bin/test b/bin/test index 4dcbd0a..65c2bc4 100755 --- a/bin/test +++ b/bin/test @@ -28,6 +28,8 @@ docker run --rm "$img" bash -ec ' sqlite3 --version vips --version vips -l | grep -i heif > /dev/null + magick --version + gs --version ' echo "== $img: runit service lifecycle" diff --git a/build.sh b/build.sh index 70658c5..9a06b3e 100755 --- a/build.sh +++ b/build.sh @@ -29,7 +29,8 @@ apt-get -y install --no-install-recommends \ shellcheck \ tzdata \ zstd sqlite3 \ - libvips-dev libvips-tools libheif-dev libheif-plugin-aomenc + libvips-dev libvips-tools libheif-dev libheif-plugin-aomenc \ + imagemagick ghostscript # --- Go (latest) ------------------------------------------------------------- gover="$(curl -fsSL 'https://go.dev/VERSION?m=text' | head -n1)" @@ -110,6 +111,8 @@ zstd --version sqlite3 --version vips --version vips -l | grep -i heif > /dev/null +magick --version +gs --version hugo version node --version npm --version -- 2.54.0 From 21c6059b98f00f57def6efaf747e1837ded2bdac Mon Sep 17 00:00:00 2001 From: sneak Date: Thu, 5 Nov 2026 13:55:41 +0000 Subject: [PATCH 08/11] Add Ruby with Bundler (closes #8) Install ruby, ruby-dev and bundler from apt. ruby-dev keeps native gems buildable, matching what jekyllsitebuilder needs. Model: deepseek-v4-pro --- README.md | 2 ++ bin/test | 2 ++ build.sh | 5 ++++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index febf1c6..b99419a 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,8 @@ Image: `git.eeqj.de/sneak/baseimage`, built for `linux/amd64` and | libheif | apt `libheif-dev`, `libheif-plugin-aomenc` | HEIF/AVIF codec | | ImageMagick | apt | `magick` | | Ghostscript | apt | `gs` | +| Ruby | apt `ruby`, `ruby-dev` | `/usr/bin/ruby` | +| Bundler | apt `bundler` | `bundle` on `PATH` | Plus the usual odds and ends: `git`, `curl`, `wget`, `jq`, `rsync`, `openssh-client`, `zip`/`unzip`, `xz-utils`, `bzip2`, and the `-dev` diff --git a/bin/test b/bin/test index 65c2bc4..ba427b1 100755 --- a/bin/test +++ b/bin/test @@ -30,6 +30,8 @@ docker run --rm "$img" bash -ec ' vips -l | grep -i heif > /dev/null magick --version gs --version + ruby --version + bundle --version ' echo "== $img: runit service lifecycle" diff --git a/build.sh b/build.sh index 9a06b3e..bf369cb 100755 --- a/build.sh +++ b/build.sh @@ -30,7 +30,8 @@ apt-get -y install --no-install-recommends \ tzdata \ zstd sqlite3 \ libvips-dev libvips-tools libheif-dev libheif-plugin-aomenc \ - imagemagick ghostscript + imagemagick ghostscript \ + ruby ruby-dev bundler # --- Go (latest) ------------------------------------------------------------- gover="$(curl -fsSL 'https://go.dev/VERSION?m=text' | head -n1)" @@ -113,6 +114,8 @@ vips --version vips -l | grep -i heif > /dev/null magick --version gs --version +ruby --version +bundle --version hugo version node --version npm --version -- 2.54.0 From 69fc40651e14e74bf6e0aea17f8e7a55785a93c7 Mon Sep 17 00:00:00 2001 From: sneak Date: Thu, 5 Nov 2026 13:55:55 +0000 Subject: [PATCH 09/11] Add Valkey and PostgreSQL (closes #9) Install valkey-server, valkey-tools and postgresql from apt. Ubuntu 26.04 ships Valkey 9 and PostgreSQL 18, replacing the postgresql16 the audit pinned. Model: deepseek-v4-pro --- README.md | 2 ++ bin/test | 7 +++++++ build.sh | 24 ++++++++++++++++++++++-- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b99419a..3d6cfd7 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,8 @@ Image: `git.eeqj.de/sneak/baseimage`, built for `linux/amd64` and | Ghostscript | apt | `gs` | | Ruby | apt `ruby`, `ruby-dev` | `/usr/bin/ruby` | | Bundler | apt `bundler` | `bundle` on `PATH` | +| Valkey | apt `valkey-server`, `valkey-tools` | `valkey-server`, `valkey-cli` | +| PostgreSQL | apt `postgresql` | `psql`, `postgres`, `initdb`, `pg_ctl` on `PATH` | Plus the usual odds and ends: `git`, `curl`, `wget`, `jq`, `rsync`, `openssh-client`, `zip`/`unzip`, `xz-utils`, `bzip2`, and the `-dev` diff --git a/bin/test b/bin/test index ba427b1..34102a5 100755 --- a/bin/test +++ b/bin/test @@ -32,6 +32,13 @@ docker run --rm "$img" bash -ec ' gs --version ruby --version bundle --version + valkey-server --version + valkey-cli --version + valkey-server /etc/valkey/valkey.conf + ok=; for i in $(seq 1 50); do if valkey-cli ping 2>/dev/null | grep -q PONG; then ok=1; break; fi; sleep 0.1; done; [ -n "$ok" ] + valkey-cli shutdown nosave + psql --version + postgres --version ' echo "== $img: runit service lifecycle" diff --git a/build.sh b/build.sh index bf369cb..0a7cefe 100755 --- a/build.sh +++ b/build.sh @@ -31,7 +31,14 @@ apt-get -y install --no-install-recommends \ zstd sqlite3 \ libvips-dev libvips-tools libheif-dev libheif-plugin-aomenc \ imagemagick ghostscript \ - ruby ruby-dev bundler + ruby ruby-dev bundler \ + valkey-server valkey-tools postgresql + +# Link the PostgreSQL server binaries onto PATH. They live under a versioned +# directory (/usr/lib/postgresql/*/bin); the client psql is already on PATH. +for p in postgres initdb pg_ctl; do + ln -s /usr/lib/postgresql/*/bin/"$p" /usr/local/bin/"$p" +done # --- Go (latest) ------------------------------------------------------------- gover="$(curl -fsSL 'https://go.dev/VERSION?m=text' | head -n1)" @@ -102,6 +109,10 @@ rm -rf /usr/share/doc /usr/share/man /usr/share/info /usr/share/lintian find /usr/share/locale -mindepth 1 -maxdepth 1 ! -name 'en*' ! -name 'locale.alias' -exec rm -rf {} + rm -rf /root/.cache /root/.npm /root/.config /root/go /tmp/* /var/tmp/* +# Valkey opens its log file at startup; recreate the log directory the +# cleanup above removed, with its package owner. +install -d -o valkey -g adm -m 2750 /var/log/valkey + # --- sanity ------------------------------------------------------------------ go version govulncheck -version @@ -116,6 +127,13 @@ magick --version gs --version ruby --version bundle --version +valkey-server --version +valkey-cli --version +valkey-server /etc/valkey/valkey.conf +ok=; for i in $(seq 1 50); do if valkey-cli ping 2>/dev/null | grep -q PONG; then ok=1; break; fi; sleep 0.1; done; [ -n "$ok" ] +valkey-cli shutdown nosave +psql --version +postgres --version hugo version node --version npm --version @@ -129,6 +147,8 @@ virtualenv --version test -x /usr/local/sbin/runsvinit test -d /etc/service -# The checks above leave caches behind (node compile cache etc). +# The checks above leave caches and log files behind. rm -rf /tmp/* /var/tmp/* /root/.cache /root/.npm /root/.config +rm -f /var/log/valkey/* +install -d -o valkey -g adm -m 2750 /var/log/valkey rm -f "$0" -- 2.54.0 From 574c5cdbcef04e69760ccb1a8eaf19550a560e83 Mon Sep 17 00:00:00 2001 From: sneak Date: Thu, 5 Nov 2026 13:57:52 +0000 Subject: [PATCH 10/11] Add browsers for tests: Playwright, Firefox, geckodriver, headless Chrome (closes #10) Firefox comes from Mozilla's own apt repo, pinned above Ubuntu's snap-only package. geckodriver is at its latest release, independent of the Firefox version. Playwright installs its Chromium and Firefox; WebKit is skipped because no repository uses it today. Playwright's Chromium is exposed as the headless Chrome. Model: deepseek-v4-pro --- Dockerfile | 1 + README.md | 23 +++++++++++++++++++---- bin/test | 4 ++++ build.sh | 42 +++++++++++++++++++++++++++++++++++++++++- 4 files changed, 65 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 345ddfd..1c338cc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,6 +20,7 @@ ENV LANG=C.UTF-8 \ COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \ COREPACK_ENABLE_AUTO_PIN=0 \ PYENV_ROOT=/usr/local/pyenv \ + PLAYWRIGHT_BROWSERS_PATH=/ms-playwright \ GOPATH=/root/go \ PATH=/usr/local/go/bin:/root/go/bin:/usr/local/nvm/current/bin:/usr/local/pyenv/shims:/usr/local/pyenv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin diff --git a/README.md b/README.md index 3d6cfd7..a31ce2a 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,11 @@ Image: `git.eeqj.de/sneak/baseimage`, built for `linux/amd64` and | yarn, pnpm | corepack, cache pre-populated | `/usr/local/corepack` | | wrangler | `npm install -g wrangler` | nvm LTS global bin | | prettier | `npm install -g prettier` | nvm LTS global bin | +| Playwright | `npm install -g playwright` | nvm LTS global bin | +| Playwright browsers | `playwright install` (chromium, firefox) | `/ms-playwright` | +| Firefox | Mozilla apt repo | `/usr/bin/firefox` | +| geckodriver | GitHub release | `/usr/local/bin/geckodriver` | +| headless Chrome | Playwright Chromium | `chromium` on `PATH` | | Python (distro) | apt `python3`, `pip`, `venv` | `/usr/bin/python3` | | pyenv | git tag | `/usr/local/pyenv` | | Python (latest) | `pyenv install` | `/usr/local/pyenv/versions/*` | @@ -68,7 +73,8 @@ dependencies in child images. Environment variables set in the image: `NVM_DIR`, `NVM_SYMLINK_CURRENT`, `COREPACK_HOME`, `COREPACK_ENABLE_DOWNLOAD_PROMPT=0`, -`COREPACK_ENABLE_AUTO_PIN=0`, `PYENV_ROOT`, `GOPATH`, `LANG=C.UTF-8`. +`COREPACK_ENABLE_AUTO_PIN=0`, `PYENV_ROOT`, `GOPATH`, `LANG=C.UTF-8`, +`PLAYWRIGHT_BROWSERS_PATH`. ### Yarn and corepack @@ -91,9 +97,18 @@ the `Dockerfile`: | `NVM_GIT_TAG` | `nvm-sh/nvm` release tag | | `PYENV_GIT_TAG` | `pyenv/pyenv` release tag | -Everything else resolves to its current release at build time: Ubuntu -packages, Go, Node LTS, Python, Hugo, golangci-lint, npm, yarn, pnpm, -wrangler. +Everything else resolves to its current release at build time: + +- Ubuntu packages +- Go, Node LTS, and Python +- Hugo and golangci-lint +- npm, yarn, pnpm, wrangler, and prettier +- Playwright and its browsers +- Firefox and geckodriver + +Playwright installs its browsers for the image's Playwright version only. A +child image that pins a different Playwright version downloads its own +browsers. ## Services and init diff --git a/bin/test b/bin/test index 34102a5..813d905 100755 --- a/bin/test +++ b/bin/test @@ -39,6 +39,10 @@ docker run --rm "$img" bash -ec ' valkey-cli shutdown nosave psql --version postgres --version + playwright --version + firefox --version + geckodriver --version + chromium --version ' echo "== $img: runit service lifecycle" diff --git a/build.sh b/build.sh index 0a7cefe..3278f8c 100755 --- a/build.sh +++ b/build.sh @@ -77,7 +77,7 @@ nvm install --lts nvm alias default "lts/*" nvm use default set -ux -npm install -g npm@latest corepack wrangler prettier +npm install -g npm@latest corepack wrangler prettier playwright corepack enable # Pre-populate the corepack cache so projects that pin a package manager # do not download at run time. The last yarn installed becomes the default. @@ -90,6 +90,42 @@ pnpm --version yarn --version nvm cache clear +# --- browsers for tests: Firefox, geckodriver, Playwright -------------------- +# Ubuntu's firefox is a snap that does not run in a container, so use +# Mozilla's own apt repository for the real binary. +install -d -m 0755 /etc/apt/keyrings +curl -fsSL https://packages.mozilla.org/apt/repo-signing-key.gpg \ + -o /etc/apt/keyrings/packages.mozilla.org.asc +echo "deb [signed-by=/etc/apt/keyrings/packages.mozilla.org.asc] https://packages.mozilla.org/apt mozilla main" \ + > /etc/apt/sources.list.d/mozilla.list +cat > /etc/apt/preferences.d/mozilla <<'EOF' +Package: * +Pin: origin packages.mozilla.org +Pin-Priority: 1000 +EOF +apt-get update +apt-get -y install --no-install-recommends -t mozilla firefox + +# geckodriver, at its latest release (independent of the Firefox version). +gd="$(latest_tag mozilla/geckodriver)" +case "$arch" in + arm64) gd_arch=linux-aarch64 ;; + *) gd_arch=linux64 ;; +esac +curl -fsSL "https://github.com/mozilla/geckodriver/releases/download/${gd}/geckodriver-${gd}-${gd_arch}.tar.gz" | + tar -C /usr/local/bin -xz geckodriver + +# Playwright's Chromium and Firefox. WebKit is skipped because no repository +# uses it today. Playwright's Chromium is also the headless Chrome for +# chromedp-style clients, exposed as `chromium` on PATH. +playwright install --with-deps chromium firefox +chromiums=( "$PLAYWRIGHT_BROWSERS_PATH"/chromium-*/chrome-linux*/chrome ) +if [ "${#chromiums[@]}" -ne 1 ] || [ ! -x "${chromiums[0]}" ]; then + echo "error: expected exactly one chromium binary, found ${#chromiums[@]}" >&2 + exit 1 +fi +ln -s "${chromiums[0]}" /usr/local/bin/chromium + # --- pyenv, Python (latest), pip, virtualenv --------------------------------- git clone -q --depth 1 --branch "$PYENV_GIT_TAG" https://github.com/pyenv/pyenv.git "$PYENV_ROOT" rm -rf "$PYENV_ROOT/.git" @@ -141,6 +177,10 @@ COREPACK_ENABLE_NETWORK=0 yarn --version COREPACK_ENABLE_NETWORK=0 pnpm --version wrangler --version prettier --version +playwright --version +firefox --version +geckodriver --version +chromium --version python3 --version pip --version virtualenv --version -- 2.54.0 From 8f976e38e021945c3eda0c5109dcf1ceb476a1b7 Mon Sep 17 00:00:00 2001 From: sneak Date: Thu, 5 Nov 2026 13:58:09 +0000 Subject: [PATCH 11/11] Add nginx (closes #11) Install nginx from apt and check nginx -t in the smoke test. Model: deepseek-v4-pro --- README.md | 87 ++++++++++++++++++++++++++++--------------------------- bin/test | 1 + build.sh | 10 +++++-- 3 files changed, 52 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index a31ce2a..0d3a182 100644 --- a/README.md +++ b/README.md @@ -11,45 +11,46 @@ Image: `git.eeqj.de/sneak/baseimage`, built for `linux/amd64` and ## What is in it -| Component | Source | Location | -| --------------- | ------------------------------ | ------------------------------ | -| Ubuntu 26.04 | `ubuntu:26.04`, fully upgraded | `/` | -| init | `runsvinit` (built from Go) | `/usr/local/sbin/runsvinit` | -| services | `runit` (apt) | `/etc/sv`, `/etc/service` | -| build-essential | apt | `gcc`, `make`, `pkg-config` | -| Go (distro) | apt `golang-go` | `/usr/lib/go-*` | -| Go (latest) | go.dev tarball | `/usr/local/go` | -| golangci-lint | GitHub release | `/usr/local/bin/golangci-lint` | -| shellcheck | apt | `/usr/bin/shellcheck` | -| govulncheck | `go install` (latest) | `/usr/local/bin/govulncheck` | -| Hugo (extended) | GitHub release `.deb` | `/usr/local/bin/hugo` | -| Node (distro) | apt `nodejs`, `npm` | `/usr/bin/node` | -| nvm | git tag | `/usr/local/nvm` | -| Node (LTS) | `nvm install --lts` | `/usr/local/nvm/current` | -| corepack | `npm install -g corepack` | nvm LTS global bin | -| yarn, pnpm | corepack, cache pre-populated | `/usr/local/corepack` | -| wrangler | `npm install -g wrangler` | nvm LTS global bin | -| prettier | `npm install -g prettier` | nvm LTS global bin | -| Playwright | `npm install -g playwright` | nvm LTS global bin | -| Playwright browsers | `playwright install` (chromium, firefox) | `/ms-playwright` | -| Firefox | Mozilla apt repo | `/usr/bin/firefox` | -| geckodriver | GitHub release | `/usr/local/bin/geckodriver` | -| headless Chrome | Playwright Chromium | `chromium` on `PATH` | -| Python (distro) | apt `python3`, `pip`, `venv` | `/usr/bin/python3` | -| pyenv | git tag | `/usr/local/pyenv` | -| Python (latest) | `pyenv install` | `/usr/local/pyenv/versions/*` | -| virtualenv | apt, and pip in pyenv Python | `virtualenv` on `PATH` | -| tzdata | apt | `/usr/share/zoneinfo` | -| zstd | apt | `/usr/bin/zstd` | -| sqlite3 (CLI) | apt | `/usr/bin/sqlite3` | -| libvips | apt `libvips-dev`, `libvips-tools` | `vips` on `PATH` | -| libheif | apt `libheif-dev`, `libheif-plugin-aomenc` | HEIF/AVIF codec | -| ImageMagick | apt | `magick` | -| Ghostscript | apt | `gs` | -| Ruby | apt `ruby`, `ruby-dev` | `/usr/bin/ruby` | -| Bundler | apt `bundler` | `bundle` on `PATH` | -| Valkey | apt `valkey-server`, `valkey-tools` | `valkey-server`, `valkey-cli` | -| PostgreSQL | apt `postgresql` | `psql`, `postgres`, `initdb`, `pg_ctl` on `PATH` | +| Component | Source | Location | +| ------------------- | ------------------------------------------ | ------------------------------------------------ | +| Ubuntu 26.04 | `ubuntu:26.04`, fully upgraded | `/` | +| init | `runsvinit` (built from Go) | `/usr/local/sbin/runsvinit` | +| services | `runit` (apt) | `/etc/sv`, `/etc/service` | +| build-essential | apt | `gcc`, `make`, `pkg-config` | +| Go (distro) | apt `golang-go` | `/usr/lib/go-*` | +| Go (latest) | go.dev tarball | `/usr/local/go` | +| golangci-lint | GitHub release | `/usr/local/bin/golangci-lint` | +| shellcheck | apt | `/usr/bin/shellcheck` | +| govulncheck | `go install` (latest) | `/usr/local/bin/govulncheck` | +| Hugo (extended) | GitHub release `.deb` | `/usr/local/bin/hugo` | +| Node (distro) | apt `nodejs`, `npm` | `/usr/bin/node` | +| nvm | git tag | `/usr/local/nvm` | +| Node (LTS) | `nvm install --lts` | `/usr/local/nvm/current` | +| corepack | `npm install -g corepack` | nvm LTS global bin | +| yarn, pnpm | corepack, cache pre-populated | `/usr/local/corepack` | +| wrangler | `npm install -g wrangler` | nvm LTS global bin | +| prettier | `npm install -g prettier` | nvm LTS global bin | +| Playwright | `npm install -g playwright` | nvm LTS global bin | +| Playwright browsers | `playwright install` (chromium, firefox) | `/ms-playwright` | +| Firefox | Mozilla apt repo | `/usr/bin/firefox` | +| geckodriver | GitHub release | `/usr/local/bin/geckodriver` | +| headless Chrome | Playwright Chromium | `chromium` on `PATH` | +| Python (distro) | apt `python3`, `pip`, `venv` | `/usr/bin/python3` | +| pyenv | git tag | `/usr/local/pyenv` | +| Python (latest) | `pyenv install` | `/usr/local/pyenv/versions/*` | +| virtualenv | apt, and pip in pyenv Python | `virtualenv` on `PATH` | +| tzdata | apt | `/usr/share/zoneinfo` | +| zstd | apt | `/usr/bin/zstd` | +| sqlite3 (CLI) | apt | `/usr/bin/sqlite3` | +| libvips | apt `libvips-dev`, `libvips-tools` | `vips` on `PATH` | +| libheif | apt `libheif-dev`, `libheif-plugin-aomenc` | HEIF/AVIF codec | +| ImageMagick | apt | `magick` | +| Ghostscript | apt | `gs` | +| Ruby | apt `ruby`, `ruby-dev` | `/usr/bin/ruby` | +| Bundler | apt `bundler` | `bundle` on `PATH` | +| Valkey | apt `valkey-server`, `valkey-tools` | `valkey-server`, `valkey-cli` | +| PostgreSQL | apt `postgresql` | `psql`, `postgres`, `initdb`, `pg_ctl` on `PATH` | +| nginx | apt | `/usr/sbin/nginx` | Plus the usual odds and ends: `git`, `curl`, `wget`, `jq`, `rsync`, `openssh-client`, `zip`/`unzip`, `xz-utils`, `bzip2`, and the `-dev` @@ -201,8 +202,8 @@ Every unpinned version is resolved at build time, so a rebuild picks up current packages and tool releases. Rebuild and retag periodically; child images get everything new on their next build. -The registry sits behind Traefik. The large `RUN` layer (about 650MB -compressed, over 2GB uncompressed) needs the entrypoint +The registry sits behind Traefik. The large `RUN` layer (about 1.63 GB +compressed, 4.3 GB on disk) needs the entrypoint `respondingTimeouts` raised from Traefik's 60 second default, or the final blob commit fails with a 499 or 500 and docker re-uploads the layer. @@ -217,7 +218,7 @@ Interactive shells (login shells, or anything that sources `commit-logs`, `clean`, plus `lib.sh` (shared setup) and `native-arch`. - `Dockerfile` — base, environment, one `COPY`, one `ADD`, one `RUN`. - `build.sh` — everything the `RUN` does, in order: apt, Go, runsvinit, - golangci-lint, Hugo, nvm/Node/corepack/wrangler, pyenv/Python, cleanup, - sanity checks. Removes itself when done. + golangci-lint, govulncheck, Hugo, nvm/Node/corepack/wrangler, browsers, + pyenv/Python, cleanup, sanity checks. Removes itself when done. - `rootfs/` — files copied verbatim onto `/` before `build.sh` runs. - `logs/YYYY-MM-DD/` — build logs for each released image, per arch. diff --git a/bin/test b/bin/test index 813d905..a94ea8d 100755 --- a/bin/test +++ b/bin/test @@ -39,6 +39,7 @@ docker run --rm "$img" bash -ec ' valkey-cli shutdown nosave psql --version postgres --version + nginx -t playwright --version firefox --version geckodriver --version diff --git a/build.sh b/build.sh index 3278f8c..5c57002 100755 --- a/build.sh +++ b/build.sh @@ -32,7 +32,8 @@ apt-get -y install --no-install-recommends \ libvips-dev libvips-tools libheif-dev libheif-plugin-aomenc \ imagemagick ghostscript \ ruby ruby-dev bundler \ - valkey-server valkey-tools postgresql + valkey-server valkey-tools postgresql \ + nginx # Link the PostgreSQL server binaries onto PATH. They live under a versioned # directory (/usr/lib/postgresql/*/bin); the client psql is already on PATH. @@ -145,8 +146,9 @@ rm -rf /usr/share/doc /usr/share/man /usr/share/info /usr/share/lintian find /usr/share/locale -mindepth 1 -maxdepth 1 ! -name 'en*' ! -name 'locale.alias' -exec rm -rf {} + rm -rf /root/.cache /root/.npm /root/.config /root/go /tmp/* /var/tmp/* -# Valkey opens its log file at startup; recreate the log directory the -# cleanup above removed, with its package owner. +# nginx and Valkey open their log files at startup; recreate the log +# directories the cleanup above removed, with their package owners. +install -d -o root -g adm /var/log/nginx install -d -o valkey -g adm -m 2750 /var/log/valkey # --- sanity ------------------------------------------------------------------ @@ -170,6 +172,7 @@ ok=; for i in $(seq 1 50); do if valkey-cli ping 2>/dev/null | grep -q PONG; the valkey-cli shutdown nosave psql --version postgres --version +nginx -t hugo version node --version npm --version @@ -190,5 +193,6 @@ test -d /etc/service # The checks above leave caches and log files behind. rm -rf /tmp/* /var/tmp/* /root/.cache /root/.npm /root/.config rm -f /var/log/valkey/* +rm -f /var/log/nginx/* install -d -o valkey -g adm -m 2750 /var/log/valkey rm -f "$0" -- 2.54.0