singularity-forge/docker/entrypoint.sh
Iouri Goussev 0e07c647c5 fix(docker): overhaul fragile setup, adopt proven container patterns (#2716)
Split fake multi-stage Dockerfile into independent CI builder and
runtime images. Add proper entrypoint with UID/GID remapping via
PUID/PGID, sentinel-based first-boot bootstrap, pre-creation of
critical file targets, and signal-forwarding privilege drop via gosu.
Standardize on Node 24, split compose into minimal + full reference.

Closes #9
2026-03-26 16:10:49 -06:00

81 lines
2.8 KiB
Bash
Executable file

#!/bin/bash
set -e
# ──────────────────────────────────────────────
# GSD Container Entrypoint
#
# Responsibilities:
# 1. UID/GID remapping — match host user via PUID/PGID
# 2. Pre-create critical files — prevent Docker bind-mount
# from creating directories where files are expected
# 3. Sentinel-based bootstrap — one-time first-boot setup
# 4. Signal forwarding — exec into the final process
# ──────────────────────────────────────────────
GSD_USER="gsd"
GSD_HOME="/home/${GSD_USER}"
GSD_DIR="${GSD_HOME}/.gsd"
# ── 1. UID/GID Remapping ────────────────────────────────
# Accept PUID/PGID from the environment so the container
# can run with the same UID/GID as the host user, avoiding
# permission headaches on bind-mounted volumes.
PUID="${PUID:-1000}"
PGID="${PGID:-1000}"
CURRENT_UID=$(id -u "${GSD_USER}")
CURRENT_GID=$(id -g "${GSD_USER}")
REMAPPED=0
if [ "${PGID}" != "${CURRENT_GID}" ]; then
groupmod -o -g "${PGID}" "${GSD_USER}"
REMAPPED=1
fi
if [ "${PUID}" != "${CURRENT_UID}" ]; then
usermod -o -u "${PUID}" "${GSD_USER}"
REMAPPED=1
fi
# Fix ownership only when UID/GID actually changed
if [ "${REMAPPED}" -eq 1 ]; then
chown -R "${PUID}:${PGID}" "${GSD_HOME}"
chown "${PUID}:${PGID}" /workspace
fi
# ── 2. Pre-create Critical Files ────────────────────────
# Docker bind-mounts will create a *directory* if the target
# path doesn't exist. We need these to be files, so touch
# them before Docker gets a chance to mangle things.
mkdir -p "${GSD_DIR}"
if [ ! -f "${GSD_DIR}/settings.json" ]; then
echo '{}' > "${GSD_DIR}/settings.json"
fi
chown "${PUID}:${PGID}" "${GSD_DIR}" "${GSD_DIR}/settings.json"
# ── 3. Sentinel-based Bootstrap ─────────────────────────
# Run first-boot setup exactly once. Subsequent container
# starts (or restarts) skip this entirely.
SENTINEL="${GSD_DIR}/.bootstrapped"
if [ ! -f "${SENTINEL}" ]; then
if [ -x /usr/local/bin/bootstrap.sh ]; then
# Run bootstrap as the gsd user so files get correct ownership
gosu "${GSD_USER}" /usr/local/bin/bootstrap.sh
fi
touch "${SENTINEL}"
chown "${PUID}:${PGID}" "${SENTINEL}"
fi
# ── 4. Drop Privileges & Exec ──────────────────────────
# Replace this shell process with the final command running
# as the gsd user. exec + gosu = proper PID 1 = proper
# signal forwarding (SIGTERM, SIGINT, etc.).
exec gosu "${GSD_USER}" "$@"