install.sh
Rama main · réplica de lectura del repositorio original.
#!/bin/sh
# wshm installer — https://github.com/wshm-dev/wshm
#
# Usage:
# curl -fsSL https://raw.githubusercontent.com/wshm-dev/wshm/main/install.sh | sh
#
# Re-run to upgrade. Pass flags via `sh -s --`:
# curl -fsSL https://raw.githubusercontent.com/wshm-dev/wshm/main/install.sh | sh -s -- --version v0.28.3
# curl -fsSL https://raw.githubusercontent.com/wshm-dev/wshm/main/install.sh | sh -s -- --dir /usr/local/bin
#
# Every download is verified against the release's checksums.txt (SHA256).
#
# Supported platforms (matches the GitHub release pipeline):
# - Linux x86_64 (x86_64-unknown-linux-gnu)
# - Linux aarch64 (aarch64-unknown-linux-gnu)
# - macOS arm64 (aarch64-apple-darwin)
# - macOS x86_64 (x86_64-apple-darwin)
# - Windows x86_64 via Git Bash / MSYS / Cygwin (x86_64-pc-windows-msvc)
set -eu
REPO="wshm-dev/wshm"
BINARY_NAME="wshm"
INSTALL_DIR="${HOME}/.local/bin"
VERSION=""
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
info() { printf "${GREEN}[INFO]${NC} %s\n" "$1"; }
warn() { printf "${YELLOW}[WARN]${NC} %s\n" "$1"; }
error() { printf "${RED}[ERROR]${NC} %s\n" "$1" >&2; exit 1; }
usage() {
cat <<EOF
wshm installer — installs or upgrades wshm from GitHub releases.
Usage: install.sh [--dir <path>] [--version <tag>] [--help]
Options:
--dir <path> Install directory (default: \$HOME/.local/bin)
--version <tag> Release tag to install (default: latest, e.g. v0.28.3)
-h, --help Show this help
Re-running this script upgrades an existing installation in place.
Each download is verified against the release's checksums.txt (SHA256).
EOF
}
parse_args() {
while [ $# -gt 0 ]; do
case "$1" in
--dir)
[ $# -ge 2 ] || error "--dir requires a path"
INSTALL_DIR="$2"
shift 2
;;
--version)
[ $# -ge 2 ] || error "--version requires a tag"
VERSION="$2"
shift 2
;;
-h|--help)
usage
exit 0
;;
*)
error "Unknown argument: $1 (use --help)"
;;
esac
done
}
detect_os() {
case "$(uname -s)" in
Linux*) OS="linux"; TARGET_SUFFIX="unknown-linux-gnu";;
MINGW*|MSYS*|CYGWIN*) OS="windows"; TARGET_SUFFIX="pc-windows-msvc";;
Darwin*) OS="macos"; TARGET_SUFFIX="apple-darwin";;
*) error "Unsupported OS: $(uname -s). wshm supports Linux, macOS, and Windows binary releases.";;
esac
}
detect_arch() {
case "$(uname -m)" in
x86_64|amd64) ARCH="x86_64";;
arm64|aarch64) ARCH="aarch64";;
*) error "Unsupported architecture: $(uname -m)";;
esac
if [ "$OS" = "windows" ] && [ "$ARCH" != "x86_64" ]; then
error "Windows release is only published for x86_64 (got: $ARCH)."
fi
}
require() {
command -v "$1" >/dev/null 2>&1 || error "$1 is required but not installed"
}
get_latest_version() {
if [ -n "$VERSION" ]; then
return
fi
VERSION=$(curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" \
| grep '"tag_name":' \
| head -1 \
| sed -E 's/.*"tag_name"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/')
[ -n "$VERSION" ] || error "Failed to determine latest release from GitHub API"
}
# Print currently installed version, or empty if not installed.
current_version() {
if [ -x "${INSTALL_DIR}/${BINARY_NAME}" ]; then
"${INSTALL_DIR}/${BINARY_NAME}" --version 2>/dev/null | awk '{print $2}'
elif command -v "$BINARY_NAME" >/dev/null 2>&1; then
"$BINARY_NAME" --version 2>/dev/null | awk '{print $2}'
fi
}
verify_sha256() {
archive="$1"
expected="$2"
if command -v sha256sum >/dev/null 2>&1; then
actual=$(sha256sum "$archive" | awk '{print $1}')
elif command -v shasum >/dev/null 2>&1; then
actual=$(shasum -a 256 "$archive" | awk '{print $1}')
else
error "Neither sha256sum nor shasum found — cannot verify integrity. Install one and retry."
fi
if [ "$expected" != "$actual" ]; then
error "SHA256 mismatch — refusing to install.
expected: ${expected}
got: ${actual}
The download was tampered with or corrupted."
fi
info "SHA256 verified: ${actual}"
}
install_binary() {
TARGET="${ARCH}-${TARGET_SUFFIX}"
if [ "$OS" = "windows" ]; then
EXT="zip"
: "${INSTALL_DIR:=${LOCALAPPDATA:-$HOME}/wshm/bin}"
else
EXT="tar.gz"
fi
mkdir -p "$INSTALL_DIR" || error "Cannot create install directory: $INSTALL_DIR"
ARCHIVE_NAME="${BINARY_NAME}-${TARGET}.${EXT}"
BASE_URL="https://github.com/${REPO}/releases/download/${VERSION}"
TEMP_DIR=$(mktemp -d)
trap 'rm -rf "$TEMP_DIR"' EXIT
ARCHIVE="${TEMP_DIR}/${ARCHIVE_NAME}"
info "Downloading ${ARCHIVE_NAME}"
curl -fsSL "${BASE_URL}/${ARCHIVE_NAME}" -o "$ARCHIVE" \
|| error "Failed to download ${BASE_URL}/${ARCHIVE_NAME}"
# SHA256 verification — mandatory, never skipped.
CHECKSUMS_FILE="${TEMP_DIR}/checksums.txt"
info "Downloading checksums.txt"
curl -fsSL "${BASE_URL}/checksums.txt" -o "$CHECKSUMS_FILE" \
|| error "Failed to download checksums.txt (required for integrity verification)"
# checksums.txt format: "<sha256> <filename>" (two spaces, sha256sum default).
EXPECTED_SHA=$(awk -v name="$ARCHIVE_NAME" '$2 == name {print $1; exit}' "$CHECKSUMS_FILE")
[ -n "$EXPECTED_SHA" ] || error "No checksum entry for ${ARCHIVE_NAME} in checksums.txt"
verify_sha256 "$ARCHIVE" "$EXPECTED_SHA"
info "Extracting"
if [ "$OS" = "windows" ]; then
require unzip
unzip -oq "$ARCHIVE" -d "$TEMP_DIR"
DEST="${INSTALL_DIR}/${BINARY_NAME}.exe"
mv -f "${TEMP_DIR}/${BINARY_NAME}.exe" "$DEST"
else
tar -xzf "$ARCHIVE" -C "$TEMP_DIR"
DEST="${INSTALL_DIR}/${BINARY_NAME}"
mv -f "${TEMP_DIR}/${BINARY_NAME}" "$DEST"
chmod +x "$DEST"
fi
info "Installed to ${DEST}"
}
print_path_warning() {
case ":${PATH:-}:" in
*":${INSTALL_DIR}:"*) ;;
*)
warn "${INSTALL_DIR} is not in your PATH. Add it with:"
printf ' export PATH="%s:$PATH"\n' "$INSTALL_DIR"
;;
esac
}
main() {
parse_args "$@"
require curl
require uname
detect_os
detect_arch
info "Platform: ${OS} ${ARCH}"
PREVIOUS_VERSION=$(current_version || true)
get_latest_version
info "Target version: ${VERSION}"
if [ -n "$PREVIOUS_VERSION" ]; then
info "Upgrading wshm (current: ${PREVIOUS_VERSION})"
else
info "Installing wshm"
fi
install_binary
echo ""
if [ -n "$PREVIOUS_VERSION" ]; then
info "Upgrade complete: ${PREVIOUS_VERSION} → ${VERSION}"
else
info "Installation complete: ${VERSION}"
fi
echo ""
echo " Next steps:"
echo " 1. cd your-repo"
echo " 2. wshm config init # creates .wshm/config.toml"
echo " 3. wshm login # authenticate with GitHub + AI provider"
echo " 4. wshm sync && wshm triage"
echo ""
print_path_warning
}
main "$@"