install.sh
Rama main · réplica de lectura del repositorio original.
#!/usr/bin/env bash
# any-console のバイナリ配布インストーラ。
#
# GitHub Releases から自分の OS/arch 向けの tarball を取得し、
# ~/.any-console へ展開する(Rust/cargo/Node/npm/Python venv の事前準備は不要。
# 実行時に git・tmux のみが必要)。
#
# 使い方:
# curl -fsSL https://raw.githubusercontent.com/kt0319/any-console/main/install.sh | bash
#
# 冪等: 既存の ~/.any-console に対して再実行すると、data/・config.json・certs/
# には一切触れずバイナリ・スクリプト・agent_manifests のみを最新版へ差し替える
# (= アップデート導線もこのスクリプトの再実行に一本化している。
# `./any-console update` もバイナリ配布モードでは内部的にこのスクリプトを
# 再実行する)。サービスが既に登録済みなら(=更新シナリオ)反映のため
# 自動で再起動する。新規インストールでは対話が必要なサービス登録は行わず
# 案内のみ表示する(標準入力がパイプ占有される curl | bash 実行を想定)。
set -euo pipefail
REPO="kt0319/any-console"
INSTALL_DIR="${ANY_CONSOLE_INSTALL_DIR:-$HOME/.any-console}"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m'
info() { echo -e "${GREEN}$1${NC}"; }
warn() { echo -e "${YELLOW}$1${NC}"; }
error() { echo -e "${RED}Error: $1${NC}" >&2; exit 1; }
detect_target() {
local os arch
os="$(uname -s)"
arch="$(uname -m)"
case "$os" in
Linux) os="unknown-linux-gnu" ;;
Darwin) os="apple-darwin" ;;
*) error "Unsupported OS: $os (any-console supports Linux and macOS)" ;;
esac
case "$arch" in
x86_64|amd64) arch="x86_64" ;;
aarch64|arm64) arch="aarch64" ;;
*) error "Unsupported architecture: $arch" ;;
esac
echo "${arch}-${os}"
}
require_cmd() {
command -v "$1" >/dev/null 2>&1 || error "$1 is required but not found. Please install it first."
}
sha256_of_file() {
if command -v sha256sum >/dev/null 2>&1; then
sha256sum "$1" | awk '{print $1}'
elif command -v shasum >/dev/null 2>&1; then
shasum -a 256 "$1" | awk '{print $1}'
else
error "Neither sha256sum nor shasum found — cannot verify checksum"
fi
}
verify_checksum() {
local file="$1" checksum_file="$2" expected actual
expected="$(awk '{print $1}' "$checksum_file")"
[ -n "$expected" ] || error "Checksum file is empty or malformed: $checksum_file"
actual="$(sha256_of_file "$file")"
if [ "$expected" != "$actual" ]; then
error "Checksum mismatch for $(basename "$file"): expected $expected, got $actual"
fi
info "Checksum verified"
}
# 一時パスへコピーしてから rename する(同一ファイルシステム内の mv は
# atomic)。ダウンロード直後の展開物から INSTALL_DIR へ反映する際、コピー
# 途中でプロセスが落ちても INSTALL_DIR 側の既存ファイルが破損した状態で
# 中途半端に残らないようにするため。
atomic_install_file() {
local src="$1" dest="$2" tmp
tmp="${dest}.new.$$"
cp "$src" "$tmp"
mv "$tmp" "$dest"
}
atomic_install_dir() {
local src="$1" dest="$2" tmp old
tmp="${dest}.new.$$"
rm -rf "$tmp"
cp -R "$src" "$tmp"
old="${dest}.old.$$"
if [ -d "$dest" ]; then
mv "$dest" "$old"
fi
mv "$tmp" "$dest"
[ -d "$old" ] && rm -rf "$old"
return 0
}
main() {
require_cmd curl
require_cmd tar
local target
target="$(detect_target)"
info "Detected platform: $target"
local tag
if [ -n "${ANY_CONSOLE_VERSION:-}" ]; then
tag="$ANY_CONSOLE_VERSION"
else
info "Looking up the latest release..."
tag="$(curl -fsSL "https://api.github.com/repos/$REPO/releases/latest" | grep -o '"tag_name": *"[^"]*"' | head -1 | cut -d'"' -f4)"
[ -n "$tag" ] || error "Could not determine the latest release tag. Check https://github.com/$REPO/releases"
fi
local current_version=""
[ -f "$INSTALL_DIR/VERSION" ] && current_version="$(cat "$INSTALL_DIR/VERSION")"
if [ "$current_version" = "$tag" ] && [ "${ANY_CONSOLE_FORCE:-}" != "1" ]; then
info "any-console is already up to date ($tag)"
return
fi
info "Installing any-console $tag${current_version:+ (currently $current_version)}"
local asset="any-console-${tag}-${target}.tar.gz"
local url="https://github.com/$REPO/releases/download/$tag/$asset"
local checksum_url="${url}.sha256"
local tmp_dir
tmp_dir="$(mktemp -d)"
trap 'rm -rf "$tmp_dir"' EXIT
info "Downloading $asset..."
curl -fsSL -o "$tmp_dir/$asset" "$url" || error "Download failed: $url"
info "Downloading checksum..."
curl -fsSL -o "$tmp_dir/$asset.sha256" "$checksum_url" || error "Checksum download failed: $checksum_url"
verify_checksum "$tmp_dir/$asset" "$tmp_dir/$asset.sha256"
info "Extracting..."
tar xzf "$tmp_dir/$asset" -C "$tmp_dir"
local stage="$tmp_dir/any-console-${tag}-${target}"
[ -d "$stage" ] || error "Unexpected archive layout (missing $stage)"
mkdir -p "$INSTALL_DIR"
# data/・config.json・certs/ には一切触れず、配布物のみを差し替える。
atomic_install_file "$stage/any-console-server" "$INSTALL_DIR/any-console-server"
atomic_install_file "$stage/any-console" "$INSTALL_DIR/any-console"
atomic_install_file "$stage/VERSION" "$INSTALL_DIR/VERSION"
atomic_install_dir "$stage/agent_manifests" "$INSTALL_DIR/agent_manifests"
# scripts/(Claude Code hooks 用)は同梱を始めた版から入る。古い tarball
# には無いため存在チェック付きで反映する。
if [ -d "$stage/scripts" ]; then
atomic_install_dir "$stage/scripts" "$INSTALL_DIR/scripts"
chmod +x "$INSTALL_DIR/scripts/"*.sh
fi
chmod +x "$INSTALL_DIR/any-console-server" "$INSTALL_DIR/any-console"
info "Installed to $INSTALL_DIR"
echo ""
info "Running non-interactive setup (dependency check, default bind)..."
(cd "$INSTALL_DIR" && ./any-console setup --non-interactive)
echo ""
if (cd "$INSTALL_DIR" && ./any-console is-registered) 2>/dev/null; then
info "Restarting the any-console service to apply the update..."
if (cd "$INSTALL_DIR" && ./any-console restart); then
info "=== Update complete ($tag) ==="
else
warn "Could not restart automatically — run: cd $INSTALL_DIR && ./any-console restart"
fi
else
info "=== Install complete ($tag) ==="
echo ""
echo "Service registration (systemd/launchd) requires an interactive step."
echo "To finish setup and start any-console as a background service, run:"
echo ""
echo " cd $INSTALL_DIR && ./any-console setup"
fi
echo ""
echo "To update later, just re-run this installer (or run './any-console update'):"
echo ""
echo " curl -fsSL https://raw.githubusercontent.com/$REPO/main/install.sh | bash"
}
main "$@"