diff --git a/README.md b/README.md index d5e3774..864320b 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,8 @@ A minimalist terminal chat server with Vim-style interface over SSH. ```sh curl -sSL https://raw.githubusercontent.com/m1ngsama/TNT/main/install.sh | sh ``` +The installer verifies the downloaded release binary against `checksums.txt` +before installing it. **From source:** ```sh diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 3d8c3b8..41b9f98 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -37,6 +37,8 @@ - The tag-triggered release workflow now builds on native x64/arm64 runners, verifies artifact architecture, emits one checksum file, and creates a draft release for manual review instead of publishing immediately. +- The one-line installer now downloads `checksums.txt`, verifies the selected + binary before installation, and fails fast on missing release assets. ## 2026-05-18 - Interactive input polish diff --git a/install.sh b/install.sh index 2935245..144b5c7 100755 --- a/install.sh +++ b/install.sh @@ -1,24 +1,51 @@ #!/bin/sh -# TNT Auto-deploy script +# TNT installer # Usage: curl -sSL https://raw.githubusercontent.com/m1ngsama/TNT/main/install.sh | sh set -e VERSION=${VERSION:-latest} INSTALL_DIR=${INSTALL_DIR:-/usr/local/bin} +REPO="m1ngsama/TNT" + +fail() { + echo "ERROR: $*" >&2 + exit 1 +} + +need_cmd() { + command -v "$1" >/dev/null 2>&1 || fail "$1 is required" +} + +sha256_of() { + 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 + return 1 + fi +} + +need_cmd curl +need_cmd awk # Detect OS and architecture OS=$(uname -s | tr '[:upper:]' '[:lower:]') ARCH=$(uname -m) +case "$OS" in + linux|darwin) ;; + *) fail "Unsupported OS: $OS" ;; +esac + case "$ARCH" in x86_64) ARCH="amd64" ;; aarch64|arm64) ARCH="arm64" ;; - *) echo "Unsupported architecture: $ARCH"; exit 1 ;; + *) fail "Unsupported architecture: $ARCH" ;; esac BINARY="tnt-${OS}-${ARCH}" -REPO="m1ngsama/TNT" echo "=== TNT Installer ===" echo "OS: $OS" @@ -29,34 +56,57 @@ echo "" # Get latest version if not specified if [ "$VERSION" = "latest" ]; then echo "Fetching latest version..." - VERSION=$(curl -sSL "https://api.github.com/repos/$REPO/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') + VERSION=$(curl -fsSL "https://api.github.com/repos/$REPO/releases/latest" | + sed -n 's/.*"tag_name":[[:space:]]*"\([^"]*\)".*/\1/p' | + head -n 1) + [ -n "$VERSION" ] || fail "Could not determine latest release version" fi echo "Installing version: $VERSION" # Download URL="https://github.com/$REPO/releases/download/$VERSION/$BINARY" +CHECKSUM_URL="https://github.com/$REPO/releases/download/$VERSION/checksums.txt" echo "Downloading from: $URL" -TMP_FILE=$(mktemp) -if ! curl -sSL -o "$TMP_FILE" "$URL"; then - echo "ERROR: Failed to download $BINARY" - rm -f "$TMP_FILE" - exit 1 -fi +TMP_FILE=$(mktemp "${TMPDIR:-/tmp}/tnt.XXXXXX") +CHECKSUM_FILE=$(mktemp "${TMPDIR:-/tmp}/tnt-checksums.XXXXXX") +cleanup() { + rm -f "$TMP_FILE" "$CHECKSUM_FILE" +} +trap cleanup EXIT INT TERM + +curl -fsSL -o "$TMP_FILE" "$URL" || fail "Failed to download $BINARY" + +echo "Downloading checksums from: $CHECKSUM_URL" +curl -fsSL -o "$CHECKSUM_FILE" "$CHECKSUM_URL" || + fail "Failed to download checksums.txt" + +EXPECTED_SHA=$(awk -v name="$BINARY" '$2 == name { print $1; exit }' "$CHECKSUM_FILE") +[ -n "$EXPECTED_SHA" ] || fail "No checksum entry found for $BINARY" + +ACTUAL_SHA=$(sha256_of "$TMP_FILE") || + fail "sha256sum or shasum is required for checksum verification" + +[ "$ACTUAL_SHA" = "$EXPECTED_SHA" ] || + fail "Checksum mismatch for $BINARY" + +echo "Checksum verified: $ACTUAL_SHA" # Install chmod +x "$TMP_FILE" -if [ -w "$INSTALL_DIR" ]; then - mv "$TMP_FILE" "$INSTALL_DIR/tnt" +if [ -d "$INSTALL_DIR" ] && [ -w "$INSTALL_DIR" ]; then + install -m 755 "$TMP_FILE" "$INSTALL_DIR/tnt" else echo "Need sudo for installation to $INSTALL_DIR" - sudo mv "$TMP_FILE" "$INSTALL_DIR/tnt" + need_cmd sudo + sudo mkdir -p "$INSTALL_DIR" + sudo install -m 755 "$TMP_FILE" "$INSTALL_DIR/tnt" fi echo "" -echo "✓ TNT installed successfully to $INSTALL_DIR/tnt" +echo "TNT installed successfully to $INSTALL_DIR/tnt" echo "" echo "Run with:" echo " tnt" diff --git a/scripts/release_check.sh b/scripts/release_check.sh index aece2ff..06eec2c 100755 --- a/scripts/release_check.sh +++ b/scripts/release_check.sh @@ -14,6 +14,7 @@ Default checks: - clean build - unit tests - staged install layout with PREFIX=/usr and DESTDIR + - installer shell syntax - Arch/Homebrew packaging syntax Environment: @@ -97,6 +98,9 @@ make DESTDIR="$tmpdir" PREFIX=/usr install-systemd [ -f "$tmpdir/usr/lib/systemd/system/tnt.service" ] || fail "missing systemd unit: /usr/lib/systemd/system/tnt.service" +step "checking installer syntax" +sh -n install.sh + step "checking packaging syntax" if command -v bash >/dev/null 2>&1; then bash -n packaging/arch/PKGBUILD