|
| 1 | +import type { TemplateConfig } from "../domain.js" |
| 2 | + |
| 3 | +// CHANGE: bootstrap OpenCode config (permissions + plugins) and share OpenCode auth.json across projects |
| 4 | +// WHY: make OpenCode usable out-of-the-box inside disposable docker-git containers |
| 5 | +// QUOTE(ТЗ): "Preinstall OpenCode and oh-my-opencode with full authorization of existing tools" |
| 6 | +// REF: issue-34 |
| 7 | +// SOURCE: n/a |
| 8 | +// FORMAT THEOREM: forall s: start(s) -> config_exists(s) |
| 9 | +// PURITY: CORE |
| 10 | +// INVARIANT: never overwrites an existing opencode.json/opencode.jsonc |
| 11 | +// COMPLEXITY: O(1) |
| 12 | +export const renderEntrypointOpenCodeConfig = (config: TemplateConfig): string => |
| 13 | + `# OpenCode: share auth.json across projects (so /connect is one-time) |
| 14 | +OPENCODE_SHARE_AUTH="\${OPENCODE_SHARE_AUTH:-1}" |
| 15 | +if [[ "$OPENCODE_SHARE_AUTH" == "1" ]]; then |
| 16 | + OPENCODE_DATA_DIR="/home/${config.sshUser}/.local/share/opencode" |
| 17 | + OPENCODE_AUTH_FILE="$OPENCODE_DATA_DIR/auth.json" |
| 18 | +
|
| 19 | + # Store in the shared auth volume to persist across projects/containers. |
| 20 | + OPENCODE_SHARED_HOME="${config.codexHome}-shared/opencode" |
| 21 | + OPENCODE_SHARED_AUTH_FILE="$OPENCODE_SHARED_HOME/auth.json" |
| 22 | +
|
| 23 | + mkdir -p "$OPENCODE_DATA_DIR" "$OPENCODE_SHARED_HOME" |
| 24 | + chown -R 1000:1000 "$OPENCODE_DATA_DIR" "$OPENCODE_SHARED_HOME" || true |
| 25 | +
|
| 26 | + # Guard against a bad bind mount creating a directory at auth.json. |
| 27 | + if [[ -d "$OPENCODE_AUTH_FILE" ]]; then |
| 28 | + mv "$OPENCODE_AUTH_FILE" "$OPENCODE_AUTH_FILE.bak-$(date +%s)" || true |
| 29 | + fi |
| 30 | +
|
| 31 | + # Migrate existing per-project auth into the shared location once. |
| 32 | + if [[ -f "$OPENCODE_AUTH_FILE" && ! -L "$OPENCODE_AUTH_FILE" ]]; then |
| 33 | + if [[ -f "$OPENCODE_SHARED_AUTH_FILE" ]]; then |
| 34 | + LOCAL_AUTH="$OPENCODE_AUTH_FILE" SHARED_AUTH="$OPENCODE_SHARED_AUTH_FILE" node - <<'NODE' |
| 35 | +const fs = require("fs") |
| 36 | +const localPath = process.env.LOCAL_AUTH |
| 37 | +const sharedPath = process.env.SHARED_AUTH |
| 38 | +const readJson = (p) => { |
| 39 | + try { |
| 40 | + return JSON.parse(fs.readFileSync(p, "utf8")) |
| 41 | + } catch { |
| 42 | + return {} |
| 43 | + } |
| 44 | +} |
| 45 | +const local = readJson(localPath) |
| 46 | +const shared = readJson(sharedPath) |
| 47 | +const merged = { ...local, ...shared } // shared wins on conflicts |
| 48 | +fs.writeFileSync(sharedPath, JSON.stringify(merged, null, 2), { mode: 0o600 }) |
| 49 | +NODE |
| 50 | + else |
| 51 | + cp "$OPENCODE_AUTH_FILE" "$OPENCODE_SHARED_AUTH_FILE" || true |
| 52 | + chmod 600 "$OPENCODE_SHARED_AUTH_FILE" || true |
| 53 | + fi |
| 54 | + chown 1000:1000 "$OPENCODE_SHARED_AUTH_FILE" || true |
| 55 | + rm -f "$OPENCODE_AUTH_FILE" || true |
| 56 | + fi |
| 57 | +
|
| 58 | + ln -sf "$OPENCODE_SHARED_AUTH_FILE" "$OPENCODE_AUTH_FILE" |
| 59 | +fi |
| 60 | +
|
| 61 | +# OpenCode: ensure global config exists (plugins + permissions) |
| 62 | +OPENCODE_CONFIG_DIR="/home/${config.sshUser}/.config/opencode" |
| 63 | +OPENCODE_CONFIG_JSON="$OPENCODE_CONFIG_DIR/opencode.json" |
| 64 | +OPENCODE_CONFIG_JSONC="$OPENCODE_CONFIG_DIR/opencode.jsonc" |
| 65 | +
|
| 66 | +mkdir -p "$OPENCODE_CONFIG_DIR" |
| 67 | +chown -R 1000:1000 "$OPENCODE_CONFIG_DIR" || true |
| 68 | +
|
| 69 | +if [[ ! -f "$OPENCODE_CONFIG_JSON" && ! -f "$OPENCODE_CONFIG_JSONC" ]]; then |
| 70 | + cat <<'EOF' > "$OPENCODE_CONFIG_JSON" |
| 71 | +{ |
| 72 | + "$schema": "https://opencode.ai/config.json", |
| 73 | + "plugin": ["oh-my-opencode"], |
| 74 | + "permission": { |
| 75 | + "doom_loop": "allow", |
| 76 | + "external_directory": "allow", |
| 77 | + "read": { |
| 78 | + "*": "allow", |
| 79 | + "*.env": "allow", |
| 80 | + "*.env.*": "allow", |
| 81 | + "*.env.example": "allow" |
| 82 | + } |
| 83 | + } |
| 84 | +} |
| 85 | +EOF |
| 86 | + chown 1000:1000 "$OPENCODE_CONFIG_JSON" || true |
| 87 | +fi` |
0 commit comments