molecule-core/workspace-template/entrypoint.sh
Molecule AI DevOps Engineer c509eca31d fix(template): copy molecule-git-token-helper.sh into image and fix path
Two bugs prevented the git credential helper (merged in #567) from ever
running at workspace boot:

1. Dockerfile never COPY'd scripts/molecule-git-token-helper.sh into the
   image — only gh-wrapper.sh was copied from scripts/. Result: the helper
   binary did not exist in any built container image.

2. entrypoint.sh looked for the helper at /workspace-template/scripts/...
   but /workspace-template/ is not a path that exists inside the container
   (WORKDIR is /app, no /workspace-template mount). The `if [ -f ... ]`
   guard silently fell through to the WARNING branch on every boot since
   #567 merged — the helper was never registered.

Fix:
- Add `COPY scripts/molecule-git-token-helper.sh ./scripts/` to Dockerfile
  so the script lands at /app/scripts/ in the image (matching WORKDIR /app)
- Update HELPER_SCRIPT path in entrypoint.sh from
  /workspace-template/scripts/... to /app/scripts/...

After this fix, every workspace container registers the helper at boot via:
  git config --global credential.https://github.com.helper \
    "!/app/scripts/molecule-git-token-helper.sh"

Closes #613.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 06:27:08 +00:00

88 lines
4.6 KiB
Bash

#!/bin/bash
# No set -e — individual commands handle their own errors gracefully
# ──────────────────────────────────────────────────────────
# Volume ownership fix (runs as root)
# ──────────────────────────────────────────────────────────
# Docker creates volume contents as root. The agent process runs as UID 1000
# and needs to write to /configs (CLAUDE.md, skills, plugins) and /workspace
# (cloned repos, scratch files). Fix ownership once at startup so every
# future file operation works without per-file chown hacks.
if [ "$(id -u)" = "0" ]; then
# Fix /configs recursively (plugins, CLAUDE.md, skills — small directory)
chown -R agent:agent /configs 2>/dev/null
# /workspace handling:
# - Always fix the top-level dir so agent can create files in it.
# - If the contents are root-owned (common on Docker Desktop / Windows
# bind mounts where host uid maps to 0 inside the container), do a
# full recursive chown — otherwise git clone, pip install, and file
# writes under /workspace fail with EACCES (issue #13). On normal
# Linux Docker with matching uids this branch is skipped, so we keep
# the fast startup for the common case.
chown agent:agent /workspace 2>/dev/null
if [ -d /workspace ]; then
# Sample the first entry inside /workspace; if it's root-owned assume
# the whole tree is a root-owned bind mount and recursively chown.
first_entry=$(find /workspace -mindepth 1 -maxdepth 1 -print -quit 2>/dev/null)
if [ -n "$first_entry" ] && [ "$(stat -c '%u' "$first_entry" 2>/dev/null)" = "0" ]; then
echo "[entrypoint] /workspace contents are root-owned — chowning recursively to agent (uid 1000)"
chown -R agent:agent /workspace 2>/dev/null
fi
fi
# Re-exec this script as the agent user via gosu (clean PID 1 handoff)
exec gosu agent "$0" "$@"
fi
# ──────────────────────────────────────────────────────────
# Everything below runs as the agent user (UID 1000)
# ──────────────────────────────────────────────────────────
# Ensure user-installed packages are in PATH
export PATH="$HOME/.local/bin:$PATH"
# Determine runtime from config.yaml
RUNTIME=$(python3 -c "
import yaml
from pathlib import Path
cfg_path = Path('/configs/config.yaml')
if cfg_path.exists():
cfg = yaml.safe_load(cfg_path.read_text()) or {}
print(cfg.get('runtime', 'langgraph'))
else:
print('langgraph')
" 2>/dev/null || echo "langgraph")
echo "=== Molecule AI Workspace ==="
echo "Runtime: $RUNTIME"
# ──────────────────────────────────────────────────────────
# GitHub credential helper — issue #547
# ──────────────────────────────────────────────────────────
# GitHub App installation tokens expire after ~60 min. The platform
# exposes GET /admin/github-installation-token (backed by the plugin's
# in-process refreshing cache) so workspaces can always get a valid
# token without restarting.
#
# Register molecule-git-token-helper.sh as the git credential helper for
# github.com. git calls it on every push/fetch; it hits the platform
# endpoint and emits a fresh token. Falls through to any existing
# credential helper (e.g. operator .env PAT) if the platform is
# unreachable.
#
# Idempotent — safe to re-run on restart.
HELPER_SCRIPT="/app/scripts/molecule-git-token-helper.sh"
if [ -f "${HELPER_SCRIPT}" ]; then
git config --global \
"credential.https://github.com.helper" \
"!${HELPER_SCRIPT}" 2>/dev/null || true
echo "[entrypoint] git credential helper registered (molecule-git-token-helper)"
else
echo "[entrypoint] WARNING: molecule-git-token-helper.sh not found at ${HELPER_SCRIPT} — GitHub tokens may expire after 60 min"
fi
# NOTE: Adapter-specific deps are now pre-installed in each adapter's Docker image
# (standalone template repos). Each image installs molecule-ai-workspace-runtime
# from PyPI plus the adapter-specific requirements. No per-runtime pip install needed here.
exec python3 main.py