99 lines
4.4 KiB
Bash
Executable File
99 lines
4.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#ddev-generated
|
|
set -euo pipefail
|
|
|
|
# All Claude Code state is stored under .ddev/claude-code so it survives
|
|
# container rebuilds without being committed to the project repository.
|
|
PERSIST_BASE="${DDEV_APPROOT}/.ddev/claude-code"
|
|
PERSIST_HOME="${PERSIST_BASE}/home" # replaces $HOME inside the container
|
|
PERSIST_CONFIG_HOME="${PERSIST_BASE}/config" # XDG_CONFIG_HOME (e.g. ~/.config)
|
|
PERSIST_DATA_HOME="${PERSIST_BASE}/data" # XDG_DATA_HOME (e.g. ~/.local/share)
|
|
PERSIST_STATE_HOME="${PERSIST_BASE}/state" # XDG_STATE_HOME (e.g. ~/.local/state)
|
|
PERSIST_BIN_DIR="${PERSIST_HOME}/.local/bin" # Claude native installer target
|
|
PERSIST_BIN="${PERSIST_BIN_DIR}/claude" # full path to the claude executable
|
|
PERSIST_GITIGNORE_GLOBAL="${PERSIST_HOME}/.gitignore_global" # mirrors $HOME/.gitignore_global
|
|
|
|
# Capture the container's original HOME before we override it below, so we can
|
|
# seed the persisted gitignore from the container image on first run.
|
|
CONTAINER_HOME="${HOME:?HOME must be set}"
|
|
CONTAINER_GITIGNORE_GLOBAL="${CONTAINER_HOME}/.gitignore_global"
|
|
|
|
# Ensure all persistence directories exist.
|
|
mkdir -p \
|
|
"${PERSIST_HOME}" \
|
|
"${PERSIST_CONFIG_HOME}" \
|
|
"${PERSIST_DATA_HOME}" \
|
|
"${PERSIST_STATE_HOME}" \
|
|
"${PERSIST_BIN_DIR}"
|
|
|
|
# Seed the persisted gitignore from the container image on first run only.
|
|
if [ -f "${CONTAINER_GITIGNORE_GLOBAL}" ] && [ ! -f "${PERSIST_GITIGNORE_GLOBAL}" ]; then
|
|
cp -f "${CONTAINER_GITIGNORE_GLOBAL}" "${PERSIST_GITIGNORE_GLOBAL}"
|
|
fi
|
|
|
|
# Claude Code's native installer stores its launcher, auth, settings, downloads,
|
|
# and versioned package data relative to HOME/XDG paths.
|
|
export HOME="${PERSIST_HOME}"
|
|
export XDG_CONFIG_HOME="${PERSIST_CONFIG_HOME}"
|
|
export XDG_DATA_HOME="${PERSIST_DATA_HOME}"
|
|
export XDG_STATE_HOME="${PERSIST_STATE_HOME}"
|
|
export PATH="${PERSIST_BIN_DIR}:${PATH}"
|
|
|
|
# If a persisted binary exists but fails to run (e.g. wrong architecture after
|
|
# a host OS change), remove it so the install step below fetches a fresh copy.
|
|
if [ -x "${PERSIST_BIN}" ] && ! "${PERSIST_BIN}" --version >/dev/null 2>&1; then
|
|
echo "Persisted Claude Code binary is not runnable, reinstalling ..."
|
|
rm -f "${PERSIST_BIN}"
|
|
fi
|
|
|
|
# Download and install the current stable Claude Code native build only when
|
|
# missing. Use "ddev claude update" to check for and install newer versions.
|
|
if [ ! -x "${PERSIST_BIN}" ]; then
|
|
echo "Installing Claude Code CLI ..."
|
|
curl -fsSL https://claude.ai/install.sh | bash -s stable
|
|
fi
|
|
|
|
# Verify the binary is present and runnable after installation.
|
|
if [ ! -x "${PERSIST_BIN}" ]; then
|
|
echo "Claude Code installation completed but no executable was found at ${PERSIST_BIN}." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if ! "${PERSIST_BIN}" --version >/dev/null 2>&1; then
|
|
echo "Claude Code installation completed but ${PERSIST_BIN} is not runnable." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Point git's global excludes file at the persisted copy when available,
|
|
# so git inside the container respects user-defined ignore patterns.
|
|
if command -v git >/dev/null 2>&1 && [ -f "${PERSIST_GITIGNORE_GLOBAL}" ]; then
|
|
CURRENT_EXCLUDESFILE="$(git config --global --get core.excludesfile || true)"
|
|
if [ "${CURRENT_EXCLUDESFILE}" != "${PERSIST_GITIGNORE_GLOBAL}" ]; then
|
|
git config --global core.excludesfile "${PERSIST_GITIGNORE_GLOBAL}"
|
|
fi
|
|
fi
|
|
|
|
# Keep the add-on's own artifacts out of this project's git, via a managed block in
|
|
# .git/info/exclude. Runs every time so the list follows the add-on, not a file that
|
|
# was copied once. Never fatal: a project need not be a git checkout.
|
|
if [ -x "${PERSIST_BASE}/git-exclude.sh" ]; then
|
|
"${PERSIST_BASE}/git-exclude.sh" || true
|
|
fi
|
|
|
|
# Headless browser, opt-in via "ddev claude-browser enable". Registers or removes the
|
|
# Playwright MCP server to match; local and quick, never fatal.
|
|
if [ -x "${PERSIST_BASE}/browser-setup.sh" ]; then
|
|
"${PERSIST_BASE}/browser-setup.sh" || true
|
|
fi
|
|
|
|
# Sibling-repo mirrors, opt-in via .ddev/claude-refs.txt. Setup is local and quick;
|
|
# the sync runs detached so neither ddev start nor ddev claude waits on the network,
|
|
# and refs-sync.sh throttles itself.
|
|
REFS_SYNC="${PERSIST_BASE}/refs-sync.sh"
|
|
if [ -f "${DDEV_APPROOT}/.ddev/claude-refs.txt" ] && [ -x "${REFS_SYNC}" ]; then
|
|
"${REFS_SYNC}" --setup-only || echo "claude-refs: setup failed, continuing" >&2
|
|
CLAUDE_REFS_DETACHED=1 setsid nohup "${REFS_SYNC}" >/dev/null 2>&1 </dev/null &
|
|
fi
|
|
|
|
echo "Claude Code version: $("${PERSIST_BIN}" --version)"
|