mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-18 16:02:10 +00:00
dcb9e40ea2
* fix(extra): make extra container runnable as a non-root UID
The root-run installs in DockerfileExtra inherit UV_CACHE_DIR
(/tmp/windmill/cache/uv) and XDG_CACHE_HOME (/pyls/.cache) from the base
image and write to them *after* the base image's world-writable pass,
leaving ~320 root-owned 0755 dirs. A non-root UID then fails to debug:
uv venv failed: Failed to write to the client cache
Caused by: Permission denied (os error 13)
at path "/tmp/windmill/cache/uv/simple-v21/pypi/.tmpWxWzTi"
Re-apply the base image's world-writable convention after the installs,
and add the windmill UID/GID 1000 account the app image already has so
runAsUser: 1000 resolves to a real user with a writable $HOME. The
entrypoint also wrote $NETRC to the 0700 /root under `set -e`, killing
the container at startup; redirect $HOME when it is not writable.
Verified against the published image as root, uid 1000, and uid 4567:0:
all four services start and `windmill prepare-deps` succeeds in each.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(extra): scope the HOME fallback per-UID and cover non-root in CI
Review findings on the previous commit:
- `${HOME:-/root}` was used in the writability test but not the assignment,
so an unset HOME (writable /root, redirect skipped) silently wrote the
netrc to `/.netrc` instead of `/root/.netrc`. Normalize HOME once.
- `mkdir -p` succeeds on a dir owned by another UID, so a root-created
/tmp/windmill-home on a shared /tmp reintroduced the startup death this
fix exists to prevent. Scope the fallback to /tmp/windmill-home-$(id -u).
- The extra image's smoke test only ran as root, so nothing exercised the
non-root path this PR is about. Add a --user 1000 run of the same suite
plus a prepare-deps assertion, which is where the EACCES surfaced.
Also correct the DockerfileExtra comment: the proven requirement is the uv
cache, not runtime writes by gopls.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(extra): keep /tmp/monaco code read-only for non-root UIDs
Review nit: /tmp/monaco holds node_modules (code, not cache), so it is not
covered by the base image's world-writable cache convention. Give it a+rX
files with 777 dirs -- enough for the go.mod / ruff.toml the entrypoint and
pyls_launcher write into it -- instead of a+rw on every file.
/tmp/windmill and /pyls/.cache keep a+rw: that is the base convention for
caches (DockerfileSlimEe:69 chmod -R a+rw /tmp/windmill/cache; Dockerfile:339
"cache files already have 666"), and uv rewrites cache entries in place.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(extra): drop the non-root CI smoke test
Reverts publish_extra.yml to its root-only smoke test; the DockerfileExtra
and entrypoint fix is unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
128 lines
4.1 KiB
Bash
128 lines
4.1 KiB
Bash
#!/bin/bash
|
|
set -e
|
|
|
|
# Windmill Extra Services Entrypoint
|
|
# Starts LSP, Multiplayer, and Debugger services based on environment variables
|
|
|
|
# Track PIDs for cleanup
|
|
PIDS=()
|
|
|
|
cleanup() {
|
|
echo "[entrypoint] Shutting down services..."
|
|
for pid in "${PIDS[@]}"; do
|
|
if kill -0 "$pid" 2>/dev/null; then
|
|
kill "$pid" 2>/dev/null || true
|
|
fi
|
|
done
|
|
wait
|
|
echo "[entrypoint] All services stopped"
|
|
exit 0
|
|
}
|
|
|
|
trap cleanup SIGTERM SIGINT
|
|
|
|
# An arbitrary non-root UID gets HOME=/ and cannot write the image's 0700 /root, so
|
|
# redirect $HOME before anything writes under it (netrc below, plus the bun/npm/go
|
|
# caches in the services). Keep the fallback UID-scoped: a leftover dir from a
|
|
# different UID on a shared /tmp is not writable. Root keeps HOME=/root.
|
|
HOME="${HOME:-/root}"
|
|
if [ ! -w "$HOME" ]; then
|
|
echo "[entrypoint] HOME=$HOME is not writable for UID $(id -u), using HOME=/tmp/windmill-home-$(id -u)"
|
|
HOME="/tmp/windmill-home-$(id -u)"
|
|
mkdir -p "$HOME"
|
|
fi
|
|
export HOME
|
|
|
|
# Setup NETRC if provided (for LSP)
|
|
if [ -n "$NETRC" ]; then
|
|
echo "$NETRC" > "$HOME/.netrc"
|
|
chmod 600 "$HOME/.netrc"
|
|
fi
|
|
|
|
# Setup cache directory for LSP (falls back to the image's world-writable
|
|
# XDG_CACHE_HOME=/pyls/.cache when $HOME/.cache isn't mounted)
|
|
if [ -d "$HOME/.cache" ]; then
|
|
export XDG_CACHE_HOME="$HOME/.cache"
|
|
cp -r /pyls/.cache "$HOME/.cache" 2>/dev/null || true
|
|
fi
|
|
|
|
# Setup Monaco temp directory for LSP
|
|
mkdir -p /tmp/monaco
|
|
if [ ! -f /tmp/monaco/go.mod ]; then
|
|
echo -e "module mymod\ngo 1.26" > /tmp/monaco/go.mod
|
|
fi
|
|
|
|
echo "[entrypoint] Starting Windmill Extra Services"
|
|
echo "[entrypoint] ENABLE_LSP=${ENABLE_LSP:-true}"
|
|
echo "[entrypoint] ENABLE_MULTIPLAYER=${ENABLE_MULTIPLAYER:-true}"
|
|
echo "[entrypoint] ENABLE_DEBUGGER=${ENABLE_DEBUGGER:-true}"
|
|
echo "[entrypoint] ENABLE_GATEWAY=${ENABLE_GATEWAY:-true}"
|
|
|
|
# Start LSP service
|
|
if [ "${ENABLE_LSP:-true}" = "true" ]; then
|
|
echo "[entrypoint] Starting LSP on port ${LSP_PORT:-3001}..."
|
|
cd /pyls
|
|
PORT=${LSP_PORT:-3001} python3 pyls_launcher.py &
|
|
PIDS+=($!)
|
|
echo "[entrypoint] LSP started (PID: ${PIDS[-1]})"
|
|
fi
|
|
|
|
# Start Multiplayer service (custom y-websocket with logging)
|
|
if [ "${ENABLE_MULTIPLAYER:-true}" = "true" ]; then
|
|
echo "[entrypoint] Starting Multiplayer on port ${MULTIPLAYER_PORT:-3002}..."
|
|
cd /multiplayer
|
|
PORT=${MULTIPLAYER_PORT:-3002} HOST=${HOST:-0.0.0.0} node server.mjs &
|
|
PIDS+=($!)
|
|
echo "[entrypoint] Multiplayer started (PID: ${PIDS[-1]})"
|
|
fi
|
|
|
|
# Start Debugger service
|
|
if [ "${ENABLE_DEBUGGER:-true}" = "true" ]; then
|
|
echo "[entrypoint] Starting Debugger on port ${DEBUGGER_PORT:-3003}..."
|
|
cd /debugger
|
|
|
|
# Build debugger arguments
|
|
DEBUGGER_ARGS="--host ${HOST:-0.0.0.0} --port ${DEBUGGER_PORT:-3003}"
|
|
DEBUGGER_ARGS="$DEBUGGER_ARGS --windmill /usr/local/bin/windmill"
|
|
|
|
# Enable nsjail if requested
|
|
if [ "${ENABLE_NSJAIL:-false}" = "true" ]; then
|
|
DEBUGGER_ARGS="$DEBUGGER_ARGS --nsjail --nsjail-config /debugger/nsjail.debug.config.proto"
|
|
fi
|
|
|
|
bun run dap_debug_service.ts $DEBUGGER_ARGS &
|
|
PIDS+=($!)
|
|
echo "[entrypoint] Debugger started (PID: ${PIDS[-1]})"
|
|
fi
|
|
|
|
# Start Gateway reverse proxy (routes /ws/*, /ws_mp/*, /ws_debug/* to the right service)
|
|
if [ "${ENABLE_GATEWAY:-true}" = "true" ]; then
|
|
echo "[entrypoint] Starting Gateway on port ${GATEWAY_PORT:-3000}..."
|
|
cd /multiplayer
|
|
PORT=${GATEWAY_PORT:-3000} node gateway.mjs &
|
|
PIDS+=($!)
|
|
echo "[entrypoint] Gateway started (PID: ${PIDS[-1]})"
|
|
fi
|
|
|
|
# Check if any services were started
|
|
if [ ${#PIDS[@]} -eq 0 ]; then
|
|
echo "[entrypoint] WARNING: No services enabled. Set ENABLE_LSP, ENABLE_MULTIPLAYER, or ENABLE_DEBUGGER to true."
|
|
echo "[entrypoint] Sleeping indefinitely..."
|
|
sleep infinity
|
|
fi
|
|
|
|
echo "[entrypoint] All enabled services started. Waiting..."
|
|
|
|
# Wait for any process to exit
|
|
wait -n "${PIDS[@]}" 2>/dev/null || true
|
|
|
|
# If one process exits, check which one and report
|
|
for i in "${!PIDS[@]}"; do
|
|
if ! kill -0 "${PIDS[$i]}" 2>/dev/null; then
|
|
echo "[entrypoint] Service (PID: ${PIDS[$i]}) has exited"
|
|
fi
|
|
done
|
|
|
|
# Keep running and wait for remaining processes
|
|
wait
|