feat(installer): add guided zeroclaw installer and distro hardening (#887)
* feat(installer): add guided zeroclaw installer entrypoint - add top-level POSIX wrapper (zeroclaw_install.sh) that ensures bash is present - route bootstrap/install compatibility scripts through the new installer entrypoint - improve Linux dependency handling for Alpine/Fedora/Arch, including pacman container fallback * fix(ci): resolve dependabot config conflict and run daily - remove duplicate docker ecosystem entry with overlapping directory/target-branch - switch cargo, github-actions, and docker schedules from monthly to daily
This commit is contained in:
parent
a2e9c0d1e1
commit
c96ea79ac0
5 changed files with 426 additions and 73 deletions
23
.github/dependabot.yml
vendored
23
.github/dependabot.yml
vendored
|
|
@ -4,7 +4,7 @@ updates:
|
||||||
- package-ecosystem: cargo
|
- package-ecosystem: cargo
|
||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: monthly
|
interval: daily
|
||||||
target-branch: main
|
target-branch: main
|
||||||
open-pull-requests-limit: 3
|
open-pull-requests-limit: 3
|
||||||
labels:
|
labels:
|
||||||
|
|
@ -20,7 +20,7 @@ updates:
|
||||||
- package-ecosystem: github-actions
|
- package-ecosystem: github-actions
|
||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: monthly
|
interval: daily
|
||||||
target-branch: main
|
target-branch: main
|
||||||
open-pull-requests-limit: 1
|
open-pull-requests-limit: 1
|
||||||
labels:
|
labels:
|
||||||
|
|
@ -37,7 +37,7 @@ updates:
|
||||||
- package-ecosystem: docker
|
- package-ecosystem: docker
|
||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: monthly
|
interval: daily
|
||||||
target-branch: main
|
target-branch: main
|
||||||
open-pull-requests-limit: 1
|
open-pull-requests-limit: 1
|
||||||
labels:
|
labels:
|
||||||
|
|
@ -50,20 +50,3 @@ updates:
|
||||||
update-types:
|
update-types:
|
||||||
- minor
|
- minor
|
||||||
- patch
|
- patch
|
||||||
|
|
||||||
- package-ecosystem: docker
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: weekly
|
|
||||||
target-branch: main
|
|
||||||
open-pull-requests-limit: 3
|
|
||||||
labels:
|
|
||||||
- "ci"
|
|
||||||
- "dependencies"
|
|
||||||
groups:
|
|
||||||
docker-minor-patch:
|
|
||||||
patterns:
|
|
||||||
- "*"
|
|
||||||
update-types:
|
|
||||||
- minor
|
|
||||||
- patch
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" >/dev/null 2>&1 && pwd || pwd)"
|
||||||
exec "$ROOT_DIR/scripts/bootstrap.sh" "$@"
|
exec "$ROOT_DIR/zeroclaw_install.sh" "$@"
|
||||||
|
|
|
||||||
|
|
@ -15,16 +15,20 @@ error() {
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
cat <<'USAGE'
|
cat <<'USAGE'
|
||||||
ZeroClaw one-click bootstrap
|
ZeroClaw installer bootstrap engine
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
./bootstrap.sh [options]
|
./zeroclaw_install.sh [options]
|
||||||
|
./bootstrap.sh [options] # compatibility entrypoint
|
||||||
|
|
||||||
Modes:
|
Modes:
|
||||||
Default mode installs/builds ZeroClaw only (requires existing Rust toolchain).
|
Default mode installs/builds ZeroClaw only (requires existing Rust toolchain).
|
||||||
|
Guided mode asks setup questions and configures options interactively.
|
||||||
Optional bootstrap mode can also install system dependencies and Rust.
|
Optional bootstrap mode can also install system dependencies and Rust.
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
|
--guided Run interactive guided installer
|
||||||
|
--no-guided Disable guided installer
|
||||||
--docker Run bootstrap in Docker and launch onboarding inside the container
|
--docker Run bootstrap in Docker and launch onboarding inside the container
|
||||||
--install-system-deps Install build dependencies (Linux/macOS)
|
--install-system-deps Install build dependencies (Linux/macOS)
|
||||||
--install-rust Install Rust via rustup if missing
|
--install-rust Install Rust via rustup if missing
|
||||||
|
|
@ -36,18 +40,22 @@ Options:
|
||||||
--api-key <key> API key for non-interactive onboarding
|
--api-key <key> API key for non-interactive onboarding
|
||||||
--provider <id> Provider for non-interactive onboarding (default: openrouter)
|
--provider <id> Provider for non-interactive onboarding (default: openrouter)
|
||||||
--model <id> Model for non-interactive onboarding (optional)
|
--model <id> Model for non-interactive onboarding (optional)
|
||||||
|
--build-first Alias for explicitly enabling separate `cargo build --release --locked`
|
||||||
--skip-build Skip `cargo build --release --locked`
|
--skip-build Skip `cargo build --release --locked`
|
||||||
--skip-install Skip `cargo install --path . --force --locked`
|
--skip-install Skip `cargo install --path . --force --locked`
|
||||||
-h, --help Show help
|
-h, --help Show help
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
./bootstrap.sh
|
./zeroclaw_install.sh
|
||||||
|
./zeroclaw_install.sh --guided
|
||||||
|
./zeroclaw_install.sh --install-system-deps --install-rust
|
||||||
|
./zeroclaw_install.sh --prefer-prebuilt
|
||||||
|
./zeroclaw_install.sh --prebuilt-only
|
||||||
|
./zeroclaw_install.sh --onboard --api-key "sk-..." --provider openrouter [--model "openrouter/auto"]
|
||||||
|
./zeroclaw_install.sh --interactive-onboard
|
||||||
|
|
||||||
|
# Compatibility entrypoint:
|
||||||
./bootstrap.sh --docker
|
./bootstrap.sh --docker
|
||||||
./bootstrap.sh --install-system-deps --install-rust
|
|
||||||
./bootstrap.sh --prefer-prebuilt
|
|
||||||
./bootstrap.sh --prebuilt-only
|
|
||||||
./bootstrap.sh --onboard --api-key "sk-..." --provider openrouter [--model "openrouter/auto"]
|
|
||||||
./bootstrap.sh --interactive-onboard
|
|
||||||
|
|
||||||
# Remote one-liner
|
# Remote one-liner
|
||||||
curl -fsSL https://raw.githubusercontent.com/zeroclaw-labs/zeroclaw/main/scripts/bootstrap.sh | bash
|
curl -fsSL https://raw.githubusercontent.com/zeroclaw-labs/zeroclaw/main/scripts/bootstrap.sh | bash
|
||||||
|
|
@ -60,6 +68,8 @@ Environment:
|
||||||
ZEROCLAW_MODEL Used when --model is not provided
|
ZEROCLAW_MODEL Used when --model is not provided
|
||||||
ZEROCLAW_BOOTSTRAP_MIN_RAM_MB Minimum RAM threshold for source build preflight (default: 2048)
|
ZEROCLAW_BOOTSTRAP_MIN_RAM_MB Minimum RAM threshold for source build preflight (default: 2048)
|
||||||
ZEROCLAW_BOOTSTRAP_MIN_DISK_MB Minimum free disk threshold for source build preflight (default: 6144)
|
ZEROCLAW_BOOTSTRAP_MIN_DISK_MB Minimum free disk threshold for source build preflight (default: 6144)
|
||||||
|
ZEROCLAW_DISABLE_ALPINE_AUTO_DEPS
|
||||||
|
Set to 1 to disable Alpine auto-install of missing prerequisites
|
||||||
USAGE
|
USAGE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -227,19 +237,152 @@ run_privileged() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is_container_runtime() {
|
||||||
|
if [[ -f /.dockerenv || -f /run/.containerenv ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -r /proc/1/cgroup ]] && grep -Eq '(docker|containerd|kubepods|podman|lxc)' /proc/1/cgroup; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
run_pacman() {
|
||||||
|
if ! have_cmd pacman; then
|
||||||
|
error "pacman is not available."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! is_container_runtime; then
|
||||||
|
run_privileged pacman "$@"
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
local pacman_cfg_tmp=""
|
||||||
|
local pacman_rc=0
|
||||||
|
pacman_cfg_tmp="$(mktemp /tmp/zeroclaw-pacman.XXXXXX.conf)"
|
||||||
|
cp /etc/pacman.conf "$pacman_cfg_tmp"
|
||||||
|
if ! grep -Eq '^[[:space:]]*DisableSandboxSyscalls([[:space:]]|$)' "$pacman_cfg_tmp"; then
|
||||||
|
printf '\nDisableSandboxSyscalls\n' >> "$pacman_cfg_tmp"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if run_privileged pacman --config "$pacman_cfg_tmp" "$@"; then
|
||||||
|
pacman_rc=0
|
||||||
|
else
|
||||||
|
pacman_rc=$?
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f "$pacman_cfg_tmp"
|
||||||
|
return "$pacman_rc"
|
||||||
|
}
|
||||||
|
|
||||||
|
ALPINE_PREREQ_PACKAGES=(
|
||||||
|
bash
|
||||||
|
build-base
|
||||||
|
pkgconf
|
||||||
|
git
|
||||||
|
curl
|
||||||
|
openssl-dev
|
||||||
|
perl
|
||||||
|
ca-certificates
|
||||||
|
)
|
||||||
|
ALPINE_MISSING_PKGS=()
|
||||||
|
|
||||||
|
find_missing_alpine_prereqs() {
|
||||||
|
ALPINE_MISSING_PKGS=()
|
||||||
|
if ! have_cmd apk; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local pkg=""
|
||||||
|
for pkg in "${ALPINE_PREREQ_PACKAGES[@]}"; do
|
||||||
|
if ! apk info -e "$pkg" >/dev/null 2>&1; then
|
||||||
|
ALPINE_MISSING_PKGS+=("$pkg")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_to_word() {
|
||||||
|
if [[ "$1" == true ]]; then
|
||||||
|
echo "yes"
|
||||||
|
else
|
||||||
|
echo "no"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt_yes_no() {
|
||||||
|
local question="$1"
|
||||||
|
local default_answer="$2"
|
||||||
|
local prompt=""
|
||||||
|
local answer=""
|
||||||
|
|
||||||
|
if [[ "$default_answer" == "yes" ]]; then
|
||||||
|
prompt="[Y/n]"
|
||||||
|
else
|
||||||
|
prompt="[y/N]"
|
||||||
|
fi
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
if ! read -r -p "$question $prompt " answer; then
|
||||||
|
error "guided installer input was interrupted."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
answer="${answer:-$default_answer}"
|
||||||
|
case "$(printf '%s' "$answer" | tr '[:upper:]' '[:lower:]')" in
|
||||||
|
y|yes)
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
n|no)
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Please answer yes or no."
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
install_system_deps() {
|
install_system_deps() {
|
||||||
info "Installing system dependencies"
|
info "Installing system dependencies"
|
||||||
|
|
||||||
case "$(uname -s)" in
|
case "$(uname -s)" in
|
||||||
Linux)
|
Linux)
|
||||||
if have_cmd apt-get; then
|
if have_cmd apk; then
|
||||||
|
find_missing_alpine_prereqs
|
||||||
|
if [[ ${#ALPINE_MISSING_PKGS[@]} -eq 0 ]]; then
|
||||||
|
info "Alpine prerequisites already installed"
|
||||||
|
else
|
||||||
|
info "Installing Alpine prerequisites: ${ALPINE_MISSING_PKGS[*]}"
|
||||||
|
run_privileged apk add --no-cache "${ALPINE_MISSING_PKGS[@]}"
|
||||||
|
fi
|
||||||
|
elif have_cmd apt-get; then
|
||||||
run_privileged apt-get update -qq
|
run_privileged apt-get update -qq
|
||||||
run_privileged apt-get install -y build-essential pkg-config git curl
|
run_privileged apt-get install -y build-essential pkg-config git curl
|
||||||
elif have_cmd dnf; then
|
elif have_cmd dnf; then
|
||||||
run_privileged dnf group install -y development-tools
|
run_privileged dnf install -y \
|
||||||
run_privileged dnf install -y pkg-config git curl
|
gcc \
|
||||||
|
gcc-c++ \
|
||||||
|
make \
|
||||||
|
pkgconf-pkg-config \
|
||||||
|
git \
|
||||||
|
curl \
|
||||||
|
openssl-devel \
|
||||||
|
perl
|
||||||
|
elif have_cmd pacman; then
|
||||||
|
run_pacman -Sy --noconfirm
|
||||||
|
run_pacman -S --noconfirm --needed \
|
||||||
|
gcc \
|
||||||
|
make \
|
||||||
|
pkgconf \
|
||||||
|
git \
|
||||||
|
curl \
|
||||||
|
openssl \
|
||||||
|
perl \
|
||||||
|
ca-certificates
|
||||||
else
|
else
|
||||||
warn "Unsupported Linux distribution. Install compiler toolchain + pkg-config + git + curl manually."
|
warn "Unsupported Linux distribution. Install compiler toolchain + pkg-config + git + curl + OpenSSL headers + perl manually."
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
Darwin)
|
Darwin)
|
||||||
|
|
@ -288,12 +431,125 @@ install_rust_toolchain() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
run_guided_installer() {
|
||||||
|
local os_name="$1"
|
||||||
|
local provider_input=""
|
||||||
|
local model_input=""
|
||||||
|
local api_key_input=""
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "ZeroClaw guided installer"
|
||||||
|
echo "Answer a few questions, then the installer will run automatically."
|
||||||
|
echo
|
||||||
|
|
||||||
|
if [[ "$os_name" == "Linux" ]]; then
|
||||||
|
if prompt_yes_no "Install Linux build dependencies (toolchain/pkg-config/git/curl)?" "yes"; then
|
||||||
|
INSTALL_SYSTEM_DEPS=true
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if prompt_yes_no "Install system dependencies for $os_name?" "no"; then
|
||||||
|
INSTALL_SYSTEM_DEPS=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if have_cmd cargo && have_cmd rustc; then
|
||||||
|
info "Detected Rust toolchain: $(rustc --version)"
|
||||||
|
else
|
||||||
|
if prompt_yes_no "Rust toolchain not found. Install Rust via rustup now?" "yes"; then
|
||||||
|
INSTALL_RUST=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if prompt_yes_no "Run a separate prebuild before install?" "yes"; then
|
||||||
|
SKIP_BUILD=false
|
||||||
|
else
|
||||||
|
SKIP_BUILD=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if prompt_yes_no "Install zeroclaw into cargo bin now?" "yes"; then
|
||||||
|
SKIP_INSTALL=false
|
||||||
|
else
|
||||||
|
SKIP_INSTALL=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if prompt_yes_no "Run onboarding after install?" "no"; then
|
||||||
|
RUN_ONBOARD=true
|
||||||
|
if prompt_yes_no "Use interactive onboarding?" "yes"; then
|
||||||
|
INTERACTIVE_ONBOARD=true
|
||||||
|
else
|
||||||
|
INTERACTIVE_ONBOARD=false
|
||||||
|
if ! read -r -p "Provider [$PROVIDER]: " provider_input; then
|
||||||
|
error "guided installer input was interrupted."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ -n "$provider_input" ]]; then
|
||||||
|
PROVIDER="$provider_input"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! read -r -p "Model [${MODEL:-leave empty}]: " model_input; then
|
||||||
|
error "guided installer input was interrupted."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ -n "$model_input" ]]; then
|
||||||
|
MODEL="$model_input"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$API_KEY" ]]; then
|
||||||
|
if ! read -r -s -p "API key (hidden, leave empty to switch to interactive onboarding): " api_key_input; then
|
||||||
|
echo
|
||||||
|
error "guided installer input was interrupted."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo
|
||||||
|
if [[ -n "$api_key_input" ]]; then
|
||||||
|
API_KEY="$api_key_input"
|
||||||
|
else
|
||||||
|
warn "No API key entered. Using interactive onboarding instead."
|
||||||
|
INTERACTIVE_ONBOARD=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
info "Installer plan"
|
||||||
|
local install_binary=true
|
||||||
|
local build_first=false
|
||||||
|
if [[ "$SKIP_INSTALL" == true ]]; then
|
||||||
|
install_binary=false
|
||||||
|
fi
|
||||||
|
if [[ "$SKIP_BUILD" == false ]]; then
|
||||||
|
build_first=true
|
||||||
|
fi
|
||||||
|
echo " docker-mode: $(bool_to_word "$DOCKER_MODE")"
|
||||||
|
echo " install-system-deps: $(bool_to_word "$INSTALL_SYSTEM_DEPS")"
|
||||||
|
echo " install-rust: $(bool_to_word "$INSTALL_RUST")"
|
||||||
|
echo " build-first: $(bool_to_word "$build_first")"
|
||||||
|
echo " install-binary: $(bool_to_word "$install_binary")"
|
||||||
|
echo " onboard: $(bool_to_word "$RUN_ONBOARD")"
|
||||||
|
if [[ "$RUN_ONBOARD" == true ]]; then
|
||||||
|
echo " interactive-onboard: $(bool_to_word "$INTERACTIVE_ONBOARD")"
|
||||||
|
if [[ "$INTERACTIVE_ONBOARD" == false ]]; then
|
||||||
|
echo " provider: $PROVIDER"
|
||||||
|
if [[ -n "$MODEL" ]]; then
|
||||||
|
echo " model: $MODEL"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
if ! prompt_yes_no "Proceed with this install plan?" "yes"; then
|
||||||
|
info "Installation canceled by user."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
ensure_docker_ready() {
|
ensure_docker_ready() {
|
||||||
if ! have_cmd docker; then
|
if ! have_cmd docker; then
|
||||||
error "docker is not installed."
|
error "docker is not installed."
|
||||||
cat <<'MSG' >&2
|
cat <<'MSG' >&2
|
||||||
Install Docker first, then re-run with:
|
Install Docker first, then re-run with:
|
||||||
./bootstrap.sh --docker
|
./zeroclaw_install.sh --docker
|
||||||
MSG
|
MSG
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
@ -342,9 +598,9 @@ run_docker_bootstrap() {
|
||||||
Use either:
|
Use either:
|
||||||
--api-key "sk-..."
|
--api-key "sk-..."
|
||||||
or:
|
or:
|
||||||
ZEROCLAW_API_KEY="sk-..." ./bootstrap.sh --docker
|
ZEROCLAW_API_KEY="sk-..." ./zeroclaw_install.sh --docker
|
||||||
or run interactive:
|
or run interactive:
|
||||||
./bootstrap.sh --docker --interactive-onboard
|
./zeroclaw_install.sh --docker --interactive-onboard
|
||||||
MSG
|
MSG
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
@ -373,6 +629,8 @@ SCRIPT_PATH="${BASH_SOURCE[0]:-$0}"
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$SCRIPT_PATH")" >/dev/null 2>&1 && pwd || pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "$SCRIPT_PATH")" >/dev/null 2>&1 && pwd || pwd)"
|
||||||
ROOT_DIR="$(cd "$SCRIPT_DIR/.." >/dev/null 2>&1 && pwd || pwd)"
|
ROOT_DIR="$(cd "$SCRIPT_DIR/.." >/dev/null 2>&1 && pwd || pwd)"
|
||||||
REPO_URL="https://github.com/zeroclaw-labs/zeroclaw.git"
|
REPO_URL="https://github.com/zeroclaw-labs/zeroclaw.git"
|
||||||
|
ORIGINAL_ARG_COUNT=$#
|
||||||
|
GUIDED_MODE="auto"
|
||||||
|
|
||||||
DOCKER_MODE=false
|
DOCKER_MODE=false
|
||||||
INSTALL_SYSTEM_DEPS=false
|
INSTALL_SYSTEM_DEPS=false
|
||||||
|
|
@ -391,6 +649,14 @@ MODEL="${ZEROCLAW_MODEL:-}"
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
|
--guided)
|
||||||
|
GUIDED_MODE="on"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--no-guided)
|
||||||
|
GUIDED_MODE="off"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
--docker)
|
--docker)
|
||||||
DOCKER_MODE=true
|
DOCKER_MODE=true
|
||||||
shift
|
shift
|
||||||
|
|
@ -448,6 +714,10 @@ while [[ $# -gt 0 ]]; do
|
||||||
}
|
}
|
||||||
shift 2
|
shift 2
|
||||||
;;
|
;;
|
||||||
|
--build-first)
|
||||||
|
SKIP_BUILD=false
|
||||||
|
shift
|
||||||
|
;;
|
||||||
--skip-build)
|
--skip-build)
|
||||||
SKIP_BUILD=true
|
SKIP_BUILD=true
|
||||||
shift
|
shift
|
||||||
|
|
@ -469,6 +739,24 @@ while [[ $# -gt 0 ]]; do
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
|
OS_NAME="$(uname -s)"
|
||||||
|
if [[ "$GUIDED_MODE" == "auto" ]]; then
|
||||||
|
if [[ "$OS_NAME" == "Linux" && "$ORIGINAL_ARG_COUNT" -eq 0 && -t 0 && -t 1 ]]; then
|
||||||
|
GUIDED_MODE="on"
|
||||||
|
else
|
||||||
|
GUIDED_MODE="off"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$DOCKER_MODE" == true && "$GUIDED_MODE" == "on" ]]; then
|
||||||
|
warn "--guided is ignored with --docker."
|
||||||
|
GUIDED_MODE="off"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$GUIDED_MODE" == "on" ]]; then
|
||||||
|
run_guided_installer "$OS_NAME"
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ "$DOCKER_MODE" == true ]]; then
|
if [[ "$DOCKER_MODE" == true ]]; then
|
||||||
if [[ "$INSTALL_SYSTEM_DEPS" == true ]]; then
|
if [[ "$INSTALL_SYSTEM_DEPS" == true ]]; then
|
||||||
warn "--install-system-deps is ignored with --docker."
|
warn "--install-system-deps is ignored with --docker."
|
||||||
|
|
@ -477,6 +765,15 @@ if [[ "$DOCKER_MODE" == true ]]; then
|
||||||
warn "--install-rust is ignored with --docker."
|
warn "--install-rust is ignored with --docker."
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
|
if [[ "$OS_NAME" == "Linux" && -z "${ZEROCLAW_DISABLE_ALPINE_AUTO_DEPS:-}" ]] && have_cmd apk; then
|
||||||
|
find_missing_alpine_prereqs
|
||||||
|
if [[ ${#ALPINE_MISSING_PKGS[@]} -gt 0 && "$INSTALL_SYSTEM_DEPS" == false ]]; then
|
||||||
|
info "Detected Alpine with missing prerequisites: ${ALPINE_MISSING_PKGS[*]}"
|
||||||
|
info "Auto-enabling system dependency installation (set ZEROCLAW_DISABLE_ALPINE_AUTO_DEPS=1 to disable)."
|
||||||
|
INSTALL_SYSTEM_DEPS=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ "$INSTALL_SYSTEM_DEPS" == true ]]; then
|
if [[ "$INSTALL_SYSTEM_DEPS" == true ]]; then
|
||||||
install_system_deps
|
install_system_deps
|
||||||
fi
|
fi
|
||||||
|
|
@ -554,8 +851,8 @@ DONE
|
||||||
cat <<'DONE'
|
cat <<'DONE'
|
||||||
|
|
||||||
Next steps:
|
Next steps:
|
||||||
./bootstrap.sh --docker --interactive-onboard
|
./zeroclaw_install.sh --docker --interactive-onboard
|
||||||
./bootstrap.sh --docker --api-key "sk-..." --provider openrouter
|
./zeroclaw_install.sh --docker --api-key "sk-..." --provider openrouter
|
||||||
DONE
|
DONE
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
@ -588,7 +885,7 @@ if [[ "$PREBUILT_INSTALLED" == false && ( "$SKIP_BUILD" == false || "$SKIP_INSTA
|
||||||
cat <<'MSG' >&2
|
cat <<'MSG' >&2
|
||||||
Install Rust first: https://rustup.rs/
|
Install Rust first: https://rustup.rs/
|
||||||
or re-run with:
|
or re-run with:
|
||||||
./bootstrap.sh --install-rust
|
./zeroclaw_install.sh --install-rust
|
||||||
MSG
|
MSG
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
@ -633,9 +930,9 @@ if [[ "$RUN_ONBOARD" == true ]]; then
|
||||||
Use either:
|
Use either:
|
||||||
--api-key "sk-..."
|
--api-key "sk-..."
|
||||||
or:
|
or:
|
||||||
ZEROCLAW_API_KEY="sk-..." ./bootstrap.sh --onboard
|
ZEROCLAW_API_KEY="sk-..." ./zeroclaw_install.sh --onboard
|
||||||
or run interactive:
|
or run interactive:
|
||||||
./bootstrap.sh --interactive-onboard
|
./zeroclaw_install.sh --interactive-onboard
|
||||||
MSG
|
MSG
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,15 @@
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" >/dev/null 2>&1 && pwd || pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" >/dev/null 2>&1 && pwd || pwd)"
|
||||||
|
INSTALLER_LOCAL="$(cd "$SCRIPT_DIR/.." >/dev/null 2>&1 && pwd || pwd)/zeroclaw_install.sh"
|
||||||
BOOTSTRAP_LOCAL="$SCRIPT_DIR/bootstrap.sh"
|
BOOTSTRAP_LOCAL="$SCRIPT_DIR/bootstrap.sh"
|
||||||
REPO_URL="https://github.com/zeroclaw-labs/zeroclaw.git"
|
REPO_URL="https://github.com/zeroclaw-labs/zeroclaw.git"
|
||||||
|
|
||||||
echo "[deprecated] scripts/install.sh -> bootstrap.sh" >&2
|
echo "[deprecated] scripts/install.sh -> ./zeroclaw_install.sh" >&2
|
||||||
|
|
||||||
|
if [[ -x "$INSTALLER_LOCAL" ]]; then
|
||||||
|
exec "$INSTALLER_LOCAL" "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ -f "$BOOTSTRAP_LOCAL" ]]; then
|
if [[ -f "$BOOTSTRAP_LOCAL" ]]; then
|
||||||
exec "$BOOTSTRAP_LOCAL" "$@"
|
exec "$BOOTSTRAP_LOCAL" "$@"
|
||||||
|
|
@ -24,35 +29,15 @@ trap cleanup EXIT
|
||||||
|
|
||||||
git clone --depth 1 "$REPO_URL" "$TEMP_DIR" >/dev/null 2>&1
|
git clone --depth 1 "$REPO_URL" "$TEMP_DIR" >/dev/null 2>&1
|
||||||
|
|
||||||
|
if [[ -x "$TEMP_DIR/zeroclaw_install.sh" ]]; then
|
||||||
|
exec "$TEMP_DIR/zeroclaw_install.sh" "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ -x "$TEMP_DIR/scripts/bootstrap.sh" ]]; then
|
if [[ -x "$TEMP_DIR/scripts/bootstrap.sh" ]]; then
|
||||||
"$TEMP_DIR/scripts/bootstrap.sh" "$@"
|
exec "$TEMP_DIR/scripts/bootstrap.sh" "$@"
|
||||||
exit 0
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "[deprecated] cloned revision has no bootstrap.sh; falling back to legacy source install flow" >&2
|
echo "error: zeroclaw_install.sh/bootstrap.sh was not found in the fetched revision." >&2
|
||||||
|
echo "Run the local bootstrap directly when possible:" >&2
|
||||||
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
|
echo " ./zeroclaw_install.sh --help" >&2
|
||||||
cat <<'USAGE'
|
exit 1
|
||||||
Legacy install.sh fallback mode
|
|
||||||
|
|
||||||
Behavior:
|
|
||||||
- Clone repository
|
|
||||||
- cargo build --release --locked
|
|
||||||
- cargo install --path <clone> --force --locked
|
|
||||||
|
|
||||||
For the new dual-mode installer, use:
|
|
||||||
./bootstrap.sh --help
|
|
||||||
USAGE
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! command -v cargo >/dev/null 2>&1; then
|
|
||||||
echo "error: cargo is required for legacy install.sh fallback mode" >&2
|
|
||||||
echo "Install Rust first: https://rustup.rs/" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
cargo build --release --locked --manifest-path "$TEMP_DIR/Cargo.toml"
|
|
||||||
cargo install --path "$TEMP_DIR" --force --locked
|
|
||||||
|
|
||||||
echo "Legacy source install completed." >&2
|
|
||||||
|
|
|
||||||
88
zeroclaw_install.sh
Executable file
88
zeroclaw_install.sh
Executable file
|
|
@ -0,0 +1,88 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
have_cmd() {
|
||||||
|
command -v "$1" >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
run_privileged() {
|
||||||
|
if [ "$(id -u)" -eq 0 ]; then
|
||||||
|
"$@"
|
||||||
|
elif have_cmd sudo; then
|
||||||
|
sudo "$@"
|
||||||
|
else
|
||||||
|
echo "error: sudo is required to install missing dependencies." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
is_container_runtime() {
|
||||||
|
if [ -f /.dockerenv ] || [ -f /run/.containerenv ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -r /proc/1/cgroup ] && grep -Eq '(docker|containerd|kubepods|podman|lxc)' /proc/1/cgroup; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
run_pacman() {
|
||||||
|
if ! is_container_runtime; then
|
||||||
|
run_privileged pacman "$@"
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
PACMAN_CFG_TMP="$(mktemp /tmp/zeroclaw-pacman.XXXXXX.conf)"
|
||||||
|
cp /etc/pacman.conf "$PACMAN_CFG_TMP"
|
||||||
|
if ! grep -Eq '^[[:space:]]*DisableSandboxSyscalls([[:space:]]|$)' "$PACMAN_CFG_TMP"; then
|
||||||
|
printf '\nDisableSandboxSyscalls\n' >> "$PACMAN_CFG_TMP"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if run_privileged pacman --config "$PACMAN_CFG_TMP" "$@"; then
|
||||||
|
PACMAN_RC=0
|
||||||
|
else
|
||||||
|
PACMAN_RC=$?
|
||||||
|
fi
|
||||||
|
rm -f "$PACMAN_CFG_TMP"
|
||||||
|
return "$PACMAN_RC"
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_bash() {
|
||||||
|
if have_cmd bash; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "==> bash not found; attempting to install it"
|
||||||
|
if have_cmd apk; then
|
||||||
|
run_privileged apk add --no-cache bash
|
||||||
|
elif have_cmd apt-get; then
|
||||||
|
run_privileged apt-get update -qq
|
||||||
|
run_privileged apt-get install -y bash
|
||||||
|
elif have_cmd dnf; then
|
||||||
|
run_privileged dnf install -y bash
|
||||||
|
elif have_cmd pacman; then
|
||||||
|
run_pacman -Sy --noconfirm
|
||||||
|
run_pacman -S --noconfirm --needed bash
|
||||||
|
else
|
||||||
|
echo "error: unsupported package manager; install bash manually and retry." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
ROOT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" >/dev/null 2>&1 && pwd || pwd)"
|
||||||
|
BOOTSTRAP_SCRIPT="$ROOT_DIR/scripts/bootstrap.sh"
|
||||||
|
|
||||||
|
if [ ! -f "$BOOTSTRAP_SCRIPT" ]; then
|
||||||
|
echo "error: scripts/bootstrap.sh not found from repository root." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ensure_bash
|
||||||
|
|
||||||
|
if [ "$#" -eq 0 ]; then
|
||||||
|
exec bash "$BOOTSTRAP_SCRIPT" --guided
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec bash "$BOOTSTRAP_SCRIPT" "$@"
|
||||||
Loading…
Add table
Add a link
Reference in a new issue