Add runtime UID/GID adjustment to match host workspace owner
This commit is contained in:
@@ -103,6 +103,25 @@ opencode
|
||||
| `GIT_USER_NAME` | Git commit author name |
|
||||
| `GIT_USER_EMAIL` | Git commit author email |
|
||||
|
||||
### User ID Mapping
|
||||
|
||||
The container runs as user `developer` (UID 1000 by default). If your host user has a different UID, file permission mismatches can occur on mounted volumes.
|
||||
|
||||
The entrypoint automatically detects the owner of `/workspace` and adjusts the container user's UID/GID to match. You can also set it explicitly:
|
||||
|
||||
| Variable | Description | Default |
|
||||
|---|---|---|
|
||||
| `USER_UID` | Container user UID | Auto-detect from `/workspace` owner |
|
||||
| `USER_GID` | Container user GID | Auto-detect from `/workspace` owner |
|
||||
|
||||
```bash
|
||||
docker run -it --rm \
|
||||
-e USER_UID=$(id -u) \
|
||||
-e USER_GID=$(id -g) \
|
||||
-v ~/projects:/workspace \
|
||||
joakimp/opencode-devbox:latest
|
||||
```
|
||||
|
||||
## Data Storage and Persistence
|
||||
|
||||
Understanding what survives container restarts and what doesn't:
|
||||
|
||||
+4
-2
@@ -31,6 +31,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
less \
|
||||
vim-tiny \
|
||||
sudo \
|
||||
gosu \
|
||||
locales \
|
||||
procps \
|
||||
unzip \
|
||||
@@ -103,9 +104,10 @@ RUN mkdir -p /workspace \
|
||||
|
||||
# ── Entrypoint ────────────────────────────────────────────────────────
|
||||
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||
RUN chmod +x /usr/local/bin/entrypoint.sh
|
||||
COPY entrypoint-user.sh /usr/local/bin/entrypoint-user.sh
|
||||
RUN chmod +x /usr/local/bin/entrypoint.sh /usr/local/bin/entrypoint-user.sh
|
||||
|
||||
USER ${USER_NAME}
|
||||
# Start as root — entrypoint adjusts UID/GID then drops to developer
|
||||
WORKDIR /workspace
|
||||
|
||||
ENTRYPOINT ["entrypoint.sh"]
|
||||
|
||||
@@ -99,6 +99,8 @@ docker compose exec devbox aws --version
|
||||
| `GIT_USER_EMAIL` | Git commit author email | — |
|
||||
| `WORKSPACE_PATH` | Host path to mount | `.` |
|
||||
| `SSH_KEY_PATH` | Host SSH key directory | `~/.ssh` |
|
||||
| `USER_UID` | Override container user UID | Auto-detect from `/workspace` |
|
||||
| `USER_GID` | Override container user GID | Auto-detect from `/workspace` |
|
||||
|
||||
### Custom opencode config
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# ── Git config defaults ──────────────────────────────────────────────
|
||||
if [ -n "${GIT_USER_NAME:-}" ] && ! git config --global user.name &>/dev/null; then
|
||||
git config --global user.name "$GIT_USER_NAME"
|
||||
fi
|
||||
if [ -n "${GIT_USER_EMAIL:-}" ] && ! git config --global user.email &>/dev/null; then
|
||||
git config --global user.email "$GIT_USER_EMAIL"
|
||||
fi
|
||||
|
||||
# ── Generate opencode config from env vars if no config mounted ──────
|
||||
CONFIG_DIR="$HOME/.config/opencode"
|
||||
CONFIG_FILE="$CONFIG_DIR/opencode.json"
|
||||
|
||||
if [ ! -f "$CONFIG_FILE" ] && [ -n "${OPENCODE_PROVIDER:-}" ]; then
|
||||
echo "Generating opencode config for provider: $OPENCODE_PROVIDER"
|
||||
mkdir -p "$CONFIG_DIR"
|
||||
|
||||
case "$OPENCODE_PROVIDER" in
|
||||
anthropic)
|
||||
cat > "$CONFIG_FILE" <<EOF
|
||||
{
|
||||
"\$schema": "https://opencode.ai/config.json",
|
||||
"model": "${OPENCODE_MODEL:-anthropic/claude-sonnet-4-5}",
|
||||
"share": "disabled",
|
||||
"autoupdate": false
|
||||
}
|
||||
EOF
|
||||
;;
|
||||
openai)
|
||||
cat > "$CONFIG_FILE" <<EOF
|
||||
{
|
||||
"\$schema": "https://opencode.ai/config.json",
|
||||
"model": "${OPENCODE_MODEL:-openai/gpt-4o}",
|
||||
"share": "disabled",
|
||||
"autoupdate": false
|
||||
}
|
||||
EOF
|
||||
;;
|
||||
amazon-bedrock)
|
||||
cat > "$CONFIG_FILE" <<EOF
|
||||
{
|
||||
"\$schema": "https://opencode.ai/config.json",
|
||||
"model": "${OPENCODE_MODEL:-amazon-bedrock/anthropic.claude-sonnet-4-5-v1}",
|
||||
"share": "disabled",
|
||||
"autoupdate": false,
|
||||
"provider": {
|
||||
"amazon-bedrock": {
|
||||
"options": {
|
||||
"region": "${AWS_REGION:-us-east-1}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
;;
|
||||
*)
|
||||
cat > "$CONFIG_FILE" <<EOF
|
||||
{
|
||||
"\$schema": "https://opencode.ai/config.json",
|
||||
"model": "${OPENCODE_MODEL:-anthropic/claude-sonnet-4-5}",
|
||||
"share": "disabled",
|
||||
"autoupdate": false
|
||||
}
|
||||
EOF
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# ── Execute command ──────────────────────────────────────────────────
|
||||
exec "$@"
|
||||
+40
-77
@@ -1,84 +1,47 @@
|
||||
#!/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 (bind mounts may have wrong perms)
|
||||
if [ -d "$HOME/.ssh" ] && [ "$(ls -A "$HOME/.ssh" 2>/dev/null)" ]; then
|
||||
chmod 700 "$HOME/.ssh"
|
||||
find "$HOME/.ssh" -type f -name "id_*" ! -name "*.pub" -exec chmod 600 {} \; 2>/dev/null || true
|
||||
find "$HOME/.ssh" -type f -name "*.pub" -exec chmod 644 {} \; 2>/dev/null || true
|
||||
[ -f "$HOME/.ssh/known_hosts" ] && chmod 644 "$HOME/.ssh/known_hosts"
|
||||
[ -f "$HOME/.ssh/config" ] && chmod 600 "$HOME/.ssh/config"
|
||||
if [ -d "/home/$USER_NAME/.ssh" ] && [ "$(ls -A "/home/$USER_NAME/.ssh" 2>/dev/null)" ]; then
|
||||
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
|
||||
|
||||
# ── Git config defaults ──────────────────────────────────────────────
|
||||
# Set git config from env vars if not already configured via mounted .gitconfig
|
||||
if [ -n "${GIT_USER_NAME:-}" ] && ! git config --global user.name &>/dev/null; then
|
||||
git config --global user.name "$GIT_USER_NAME"
|
||||
fi
|
||||
if [ -n "${GIT_USER_EMAIL:-}" ] && ! git config --global user.email &>/dev/null; then
|
||||
git config --global user.email "$GIT_USER_EMAIL"
|
||||
fi
|
||||
|
||||
# ── Generate opencode config from env vars if no config mounted ──────
|
||||
CONFIG_DIR="$HOME/.config/opencode"
|
||||
CONFIG_FILE="$CONFIG_DIR/opencode.json"
|
||||
|
||||
if [ ! -f "$CONFIG_FILE" ] && [ -n "${OPENCODE_PROVIDER:-}" ]; then
|
||||
echo "Generating opencode config for provider: $OPENCODE_PROVIDER"
|
||||
mkdir -p "$CONFIG_DIR"
|
||||
|
||||
# Build provider-specific config
|
||||
case "$OPENCODE_PROVIDER" in
|
||||
anthropic)
|
||||
cat > "$CONFIG_FILE" <<EOF
|
||||
{
|
||||
"\$schema": "https://opencode.ai/config.json",
|
||||
"model": "${OPENCODE_MODEL:-anthropic/claude-sonnet-4-5}",
|
||||
"share": "disabled",
|
||||
"autoupdate": false
|
||||
}
|
||||
EOF
|
||||
;;
|
||||
openai)
|
||||
cat > "$CONFIG_FILE" <<EOF
|
||||
{
|
||||
"\$schema": "https://opencode.ai/config.json",
|
||||
"model": "${OPENCODE_MODEL:-openai/gpt-4o}",
|
||||
"share": "disabled",
|
||||
"autoupdate": false
|
||||
}
|
||||
EOF
|
||||
;;
|
||||
amazon-bedrock)
|
||||
cat > "$CONFIG_FILE" <<EOF
|
||||
{
|
||||
"\$schema": "https://opencode.ai/config.json",
|
||||
"model": "${OPENCODE_MODEL:-amazon-bedrock/anthropic.claude-sonnet-4-5-v1}",
|
||||
"share": "disabled",
|
||||
"autoupdate": false,
|
||||
"provider": {
|
||||
"amazon-bedrock": {
|
||||
"options": {
|
||||
"region": "${AWS_REGION:-us-east-1}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
;;
|
||||
*)
|
||||
cat > "$CONFIG_FILE" <<EOF
|
||||
{
|
||||
"\$schema": "https://opencode.ai/config.json",
|
||||
"model": "${OPENCODE_MODEL:-anthropic/claude-sonnet-4-5}",
|
||||
"share": "disabled",
|
||||
"autoupdate": false
|
||||
}
|
||||
EOF
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# ── Execute command ──────────────────────────────────────────────────
|
||||
exec "$@"
|
||||
# ── Drop to developer user for remaining setup ──────────────────────
|
||||
exec gosu "$USER_NAME" /usr/local/bin/entrypoint-user.sh "$@"
|
||||
|
||||
Reference in New Issue
Block a user