Files
opencode-devbox/entrypoint.sh
T
joakimp f210d533eb Fix root-owned parent dirs left behind by nested volume mounts
When a named volume is mounted at a nested path like
/home/developer/.local/state/opencode, Docker creates the parent
directory (.local/state) as root:root. The existing chown loop only
fixed the leaf mount points, leaving parents unwritable by the
developer user.

Add a non-recursive pre-pass that fixes ownership of the common
parent dirs (.local, .local/share, .local/state, .config) so that
anything creating new subdirs beneath them works correctly after a
fresh container recreate. Regression introduced by commit 967ce7d
(devbox-state volume) and only partially addressed by a06dc5f.
2026-04-20 22:12:14 +02:00

87 lines
4.0 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
USER_NAME="developer"
CURRENT_UID=$(id -u "$USER_NAME")
CURRENT_GID=$(id -g "$USER_NAME")
# ── UID/GID adjustment ───────────────────────────────────────────────
# Priority: env vars > auto-detect from /workspace > default (1000)
TARGET_UID="${USER_UID:-}"
TARGET_GID="${USER_GID:-}"
# Auto-detect from /workspace owner if env vars not set
if [ -z "$TARGET_UID" ] && [ -d /workspace ]; then
WORKSPACE_UID=$(stat -c '%u' /workspace 2>/dev/null || stat -f '%u' /workspace 2>/dev/null)
WORKSPACE_GID=$(stat -c '%g' /workspace 2>/dev/null || stat -f '%g' /workspace 2>/dev/null)
# Only adjust if workspace is owned by a non-root user
if [ "$WORKSPACE_UID" != "0" ] && [ "$WORKSPACE_UID" != "$CURRENT_UID" ]; then
TARGET_UID="$WORKSPACE_UID"
TARGET_GID="${TARGET_GID:-$WORKSPACE_GID}"
fi
fi
# Apply UID/GID changes if needed
if [ -n "$TARGET_GID" ] && [ "$TARGET_GID" != "$CURRENT_GID" ]; then
groupmod -g "$TARGET_GID" "$USER_NAME" 2>/dev/null || true
find /home/"$USER_NAME" -not -path "/home/$USER_NAME/.ssh/*" -group "$CURRENT_GID" -exec chgrp "$TARGET_GID" {} + 2>/dev/null || true
fi
if [ -n "$TARGET_UID" ] && [ "$TARGET_UID" != "$CURRENT_UID" ]; then
usermod -u "$TARGET_UID" "$USER_NAME" 2>/dev/null || true
find /home/"$USER_NAME" -not -path "/home/$USER_NAME/.ssh/*" -user "$CURRENT_UID" -exec chown "$TARGET_UID" {} + 2>/dev/null || true
echo "Adjusted developer UID:GID to $TARGET_UID:${TARGET_GID:-$CURRENT_GID}"
fi
# ── SSH key permissions ──────────────────────────────────────────────
# If SSH keys are mounted, fix permissions (skip if read-only mount)
if [ -d "/home/$USER_NAME/.ssh" ] && [ "$(ls -A "/home/$USER_NAME/.ssh" 2>/dev/null)" ]; then
if touch "/home/$USER_NAME/.ssh/.perm_test" 2>/dev/null; then
rm -f "/home/$USER_NAME/.ssh/.perm_test"
chmod 700 "/home/$USER_NAME/.ssh"
find "/home/$USER_NAME/.ssh" -type f -name "id_*" ! -name "*.pub" -exec chmod 600 {} \; 2>/dev/null || true
find "/home/$USER_NAME/.ssh" -type f -name "*.pub" -exec chmod 644 {} \; 2>/dev/null || true
[ -f "/home/$USER_NAME/.ssh/known_hosts" ] && chmod 644 "/home/$USER_NAME/.ssh/known_hosts"
[ -f "/home/$USER_NAME/.ssh/config" ] && chmod 600 "/home/$USER_NAME/.ssh/config"
fi
fi
# ── Fix ownership of named volume mount points ──────────────────────
# Named volumes are created as root on first use. Fix ownership so the
# developer user can write to them.
FINAL_UID="${TARGET_UID:-$CURRENT_UID}"
FINAL_GID="${TARGET_GID:-$CURRENT_GID}"
# First, fix parent dirs that Docker auto-creates as root:root when it
# materializes nested mount points (e.g. mounting a volume at
# .local/state/opencode creates .local/state as root). Non-recursive —
# we only need the dir node itself; children are handled below or were
# created by the user.
for parent in \
/home/"$USER_NAME"/.local \
/home/"$USER_NAME"/.local/share \
/home/"$USER_NAME"/.local/state \
/home/"$USER_NAME"/.config; do
if [ -d "$parent" ] && [ "$(stat -c '%u' "$parent" 2>/dev/null)" != "$FINAL_UID" ]; then
chown "$FINAL_UID":"$FINAL_GID" "$parent" 2>/dev/null || true
fi
done
for dir in \
/home/"$USER_NAME"/.local/share/opencode \
/home/"$USER_NAME"/.local/state/opencode \
/home/"$USER_NAME"/.local/share/uv \
/home/"$USER_NAME"/.rustup \
/home/"$USER_NAME"/.cargo \
/home/"$USER_NAME"/.vscode-server \
/home/"$USER_NAME"/.config/opencode \
/home/"$USER_NAME"/.config/nvim \
/home/"$USER_NAME"/.agents/skills; do
if [ -d "$dir" ] && [ "$(stat -c '%u' "$dir" 2>/dev/null)" != "$FINAL_UID" ]; then
chown -R "$FINAL_UID":"$FINAL_GID" "$dir" 2>/dev/null || true
fi
done
# ── Drop to developer user for remaining setup ──────────────────────
exec gosu "$USER_NAME" /usr/local/bin/entrypoint-user.sh "$@"