mirror of
https://github.com/okxlin/release-factory.git
synced 2026-09-22 16:02:15 +00:00
257 lines
7.3 KiB
Bash
Executable File
257 lines
7.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
if [[ -z "${HOME:-}" ]]; then
|
|
runtime_home="$(getent passwd "$(id -u)" | cut -d: -f6 2>/dev/null || true)"
|
|
export HOME="${runtime_home}"
|
|
fi
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
OPENCODE_USER="${OPENCODE_USER:-opencode}"
|
|
OPENCODE_GROUP="${OPENCODE_GROUP:-$(id -gn "${OPENCODE_USER}" 2>/dev/null || printf '%s' "${OPENCODE_USER}")}"
|
|
OPENCODE_HOME_DIR="${OPENCODE_HOME_DIR:-/home/${OPENCODE_USER}}"
|
|
|
|
: "${OPENCODE_BOOTSTRAP:=1}"
|
|
: "${OMO_AUTO_INSTALL:=0}"
|
|
: "${ACP_AUTO_START:=0}"
|
|
: "${OPENCODE_RUNTIME_MODE:=acp}"
|
|
: "${CONTAINER_WORKSPACE:=/workspace}"
|
|
: "${CONTAINER_CACHE:=/cache}"
|
|
: "${KEEPALIVE_COMMAND:=sleep infinity}"
|
|
: "${OPENCODE_INSTALL_DIR:=${OPENCODE_HOME_DIR}/.local/share/opencode}"
|
|
: "${OPENCODE_NPM_BIN_DIR:=${OPENCODE_HOME_DIR}/.local/bin}"
|
|
: "${OPENCODE_CONFIG_DIR:=${OPENCODE_HOME_DIR}/.config/opencode}"
|
|
: "${OMO_INSTALL_DIR:=${OPENCODE_HOME_DIR}/.local/share/oh-my-opencode}"
|
|
|
|
export OPENCODE_CONFIG_DIR OPENCODE_INSTALL_DIR OPENCODE_NPM_BIN_DIR OMO_INSTALL_DIR
|
|
export HOME="${OPENCODE_HOME_DIR}"
|
|
export USER="${OPENCODE_USER}"
|
|
export PATH="/opt/bun/bin:/usr/local/go/bin:${HOME}/.cargo/bin:${OPENCODE_NPM_BIN_DIR}:${HOME}/.local/bin:${PATH:-/usr/local/bin:/usr/bin:/bin}"
|
|
|
|
log() {
|
|
printf '[entrypoint] %s\n' "$*"
|
|
}
|
|
|
|
ensure_owned_dir() {
|
|
local dir="$1"
|
|
if ! sudo install -d -o "${OPENCODE_USER}" -g "${OPENCODE_GROUP}" "${dir}"; then
|
|
log "failed to prepare ${dir}"
|
|
fi
|
|
}
|
|
|
|
configure_docker_socket_access() {
|
|
local socket="/var/run/docker.sock"
|
|
local socket_gid group_name
|
|
|
|
[[ -S "${socket}" ]] || return 0
|
|
|
|
socket_gid="$(stat -c '%g' "${socket}" 2>/dev/null || true)"
|
|
if [[ -z "${socket_gid}" ]]; then
|
|
log "failed to inspect ${socket}"
|
|
return 0
|
|
fi
|
|
|
|
if [[ "${socket_gid}" == "0" ]]; then
|
|
log "${socket} is owned by root group; use sudo docker inside the container"
|
|
return 0
|
|
fi
|
|
|
|
if id -G "${OPENCODE_USER}" | tr ' ' '\n' | grep -qx "${socket_gid}"; then
|
|
return 0
|
|
fi
|
|
|
|
group_name="$(getent group "${socket_gid}" | cut -d: -f1 || true)"
|
|
if [[ -z "${group_name}" ]]; then
|
|
group_name="docker-host"
|
|
if getent group "${group_name}" >/dev/null 2>&1; then
|
|
group_name="docker-host-${socket_gid}"
|
|
fi
|
|
if ! sudo groupadd -g "${socket_gid}" "${group_name}" 2>/dev/null; then
|
|
group_name="$(getent group "${socket_gid}" | cut -d: -f1 || true)"
|
|
fi
|
|
fi
|
|
|
|
if [[ -z "${group_name}" ]]; then
|
|
log "failed to create group for ${socket}; use sudo docker inside the container"
|
|
return 0
|
|
fi
|
|
|
|
if ! sudo usermod -aG "${group_name}" "${OPENCODE_USER}"; then
|
|
log "failed to add ${OPENCODE_USER} to ${group_name}; use sudo docker inside the container"
|
|
return 0
|
|
fi
|
|
|
|
export OPENCODE_GROUP_REFRESH_NEEDED=1
|
|
}
|
|
|
|
prepare_directories() {
|
|
for dir in \
|
|
"${CONTAINER_WORKSPACE}" \
|
|
"${CONTAINER_CACHE}" \
|
|
"$HOME/.config" \
|
|
"$HOME/.config/opencode" \
|
|
"$HOME/.agents" \
|
|
"$HOME/.cache" \
|
|
"$HOME/.cache/npm" \
|
|
"$HOME/.cargo" \
|
|
"$HOME/.cargo/bin" \
|
|
"$HOME/.claude" \
|
|
"$HOME/.opencode" \
|
|
"$HOME/.local" \
|
|
"$HOME/.local/bin" \
|
|
"$HOME/.local/share" \
|
|
"$HOME/.local/share/opencode" \
|
|
"$HOME/.local/share/oh-my-opencode" \
|
|
"$HOME/.npm"; do
|
|
ensure_owned_dir "$dir"
|
|
done
|
|
}
|
|
|
|
repair_recursive_ownership() {
|
|
if [[ "${FIX_WORKSPACE_OWNERSHIP_RECURSIVE:-false}" == "true" ]]; then
|
|
if [[ "${CONTAINER_WORKSPACE}" == "/workspace" ]]; then
|
|
sudo chown -R "${OPENCODE_USER}:${OPENCODE_GROUP}" "${CONTAINER_WORKSPACE}" || true
|
|
else
|
|
log "recursive workspace ownership repair is only allowed for /workspace; skipped ${CONTAINER_WORKSPACE}"
|
|
fi
|
|
fi
|
|
|
|
if [[ "${FIX_CACHE_OWNERSHIP_RECURSIVE:-false}" == "true" ]]; then
|
|
if [[ "${CONTAINER_CACHE}" == "/cache" ]]; then
|
|
sudo chown -R "${OPENCODE_USER}:${OPENCODE_GROUP}" "${CONTAINER_CACHE}" || true
|
|
else
|
|
log "recursive cache ownership repair is only allowed for /cache; skipped ${CONTAINER_CACHE}"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
drop_to_runtime_user() {
|
|
if [[ "$(id -u)" == "0" || "${OPENCODE_GROUP_REFRESH_NEEDED:-0}" == "1" ]]; then
|
|
if [[ "${OPENCODE_RUNTIME_USER_READY:-0}" != "1" ]]; then
|
|
export OPENCODE_RUNTIME_USER_READY=1
|
|
exec sudo -E -u "${OPENCODE_USER}" -g "${OPENCODE_GROUP}" \
|
|
env HOME="${OPENCODE_HOME_DIR}" USER="${OPENCODE_USER}" "$0" "$@"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
prepare_directories
|
|
configure_docker_socket_access
|
|
repair_recursive_ownership
|
|
drop_to_runtime_user "$@"
|
|
|
|
log "migrating deprecated OpenCode configuration"
|
|
python3 "${SCRIPT_DIR}/update_opencode_config.py" migrate-deprecated
|
|
|
|
ensure_omo_storage() {
|
|
local persistent_omo_dir="${HOME}/.config/.omo"
|
|
local legacy_omo_dir="${HOME}/.omo"
|
|
local backup_dir
|
|
|
|
mkdir -p "${persistent_omo_dir}"
|
|
|
|
if [[ -L "${legacy_omo_dir}" ]]; then
|
|
if [[ "$(readlink -f "${legacy_omo_dir}" 2>/dev/null || true)" == "$(readlink -f "${persistent_omo_dir}")" ]]; then
|
|
return 0
|
|
fi
|
|
log "leaving custom ${legacy_omo_dir} symlink unchanged"
|
|
return 0
|
|
fi
|
|
|
|
if [[ -d "${legacy_omo_dir}" ]]; then
|
|
log "migrating legacy OMO data into persistent config storage"
|
|
cp -a -n "${legacy_omo_dir}/." "${persistent_omo_dir}/"
|
|
backup_dir="${HOME}/.omo.migrated.$(date +%s%N)"
|
|
mv "${legacy_omo_dir}" "${backup_dir}"
|
|
log "legacy OMO data preserved at ${backup_dir}"
|
|
elif [[ -e "${legacy_omo_dir}" ]]; then
|
|
log "cannot replace non-directory ${legacy_omo_dir}; leaving it unchanged"
|
|
return 0
|
|
fi
|
|
|
|
ln -s "${persistent_omo_dir}" "${legacy_omo_dir}"
|
|
}
|
|
|
|
ensure_omo_storage
|
|
|
|
sample_user_config="$HOME/.config/opencode/opencode.user.sample.json"
|
|
if [[ ! -f "${sample_user_config}" ]]; then
|
|
cat > "${sample_user_config}" <<'EOF'
|
|
{
|
|
"plugin": [
|
|
"your-plugin-name"
|
|
],
|
|
"provider": {
|
|
"mimo": {
|
|
"npm": "@ai-sdk/openai-compatible",
|
|
"name": "Mimo",
|
|
"options": {
|
|
"baseURL": "{env:OPENAI_BASE_URL}",
|
|
"apiKey": "{env:OPENAI_API_KEY}"
|
|
},
|
|
"models": {
|
|
"mimo-v2.5": {
|
|
"name": "mimo-v2.5"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
EOF
|
|
fi
|
|
|
|
for dir in \
|
|
"${CONTAINER_WORKSPACE}" \
|
|
"${CONTAINER_CACHE}" \
|
|
"$HOME/.config" \
|
|
"$HOME/.agents" \
|
|
"$HOME/.claude" \
|
|
"$HOME/.opencode" \
|
|
"$HOME/.local/share"; do
|
|
if [[ -d "$dir" && ! -w "$dir" ]]; then
|
|
log "mount not writable for current user: $dir"
|
|
fi
|
|
done
|
|
|
|
if [[ "${OPENCODE_BOOTSTRAP}" == "1" ]]; then
|
|
log "bootstrapping OpenCode userland"
|
|
"${SCRIPT_DIR}/bootstrap-opencode-userland.sh"
|
|
log "installing OpenCode plugins and config hooks"
|
|
"${SCRIPT_DIR}/install-opencode-plugins.sh"
|
|
fi
|
|
|
|
if [[ "${OMO_AUTO_INSTALL}" == "1" ]]; then
|
|
log "installing or refreshing oh-my-opencode"
|
|
"${SCRIPT_DIR}/install-oh-my-opencode.sh"
|
|
fi
|
|
|
|
log "runtime versions"
|
|
if command -v node >/dev/null 2>&1; then node --version; fi
|
|
if command -v npm >/dev/null 2>&1; then npm --version; fi
|
|
if command -v bun >/dev/null 2>&1; then bun --version; fi
|
|
if command -v opencode >/dev/null 2>&1; then opencode --version; fi
|
|
|
|
if [[ "${ACP_AUTO_START}" == "1" ]]; then
|
|
log "starting OpenCode runtime in background (mode=${OPENCODE_RUNTIME_MODE})"
|
|
"${SCRIPT_DIR}/start-opencode-runtime.sh" &
|
|
fi
|
|
|
|
case "${1:-shell}" in
|
|
shell)
|
|
log "keeping container alive via: ${KEEPALIVE_COMMAND}"
|
|
exec bash -lc "${KEEPALIVE_COMMAND}"
|
|
;;
|
|
doctor)
|
|
exec "${SCRIPT_DIR}/doctor.sh"
|
|
;;
|
|
smoke)
|
|
exec "${SCRIPT_DIR}/smoke-test.sh"
|
|
;;
|
|
runtime)
|
|
exec "${SCRIPT_DIR}/start-opencode-runtime.sh"
|
|
;;
|
|
*)
|
|
exec "$@"
|
|
;;
|
|
esac
|