#!/usr/bin/env bash # SPDX-License-Identifier: Apache-2.0 # Toolchain for Claude Code cloud sessions (CLAUDE_CODE_REMOTE=true): Rust, Bun, uv, Graphify, build tools and # the repository dependencies. User-level, idempotent, tolerant: every step logs and continues, the exit code # is always 0 so a missing network route never blocks a session. # --background fork the installation and return at once (SessionStart hook) # --wait run in the foreground # --dry-run print the steps only # --force run outside a cloud session set -u mode=wait force=0 dry=0 for arg in "$@"; do case "$arg" in --background) mode=background ;; --wait) mode=wait ;; --dry-run) dry=1 ;; --force) force=1 ;; esac done [[ "${CLAUDE_CODE_REMOTE:-}" == "true" || $force == 1 || $dry == 1 ]] || exit 0 root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)" log_dir="${XDG_STATE_HOME:-$HOME/.local/state}/aphrody" log="$log_dir/cloud-setup.log" mkdir -p "$log_dir" "$HOME/.local/bin" "$HOME/.cargo/bin" "$HOME/.bun/bin" export PATH="$HOME/.cargo/bin:$HOME/.bun/bin:$HOME/.local/bin:$PATH" # Later Bash calls of the session find the tools without a shell profile. if [[ -n "${CLAUDE_ENV_FILE:-}" ]]; then grep -q 'aphrody-cloud-path' "$CLAUDE_ENV_FILE" 2>/dev/null || echo 'export PATH="$HOME/.cargo/bin:$HOME/.bun/bin:$HOME/.local/bin:$PATH" # aphrody-cloud-path' >>"$CLAUDE_ENV_FILE" fi if [[ $mode == background && $dry == 0 ]]; then nohup bash "$0" --wait >>"$log" 2>&1 /dev/null 2>&1; } lock="$log_dir/cloud-setup.lock" if (( dry == 0 )); then exec 9>"$lock" flock -n 9 || { echo "another cloud setup is running"; exit 0; } fi step "versions from the repository" channel="$(sed -nE 's/^channel[[:space:]]*=[[:space:]]*"([^"]+)"/\1/p' "$root/rust-toolchain.toml" 2>/dev/null | head -1)" bun_pin="$(sed -nE '/"bun"[[:space:]]*:[[:space:]]*\{/,/\}/ s/.*"version"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/p' "$root/config/system-packages.json" 2>/dev/null | head -1)" echo " rust channel: ${channel:-stable} bun: ${bun_pin:-latest}" step "rust" if ! have rustup; then run bash -c "curl --proto '=https' --tlsv1.2 -fsSL https://sh.rustup.rs | sh -s -- -y --no-modify-path --default-toolchain none" fi if have rustup || (( dry == 1 )); then run rustup toolchain install "${channel:-stable}" --profile minimal -c rustfmt -c clippy run rustup default "${channel:-stable}" fi step "bun" if ! have bun; then run bash -c "curl -fsSL https://bun.sh/install | bash -s ${bun_pin:+bun-v$bun_pin}" fi step "uv and Graphify" if ! have uv; then run bash -c "curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR=$HOME/.local/bin INSTALLER_NO_MODIFY_PATH=1 sh" fi if have uv || (( dry == 1 )); then have graphify || run uv tool install graphifyy run graphify install fi step "build and search tools (cargo-binstall, prebuilt binaries only)" if ! have cargo-binstall; then run bash -c "curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash" fi if have cargo-binstall || (( dry == 1 )); then for pair in just:just sccache:sccache cargo-nextest:cargo-nextest nu:nu fd:fd-find rg:ripgrep bat:bat sd:sd; do have "${pair%%:*}" || run cargo binstall -y --locked --disable-strategies compile "${pair##*:}" done fi # Where the proxy blocks api.github.com/graphql (cargo-binstall cannot resolve releases), the same cargo-quickinstall # archives are downloaded directly; the missing command lists come from config/developer-cli-tools.json and the # cargoTools of config/system-packages.json. if have bun || (( dry == 1 )); then missing="" for crate in nu choose du-dust dysk procs bottom zoxide git-delta xh tealdeer gitui just atuin starship zellij sccache \ cargo-nextest cargo-deny cargo-audit cargo-machete cargo-expand cargo-hack cargo-zigbuild cargo-xwin cargo-update hyperfine; do case "$crate" in du-dust) cmd=dust ;; bottom) cmd=btm ;; git-delta) cmd=delta ;; tealdeer) cmd=tldr ;; cargo-update) cmd=cargo-install-update ;; *) cmd="$crate" ;; esac have "$cmd" || missing="$missing $crate" done [[ -z "$missing" ]] || run bun "$root/scripts/dev/cloud-quickinstall.ts" $missing fi # `sd` is required by the workspace code-edit tests; it is small, so compile it when no prebuilt binary is served. have sd || run cargo install sd --locked step "Bun lint and format tools (workspace gates)" if have bun || (( dry == 1 )); then have oxlint && have oxfmt || run bun add -g oxlint oxfmt fi step "repository dependencies" if [[ -f "$root/bun.lock" ]] && (have bun || (( dry == 1 ))); then run bash -c "cd '$root' && bun install --frozen-lockfile" fi step "Aphrody and Yolo binaries (download server, when announced)" if [[ -n "${APHRODY_DOWNLOAD_BASE:-}" ]]; then run bash -c "curl -fsSL '$APHRODY_DOWNLOAD_BASE/install.sh' | sh" else echo " APHRODY_DOWNLOAD_BASE is not set: skipped" fi step "code graph (local, offline)" if have graphify && [[ -f "$root/.graphifyignore" ]]; then run bash -c "cd '$root' && graphify update ." fi step "done" exit 0