Compare commits

..

4 Commits

Author SHA1 Message Date
8eaf9591dc merge v1.10.2-booterm-glibc 2026-05-19 13:14:25 +00:00
5d52b79a07 v1.10.2: booterm runtime on bookworm-slim (glibc), su-exec → gosu
Switched the booterm runtime + proddeps stages from node:20-alpine (musl)
to node:20-bookworm-slim (glibc) so host-installed glibc binaries (Claude
Code, opencode, nvm node) run inside the container when invoked from the
terminal pane. node-pty's native .node has to be compiled in the same
libc env as the runtime, so both stages flip together; the TypeScript-only
builder stage stays on alpine.

su-exec is alpine-only; Debian replacement is gosu — swapped in both the
runtime apt install and the tmux default-command. uid/gid 1000 collision
with the bookworm `node` user handled via userdel/groupdel before
groupadd/useradd.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 13:14:21 +00:00
ead7cb9d01 merge v1.10.1-booterm-user 2026-05-19 13:07:59 +00:00
d04b30687f v1.10.1: booterm runs shells as samkintop with login bash 2026-05-19 13:07:59 +00:00
3 changed files with 38 additions and 5 deletions

View File

@@ -15,22 +15,45 @@ COPY apps/booterm ./apps/booterm
RUN pnpm --filter=@boocode/booterm build RUN pnpm --filter=@boocode/booterm build
# ---- Prod-deps stage: hoisted, native built via npm rebuild ---- # ---- Prod-deps stage: hoisted, native built via npm rebuild ----
FROM node:20-alpine AS proddeps # v1.10.2: switched to bookworm-slim (glibc) so node-pty's native .node is
# compiled against the same libc as the runtime stage. A musl-built .node
# won't dlopen in a glibc node binary, so both stages must match.
FROM node:20-bookworm-slim AS proddeps
ENV COREPACK_DEFAULT_TO_LATEST=0 ENV COREPACK_DEFAULT_TO_LATEST=0
RUN corepack enable && corepack prepare pnpm@10.15.1 --activate RUN corepack enable && corepack prepare pnpm@10.15.1 --activate
RUN apk add --no-cache python3 make g++ RUN apt-get update && apt-get install -y --no-install-recommends \
python3 make g++ ca-certificates \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /prod WORKDIR /prod
COPY apps/booterm/package.json ./package.json COPY apps/booterm/package.json ./package.json
RUN pnpm install --prod --config.node-linker=hoisted --config.strict-peer-dependencies=false RUN pnpm install --prod --config.node-linker=hoisted --config.strict-peer-dependencies=false
# pnpm 10 ignores build scripts; force compile with npm directly. # pnpm 10 ignores build scripts; force compile with npm directly.
# node-gyp is bundled with npm in the node:20-alpine image. # node-gyp is bundled with npm in the node:20-bookworm-slim image.
RUN cd node_modules/node-pty && npm run install RUN cd node_modules/node-pty && npm run install
# Sanity check — fail the build if the artifact still isn't there # Sanity check — fail the build if the artifact still isn't there
RUN test -f node_modules/node-pty/build/Release/pty.node && echo "pty.node OK" || (echo "pty.node MISSING" && exit 1) RUN test -f node_modules/node-pty/build/Release/pty.node && echo "pty.node OK" || (echo "pty.node MISSING" && exit 1)
# ---- Runtime ---- # ---- Runtime ----
FROM node:20-alpine AS runtime # v1.10.2: switched from node:20-alpine (musl) to node:20-bookworm-slim (glibc)
RUN apk add --no-cache tmux libstdc++ # so glibc-linked binaries from /home/samkintop (Claude Code, opencode, the
# host's nvm node) run inside the container when invoked from the terminal
# pane. Side-effect: su-exec is alpine-only — Debian replacement is gosu.
FROM node:20-bookworm-slim AS runtime
RUN apt-get update && apt-get install -y --no-install-recommends \
tmux bash gosu ca-certificates procps \
&& rm -rf /var/lib/apt/lists/*
# Mirror uid/gid 1000:1000 from the host so the bind-mounted /home/samkintop
# (added in docker-compose) is owned by the user from the container's view.
# bookworm-slim ships a `node` user at 1000 — wipe whatever sits on uid/gid
# 1000 first, then create samkintop fresh.
RUN if id -u 1000 >/dev/null 2>&1; then \
userdel -r "$(id -un 1000)" 2>/dev/null || true; \
fi; \
if getent group 1000 >/dev/null 2>&1; then \
groupdel "$(getent group 1000 | cut -d: -f1)" 2>/dev/null || true; \
fi; \
groupadd -g 1000 samkintop && \
useradd -m -u 1000 -g 1000 -s /bin/bash samkintop
WORKDIR /app WORKDIR /app
COPY --from=builder /build/apps/booterm/dist ./dist COPY --from=builder /build/apps/booterm/dist ./dist
COPY --from=proddeps /prod/package.json ./package.json COPY --from=proddeps /prod/package.json ./package.json

View File

@@ -4,3 +4,12 @@ set -g mouse on
setw -g mode-keys vi setw -g mode-keys vi
set -g status off set -g status off
set -g destroy-unattached off set -g destroy-unattached off
# v1.10.1: shells drop privs to samkintop (uid 1000) so the terminal runs in
# the user's environment, not root. `env HOME=… USER=…` is required because
# gosu (and su-exec before it) only changes uid/gid — it leaves env intact,
# and the tmux server runs as root so HOME would otherwise be /root. bash -l
# then sources samkintop's ~/.profile / ~/.bashrc to pick up PATH (nvm,
# ~/.local/bin, ~/.opencode/bin).
# v1.10.2: su-exec → gosu (alpine → debian; functionally identical).
set -g default-command "gosu samkintop:samkintop env HOME=/home/samkintop USER=samkintop SHELL=/bin/bash bash -l"

View File

@@ -34,6 +34,7 @@ services:
DATABASE_URL: postgres://boocode:${POSTGRES_PASSWORD}@boocode_db:5432/boocode DATABASE_URL: postgres://boocode:${POSTGRES_PASSWORD}@boocode_db:5432/boocode
volumes: volumes:
- /opt:/opt:rw - /opt:/opt:rw
- /home/samkintop:/home/samkintop:rw
depends_on: depends_on:
- boocode_db - boocode_db
networks: networks: