191 lines
6.3 KiB
Bash
Executable File
191 lines
6.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# bootstrap-dev-env - provider-agnostic Kandev development environment setup.
|
|
#
|
|
# Cloud executor prepare scripts should handle provider concerns first:
|
|
# authentication, clone/checkout, workspace placement, and agentctl startup.
|
|
# After cloning, call this script from the repo root to install the Kandev
|
|
# toolchain and dependencies consistently across Sprites, Daytona, Modal,
|
|
# Codespaces, local VMs, and similar environments.
|
|
#
|
|
# Usage:
|
|
# scripts/bootstrap-dev-env [options]
|
|
#
|
|
# Options:
|
|
# --with-e2e Install Playwright browser/system deps for E2E
|
|
# --without-hooks Skip pre-commit hook wiring
|
|
# --without-node-modules Skip pnpm install
|
|
# --without-os-packages Skip best-effort OS package installation
|
|
# --without-mise-install Require mise to already be installed
|
|
# --provider NAME Record caller/provider name in logs only
|
|
# -h, --help Show this help
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
|
|
WITH_E2E=0
|
|
WITH_HOOKS=1
|
|
WITH_NODE_MODULES=1
|
|
WITH_OS_PACKAGES=1
|
|
WITH_MISE_INSTALL=1
|
|
PROVIDER="generic"
|
|
|
|
usage() {
|
|
awk 'NR > 1 { if ($0 !~ /^#/) exit; sub(/^# ?/, ""); print }' "$0"
|
|
exit "${1:-2}"
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--with-e2e) WITH_E2E=1; shift ;;
|
|
--without-hooks) WITH_HOOKS=0; shift ;;
|
|
--without-node-modules) WITH_NODE_MODULES=0; shift ;;
|
|
--without-os-packages) WITH_OS_PACKAGES=0; shift ;;
|
|
--without-mise-install) WITH_MISE_INSTALL=0; shift ;;
|
|
--provider) PROVIDER="${2:?--provider requires a value}"; shift 2 ;;
|
|
-h|--help) usage 0 ;;
|
|
*) echo "bootstrap-dev-env: unknown argument '$1'" >&2; usage 2 ;;
|
|
esac
|
|
done
|
|
|
|
log() { printf '[bootstrap:%s] %s\n' "$PROVIDER" "$*" >&2; }
|
|
warn() { printf '[bootstrap:%s] warning: %s\n' "$PROVIDER" "$*" >&2; }
|
|
die() { printf '[bootstrap:%s] error: %s\n' "$PROVIDER" "$*" >&2; exit 1; }
|
|
|
|
run_privileged() {
|
|
if [[ "${EUID:-$(id -u)}" -eq 0 ]]; then
|
|
"$@"
|
|
elif command -v sudo >/dev/null 2>&1; then
|
|
sudo "$@"
|
|
else
|
|
return 127
|
|
fi
|
|
}
|
|
|
|
install_os_packages() {
|
|
[[ "$WITH_OS_PACKAGES" -eq 1 ]] || { log "skipping OS packages"; return 0; }
|
|
|
|
if command -v apt-get >/dev/null 2>&1; then
|
|
log "installing OS packages with apt-get"
|
|
if ! run_privileged apt-get update; then
|
|
warn "apt-get requires root/sudo; skipping OS package installation"
|
|
return 0
|
|
fi
|
|
run_privileged env DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
|
bash build-essential ca-certificates curl file git gzip lsof make openssh-client \
|
|
pipx pkg-config python3 python3-pip rsync sqlite3 tar unzip xz-utils zip \
|
|
libsqlite3-dev || warn "apt-get package installation failed; continuing because packages may already be present"
|
|
return 0
|
|
fi
|
|
|
|
if command -v dnf >/dev/null 2>&1; then
|
|
log "installing OS packages with dnf"
|
|
run_privileged dnf install -y \
|
|
bash ca-certificates curl file gcc gcc-c++ git gzip lsof make openssh-clients \
|
|
pipx pkgconf-pkg-config python3 python3-pip rsync sqlite sqlite-devel tar unzip xz zip \
|
|
|| warn "dnf package installation failed; continuing because packages may already be present"
|
|
return 0
|
|
fi
|
|
|
|
if command -v apk >/dev/null 2>&1; then
|
|
log "installing OS packages with apk"
|
|
run_privileged apk add --no-cache \
|
|
bash build-base ca-certificates curl file git gzip lsof make openssh-client \
|
|
pipx pkgconf python3 py3-pip rsync sqlite sqlite-dev tar unzip xz zip \
|
|
|| warn "apk package installation failed; continuing because packages may already be present"
|
|
return 0
|
|
fi
|
|
|
|
if command -v brew >/dev/null 2>&1; then
|
|
log "installing OS packages with Homebrew"
|
|
brew install bash ca-certificates curl file git lsof make openssh pipx pkg-config \
|
|
python rsync sqlite unzip xz zip \
|
|
|| warn "Homebrew package installation failed; continuing because packages may already be present"
|
|
return 0
|
|
fi
|
|
|
|
warn "no supported package manager found; install build tools, sqlite headers, unzip, pipx, and OpenSSH manually if needed"
|
|
}
|
|
|
|
install_mise() {
|
|
if command -v mise >/dev/null 2>&1; then
|
|
log "mise already installed: $(mise --version 2>/dev/null || true)"
|
|
persist_mise_activation
|
|
return 0
|
|
fi
|
|
|
|
[[ "$WITH_MISE_INSTALL" -eq 1 ]] || die "mise is not installed and --without-mise-install was passed"
|
|
command -v curl >/dev/null 2>&1 || die "curl is required to install mise"
|
|
|
|
log "installing mise"
|
|
tmp_mise_script="$(mktemp)"
|
|
trap 'rm -f "$tmp_mise_script"' EXIT
|
|
curl -fsSL https://mise.run -o "$tmp_mise_script"
|
|
sh "$tmp_mise_script"
|
|
export PATH="$HOME/.local/bin:$PATH"
|
|
command -v mise >/dev/null 2>&1 || die "mise install finished but mise is not on PATH"
|
|
persist_mise_activation
|
|
}
|
|
|
|
persist_mise_activation() {
|
|
[[ -n "${HOME:-}" ]] || return 0
|
|
local bashrc="$HOME/.bashrc"
|
|
|
|
touch "$bashrc"
|
|
grep -Fqx 'export PATH="$HOME/.local/bin:$PATH"' "$bashrc" 2>/dev/null \
|
|
|| printf '\nexport PATH="$HOME/.local/bin:$PATH"\n' >> "$bashrc"
|
|
grep -Fqx 'eval "$(mise activate bash)"' "$bashrc" 2>/dev/null \
|
|
|| printf 'eval "$(mise activate bash)"\n' >> "$bashrc"
|
|
}
|
|
|
|
activate_mise() {
|
|
export PATH="$HOME/.local/bin:$PATH"
|
|
cd "$REPO_ROOT"
|
|
|
|
[[ -f "$REPO_ROOT/mise.toml" ]] || die "missing mise.toml at repo root"
|
|
|
|
log "trusting mise.toml"
|
|
mise trust "$REPO_ROOT/mise.toml" >/dev/null 2>&1 || mise trust >/dev/null
|
|
|
|
log "installing mise tools"
|
|
mise install
|
|
|
|
log "activating mise environment for bootstrap"
|
|
eval "$(mise activate bash)"
|
|
}
|
|
|
|
install_node_modules() {
|
|
[[ "$WITH_NODE_MODULES" -eq 1 ]] || { log "skipping pnpm install"; return 0; }
|
|
log "installing pnpm workspace dependencies"
|
|
cd "$REPO_ROOT/apps"
|
|
pnpm install --frozen-lockfile
|
|
}
|
|
|
|
install_hooks() {
|
|
[[ "$WITH_HOOKS" -eq 1 ]] || { log "skipping git hook setup"; return 0; }
|
|
log "wiring pre-commit hooks"
|
|
cd "$REPO_ROOT"
|
|
make doctor
|
|
}
|
|
|
|
install_e2e() {
|
|
[[ "$WITH_E2E" -eq 1 ]] || { log "skipping Playwright E2E browser setup"; return 0; }
|
|
log "installing Playwright Chromium browser/deps"
|
|
"$REPO_ROOT/scripts/install-playwright-browsers.sh"
|
|
}
|
|
|
|
main() {
|
|
cd "$REPO_ROOT"
|
|
log "bootstrapping Kandev development environment at $REPO_ROOT"
|
|
install_os_packages
|
|
install_mise
|
|
activate_mise
|
|
install_node_modules
|
|
install_hooks
|
|
install_e2e
|
|
log "bootstrap complete"
|
|
}
|
|
|
|
main
|