Infrastructure pass: CI smoke tests, floating versions, chown sentinel, generate-config script
Main changes: - Extract opencode.json generation from entrypoint-user.sh into a standalone Python script (rootfs/usr/local/lib/opencode-devbox/ generate-config.py). Preserves the never-overwrite-existing-config guarantee. Cuts entrypoint-user.sh from 176 to 97 lines. - Install MemPalace via 'uv tool install' into an isolated venv at /opt/uv-tools/mempalace/ with a /usr/local/bin/mempalace-mcp-server wrapper, replacing the 'pip install --break-system-packages' escape hatch. The wrapper is what generate-config.py references in the auto-generated opencode.json. Also fix 'mempalace init' in entrypoint-user.sh to use --yes so first-start initialization isn't interactive (this used to hang or print prompts into the user's terminal). Gated by INSTALL_MEMPALACE build arg (default true) so users who don't need AI memory can shave ~300 MB. - Sentinel-file pattern in entrypoint.sh volume-ownership loop: write .devbox-owner after a successful chown -R, skip the recursive walk on subsequent starts when the sentinel matches FINAL_UID:FINAL_GID. Cuts multi-second startup costs to milliseconds on large volumes (nvim plugins, palace data). UID changes still trigger a full chown. - Float all GitHub/Gitea-hosted binary versions: gosu, fzf, git-lfs, neovim, bat, eza, zoxide, uv, gitea-mcp now default to 'latest' and resolve the newest upstream release at build time via the /releases/ latest redirect. Go (go.dev JSON feed) and oh-my-opencode-slim (npm @latest) likewise. Intentional pins still in place: OPENCODE_VERSION, NODE_VERSION=22, DEBIAN_VERSION=trixie-slim. Each *_VERSION ARG accepts an explicit value to lock a specific version when needed. - New scripts/smoke-test.sh verifies binary presence, opencode startup, entrypoint user drop, generate-config idempotency, bun's presence- per-variant, and image size against thresholds (2500 MB base, 3000 MB OMOS). Prints resolved component versions as its first step so CI logs always record what got baked into a given image. - New .gitea/workflows/validate.yml runs on push to main and PRs: single-arch amd64 build, smoke test, DOCKER_HUB.md sync check. Tag- triggered docker-publish.yml now smoke-tests each variant on amd64 before the full multi-arch push. - scripts/generate-dockerhub-md.py auto-generates DOCKER_HUB.md from README.md using explicit SECTION_RULES. --check mode fails CI when the committed file is out of sync. Enforces the 25 kB Docker Hub limit. Adding a new README section forces an explicit keep/drop/ replace decision. - Remove dead INSTALL_PYTHON build arg (was a no-op since mempalace added python3 unconditionally).
This commit is contained in:
+13
-89
@@ -17,12 +17,15 @@ fi
|
||||
|
||||
# ── MemPalace: initialize palace for the workspace if mempalace is installed
|
||||
# Creates the palace directory structure on first run. Idempotent — skips
|
||||
# if palace already exists. Uses the workspace path to derive the wing name.
|
||||
# if palace already exists, so upgrades from older versions preserve
|
||||
# existing data. `--yes` auto-accepts detected entities so the init is
|
||||
# non-interactive — the container entrypoint has no usable stdin for
|
||||
# prompts anyway.
|
||||
if command -v mempalace &>/dev/null && [ -d /workspace ]; then
|
||||
PALACE_DIR="${HOME}/.mempalace"
|
||||
if [ ! -d "$PALACE_DIR/palace" ]; then
|
||||
echo "Initializing MemPalace for workspace..."
|
||||
mempalace init /workspace 2>/dev/null || true
|
||||
echo "Initializing MemPalace for workspace (non-interactive)..."
|
||||
mempalace init --yes /workspace >/dev/null 2>&1 || true
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -35,93 +38,14 @@ if [ -n "${GIT_USER_EMAIL:-}" ] && ! git config --global user.email &>/dev/null;
|
||||
fi
|
||||
|
||||
# ── Generate opencode config from env vars if no config mounted ──────
|
||||
# Delegated to a standalone Python script for clarity and testability.
|
||||
# The script is idempotent: it never overwrites an existing opencode.json
|
||||
# (bind-mounted from host, persisted in named volume, or previously
|
||||
# generated) and no-ops if OPENCODE_PROVIDER is unset.
|
||||
python3 /usr/local/lib/opencode-devbox/generate-config.py
|
||||
|
||||
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-6}",
|
||||
"share": "disabled",
|
||||
"autoupdate": false
|
||||
}
|
||||
EOF
|
||||
;;
|
||||
openai)
|
||||
cat > "$CONFIG_FILE" <<EOF
|
||||
{
|
||||
"\$schema": "https://opencode.ai/config.json",
|
||||
"model": "${OPENCODE_MODEL:-openai/gpt-5.4}",
|
||||
"share": "disabled",
|
||||
"autoupdate": false
|
||||
}
|
||||
EOF
|
||||
;;
|
||||
amazon-bedrock)
|
||||
cat > "$CONFIG_FILE" <<EOF
|
||||
{
|
||||
"\$schema": "https://opencode.ai/config.json",
|
||||
"model": "${OPENCODE_MODEL:-amazon-bedrock/global.anthropic.claude-sonnet-4-5-20250929-v1:0}",
|
||||
"share": "disabled",
|
||||
"autoupdate": false,
|
||||
"provider": {
|
||||
"amazon-bedrock": {
|
||||
"options": {
|
||||
"region": "${AWS_REGION:-us-east-1}",
|
||||
"profile": "${AWS_PROFILE:-default}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
;;
|
||||
*)
|
||||
cat > "$CONFIG_FILE" <<EOF
|
||||
{
|
||||
"\$schema": "https://opencode.ai/config.json",
|
||||
"model": "${OPENCODE_MODEL:-anthropic/claude-sonnet-4-6}",
|
||||
"share": "disabled",
|
||||
"autoupdate": false
|
||||
}
|
||||
EOF
|
||||
;;
|
||||
esac
|
||||
|
||||
# Add MCP servers for installed tools
|
||||
if command -v python3 &>/dev/null; then
|
||||
python3 -c "
|
||||
import json, shutil
|
||||
with open('$CONFIG_FILE') as f:
|
||||
config = json.load(f)
|
||||
mcp = config.setdefault('mcp', {})
|
||||
|
||||
# MemPalace — local AI memory (if installed)
|
||||
if shutil.which('mempalace'):
|
||||
mcp['mempalace'] = {
|
||||
'type': 'local',
|
||||
'command': ['python3', '-m', 'mempalace.mcp_server']
|
||||
}
|
||||
|
||||
# Gitea — self-hosted Git forge API (if installed)
|
||||
if shutil.which('gitea-mcp'):
|
||||
mcp['gitea'] = {
|
||||
'type': 'local',
|
||||
'command': ['gitea-mcp', '-t', 'stdio'],
|
||||
'enabled': False
|
||||
}
|
||||
|
||||
with open('$CONFIG_FILE', 'w') as f:
|
||||
json.dump(config, f, indent=2)
|
||||
f.write('\n')
|
||||
" 2>/dev/null && echo "MCP servers registered in opencode config."
|
||||
fi
|
||||
fi
|
||||
OMOS_CONFIG="$CONFIG_DIR/oh-my-opencode-slim.json"
|
||||
|
||||
# ── oh-my-opencode-slim setup (multi-agent orchestration) ────────────
|
||||
# Activated by ENABLE_OMOS=true. Requires the image to be built with
|
||||
|
||||
Reference in New Issue
Block a user