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
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: monthly
|
||||
interval: daily
|
||||
target-branch: main
|
||||
open-pull-requests-limit: 3
|
||||
labels:
|
||||
|
|
@ -20,7 +20,7 @@ updates:
|
|||
- package-ecosystem: github-actions
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: monthly
|
||||
interval: daily
|
||||
target-branch: main
|
||||
open-pull-requests-limit: 1
|
||||
labels:
|
||||
|
|
@ -37,7 +37,7 @@ updates:
|
|||
- package-ecosystem: docker
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: monthly
|
||||
interval: daily
|
||||
target-branch: main
|
||||
open-pull-requests-limit: 1
|
||||
labels:
|
||||
|
|
@ -50,20 +50,3 @@ updates:
|
|||
update-types:
|
||||
- minor
|
||||
- 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
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
exec "$ROOT_DIR/scripts/bootstrap.sh" "$@"
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" >/dev/null 2>&1 && pwd || pwd)"
|
||||
exec "$ROOT_DIR/zeroclaw_install.sh" "$@"
|
||||
|
|
|
|||
|
|
@ -15,16 +15,20 @@ error() {
|
|||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
ZeroClaw one-click bootstrap
|
||||
ZeroClaw installer bootstrap engine
|
||||
|
||||
Usage:
|
||||
./bootstrap.sh [options]
|
||||
./zeroclaw_install.sh [options]
|
||||
./bootstrap.sh [options] # compatibility entrypoint
|
||||
|
||||
Modes:
|
||||
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.
|
||||
|
||||
Options:
|
||||
--guided Run interactive guided installer
|
||||
--no-guided Disable guided installer
|
||||
--docker Run bootstrap in Docker and launch onboarding inside the container
|
||||
--install-system-deps Install build dependencies (Linux/macOS)
|
||||
--install-rust Install Rust via rustup if missing
|
||||
|
|
@ -36,18 +40,22 @@ Options:
|
|||
--api-key <key> API key for non-interactive onboarding
|
||||
--provider <id> Provider for non-interactive onboarding (default: openrouter)
|
||||
--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-install Skip `cargo install --path . --force --locked`
|
||||
-h, --help Show help
|
||||
|
||||
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 --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
|
||||
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_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_DISABLE_ALPINE_AUTO_DEPS
|
||||
Set to 1 to disable Alpine auto-install of missing prerequisites
|
||||
USAGE
|
||||
}
|
||||
|
||||
|
|
@ -227,19 +237,152 @@ run_privileged() {
|
|||
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() {
|
||||
info "Installing system dependencies"
|
||||
|
||||
case "$(uname -s)" in
|
||||
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 install -y build-essential pkg-config git curl
|
||||
elif have_cmd dnf; then
|
||||
run_privileged dnf group install -y development-tools
|
||||
run_privileged dnf install -y pkg-config git curl
|
||||
run_privileged dnf install -y \
|
||||
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
|
||||
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
|
||||
;;
|
||||
Darwin)
|
||||
|
|
@ -288,12 +431,125 @@ install_rust_toolchain() {
|
|||
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() {
|
||||
if ! have_cmd docker; then
|
||||
error "docker is not installed."
|
||||
cat <<'MSG' >&2
|
||||
Install Docker first, then re-run with:
|
||||
./bootstrap.sh --docker
|
||||
./zeroclaw_install.sh --docker
|
||||
MSG
|
||||
exit 1
|
||||
fi
|
||||
|
|
@ -342,9 +598,9 @@ run_docker_bootstrap() {
|
|||
Use either:
|
||||
--api-key "sk-..."
|
||||
or:
|
||||
ZEROCLAW_API_KEY="sk-..." ./bootstrap.sh --docker
|
||||
ZEROCLAW_API_KEY="sk-..." ./zeroclaw_install.sh --docker
|
||||
or run interactive:
|
||||
./bootstrap.sh --docker --interactive-onboard
|
||||
./zeroclaw_install.sh --docker --interactive-onboard
|
||||
MSG
|
||||
exit 1
|
||||
fi
|
||||
|
|
@ -373,6 +629,8 @@ SCRIPT_PATH="${BASH_SOURCE[0]:-$0}"
|
|||
SCRIPT_DIR="$(cd "$(dirname "$SCRIPT_PATH")" >/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"
|
||||
ORIGINAL_ARG_COUNT=$#
|
||||
GUIDED_MODE="auto"
|
||||
|
||||
DOCKER_MODE=false
|
||||
INSTALL_SYSTEM_DEPS=false
|
||||
|
|
@ -391,6 +649,14 @@ MODEL="${ZEROCLAW_MODEL:-}"
|
|||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--guided)
|
||||
GUIDED_MODE="on"
|
||||
shift
|
||||
;;
|
||||
--no-guided)
|
||||
GUIDED_MODE="off"
|
||||
shift
|
||||
;;
|
||||
--docker)
|
||||
DOCKER_MODE=true
|
||||
shift
|
||||
|
|
@ -448,6 +714,10 @@ while [[ $# -gt 0 ]]; do
|
|||
}
|
||||
shift 2
|
||||
;;
|
||||
--build-first)
|
||||
SKIP_BUILD=false
|
||||
shift
|
||||
;;
|
||||
--skip-build)
|
||||
SKIP_BUILD=true
|
||||
shift
|
||||
|
|
@ -469,6 +739,24 @@ while [[ $# -gt 0 ]]; do
|
|||
esac
|
||||
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 [[ "$INSTALL_SYSTEM_DEPS" == true ]]; then
|
||||
warn "--install-system-deps is ignored with --docker."
|
||||
|
|
@ -477,6 +765,15 @@ if [[ "$DOCKER_MODE" == true ]]; then
|
|||
warn "--install-rust is ignored with --docker."
|
||||
fi
|
||||
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
|
||||
install_system_deps
|
||||
fi
|
||||
|
|
@ -554,8 +851,8 @@ DONE
|
|||
cat <<'DONE'
|
||||
|
||||
Next steps:
|
||||
./bootstrap.sh --docker --interactive-onboard
|
||||
./bootstrap.sh --docker --api-key "sk-..." --provider openrouter
|
||||
./zeroclaw_install.sh --docker --interactive-onboard
|
||||
./zeroclaw_install.sh --docker --api-key "sk-..." --provider openrouter
|
||||
DONE
|
||||
exit 0
|
||||
fi
|
||||
|
|
@ -588,7 +885,7 @@ if [[ "$PREBUILT_INSTALLED" == false && ( "$SKIP_BUILD" == false || "$SKIP_INSTA
|
|||
cat <<'MSG' >&2
|
||||
Install Rust first: https://rustup.rs/
|
||||
or re-run with:
|
||||
./bootstrap.sh --install-rust
|
||||
./zeroclaw_install.sh --install-rust
|
||||
MSG
|
||||
exit 1
|
||||
fi
|
||||
|
|
@ -633,9 +930,9 @@ if [[ "$RUN_ONBOARD" == true ]]; then
|
|||
Use either:
|
||||
--api-key "sk-..."
|
||||
or:
|
||||
ZEROCLAW_API_KEY="sk-..." ./bootstrap.sh --onboard
|
||||
ZEROCLAW_API_KEY="sk-..." ./zeroclaw_install.sh --onboard
|
||||
or run interactive:
|
||||
./bootstrap.sh --interactive-onboard
|
||||
./zeroclaw_install.sh --interactive-onboard
|
||||
MSG
|
||||
exit 1
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -2,10 +2,15 @@
|
|||
set -euo pipefail
|
||||
|
||||
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"
|
||||
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
|
||||
exec "$BOOTSTRAP_LOCAL" "$@"
|
||||
|
|
@ -24,35 +29,15 @@ trap cleanup EXIT
|
|||
|
||||
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
|
||||
"$TEMP_DIR/scripts/bootstrap.sh" "$@"
|
||||
exit 0
|
||||
exec "$TEMP_DIR/scripts/bootstrap.sh" "$@"
|
||||
fi
|
||||
|
||||
echo "[deprecated] cloned revision has no bootstrap.sh; falling back to legacy source install flow" >&2
|
||||
|
||||
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
|
||||
cat <<'USAGE'
|
||||
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
|
||||
echo "error: zeroclaw_install.sh/bootstrap.sh was not found in the fetched revision." >&2
|
||||
echo "Run the local bootstrap directly when possible:" >&2
|
||||
echo " ./zeroclaw_install.sh --help" >&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