From bf1529b3541c0640e826c83995f33efe6f9874e9 Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Sat, 20 Jun 2026 13:24:52 +0530 Subject: [PATCH] feat: Auto updator --- VERSION | 2 +- b.sh | 50 +++++++++++++++++++++------- bootstrap.sh | 13 ++++++++ commands/help.sh | 1 + commands/up.sh | 82 ++++++++++++++++++++++++++++++++++++++++++++++ readme.md | 14 ++++++++ routes.sh | 8 +++++ scripts/pre-commit | 43 ++++++++++++++++++++++++ 8 files changed, 200 insertions(+), 13 deletions(-) create mode 100644 commands/up.sh create mode 100644 scripts/pre-commit diff --git a/VERSION b/VERSION index 3eefcb9..7dea76e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.0 +1.0.1 diff --git a/b.sh b/b.sh index 4129ffe..2f4de1c 100755 --- a/b.sh +++ b/b.sh @@ -15,19 +15,45 @@ b() { local last_update=0 [ -f "$last_update_file" ] && last_update=$(cat "$last_update_file" 2>/dev/null || echo 0) - # Update everything once every 24 hours, or if routes.sh is missing - if [ $((current_time - last_update)) -gt 86400 ] || [ ! -f "$routes_file" ]; then - local bootstrap_url="https://git.adityagupta.dev/sortedcord/bootstrap/raw/branch/master/bootstrap.sh" - local tmp_bootstrap - tmp_bootstrap="$(mktemp)" - - # Download and run the bootstrap installer to update all CLI files - if curl -fsSL "$bootstrap_url" -o "$tmp_bootstrap" 2>/dev/null || wget -qO "$tmp_bootstrap" "$bootstrap_url" 2>/dev/null; then - if bash "$tmp_bootstrap"; then - echo "$current_time" > "$last_update_file" + # Version comparison helper (returns 0 if $1 < $2, 1 otherwise) + _version_lt() { + [ "$1" = "$2" ] && return 1 + local IFS=. + local i ver1=($1) ver2=($2) + for ((i=${#ver1[@]}; i<3; i++)); do ver1[i]=0; done + for ((i=${#ver2[@]}; i<3; i++)); do ver2[i]=0; done + for ((i=0; i<3; i++)); do + if ((10#${ver1[i]} < 10#${ver2[i]})); then + return 0 + elif ((10#${ver1[i]} > 10#${ver2[i]})); then + return 1 + fi + done + return 1 + } + + # Auto-update check: once every 24 hours, or if routes.sh or VERSION is missing + if [ $((current_time - last_update)) -gt 86400 ] || [ ! -f "$routes_file" ] || [ ! -f "$routes_dir/VERSION" ]; then + # Update the timestamp immediately to prevent spamming on connection errors + echo "$current_time" > "$last_update_file" + + local base_url="https://git.adityagupta.dev/sortedcord/bootstrap/raw/branch/master" + local local_ver="0.0.0" + [ -f "$routes_dir/VERSION" ] && local_ver=$(cat "$routes_dir/VERSION" 2>/dev/null | tr -d '[:space:]') + + local remote_ver + if remote_ver=$(curl -fsSL "$base_url/VERSION" 2>/dev/null || wget -qO- "$base_url/VERSION" 2>/dev/null); then + remote_ver=$(echo "$remote_ver" | tr -d '[:space:]') + if [ -n "$remote_ver" ] && _version_lt "$local_ver" "$remote_ver"; then + echo "New version $remote_ver available (local: $local_ver). Auto-updating..." >&2 + local tmp_bootstrap + tmp_bootstrap="$(mktemp)" + if curl -fsSL "$base_url/bootstrap.sh" -o "$tmp_bootstrap" 2>/dev/null || wget -qO "$tmp_bootstrap" "$base_url/bootstrap.sh" 2>/dev/null; then + bash "$tmp_bootstrap" >/dev/null 2>&1 + fi + rm -f "$tmp_bootstrap" fi fi - rm -f "$tmp_bootstrap" fi if [ ! -f "$routes_file" ]; then @@ -48,7 +74,7 @@ _b_completion() { # If completing the first argument after 'b' if [ "$COMP_CWORD" -eq 1 ]; then - opts="all con bye" + opts="all con bye up" local routes_file="$HOME/.config/bootstrap/routes.sh" if [ -f "$routes_file" ]; then diff --git a/bootstrap.sh b/bootstrap.sh index 8c8ddc9..38add20 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -88,6 +88,7 @@ install_bootstrap() { # List of all files to download/copy local files=( + "VERSION" "b.sh" "routes.sh" "lib/common.sh" @@ -96,6 +97,7 @@ install_bootstrap() { "commands/help.sh" "commands/con.sh" "commands/uninstall.sh" + "commands/up.sh" ) if [ -f "$_SCRIPT_DIR/b.sh" ] && [ -f "$_SCRIPT_DIR/routes.sh" ]; then @@ -180,6 +182,17 @@ EOF # Initialize the last update timestamp to prevent immediate update on first execution (Fix 2) local last_update_file="$routes_dir/.last_b_update" date +%s 2>/dev/null > "$last_update_file" || date +%s > "$last_update_file" + + # Set up pre-commit hook if in a git repository locally + if [ -d "$_SCRIPT_DIR/.git" ]; then + log_info "Setting up git pre-commit hook..." + mkdir -p "$_SCRIPT_DIR/.git/hooks" + if [ -f "$_SCRIPT_DIR/scripts/pre-commit" ]; then + cp "$_SCRIPT_DIR/scripts/pre-commit" "$_SCRIPT_DIR/.git/hooks/pre-commit" + chmod +x "$_SCRIPT_DIR/.git/hooks/pre-commit" + log_success "Pre-commit hook installed to .git/hooks/pre-commit" + fi + fi } # Only execute installation if not sourced (Fix 3) diff --git a/commands/help.sh b/commands/help.sh index a72bea4..a51a7d8 100644 --- a/commands/help.sh +++ b/commands/help.sh @@ -5,6 +5,7 @@ echo "Available bootstrap commands:" # Non-installers first (aligned to 6 chars width) printf " %-6s - %s\n" "all" "List all available commands" printf " %-6s - %s\n" "con" "Edit config (e.g. b con nvim)" +printf " %-6s - %s\n" "up" "Check for updates and update Bootstrap CLI" printf " %-6s - %s\n" "bye" "Uninstall Bootstrap CLI helper" # Installers second diff --git a/commands/up.sh b/commands/up.sh new file mode 100644 index 0000000..0e483bd --- /dev/null +++ b/commands/up.sh @@ -0,0 +1,82 @@ +# Command: up +# Manually checks for updates and runs the updater if a newer version is found. + +# Source libraries if needed +if [ -z "${_LIB_COMMON_SOURCED:-}" ]; then + _LIB_DIR="${BOOTSTRAP_DIR:-$HOME/.config/bootstrap}/lib" + . "$_LIB_DIR/common.sh" +fi + +require_bash + +log_info "Checking for updates..." + +local_ver="0.0.0" +version_file="${BOOTSTRAP_DIR:-$HOME/.config/bootstrap}/VERSION" +if [ -f "$version_file" ]; then + local_ver=$(cat "$version_file" | tr -d '[:space:]') +fi + +base_url="https://git.adityagupta.dev/sortedcord/bootstrap/raw/branch/master" +remote_ver=$(curl -fsSL "$base_url/VERSION" 2>/dev/null || wget -qO- "$base_url/VERSION" 2>/dev/null) +remote_ver=$(echo "$remote_ver" | tr -d '[:space:]') + +if [ -z "$remote_ver" ]; then + log_error "Failed to fetch remote version. Please check your internet connection." + exit 1 +fi + +# Version comparison helper +version_lt() { + [ "$1" = "$2" ] && return 1 + local IFS=. + local i ver1=($1) ver2=($2) + for ((i=${#ver1[@]}; i<3; i++)); do ver1[i]=0; done + for ((i=${#ver2[@]}; i<3; i++)); do ver2[i]=0; done + for ((i=0; i<3; i++)); do + if ((10#${ver1[i]} < 10#${ver2[i]})); then + return 0 + elif ((10#${ver1[i]} > 10#${ver2[i]})); then + return 1 + fi + done + return 1 +} + +log_info "Local version: $local_ver" +log_info "Remote version: $remote_ver" + +force_update=false +if [ "${1:-}" = "--force" ] || [ "${1:-}" = "-f" ]; then + force_update=true +fi + +if version_lt "$local_ver" "$remote_ver" || [ "$force_update" = true ]; then + if [ "$force_update" = true ]; then + log_info "Force updating..." + else + log_info "New version available! Updating..." + fi + + tmp_bootstrap="$(mktemp)" + if curl -fsSL "$base_url/bootstrap.sh" -o "$tmp_bootstrap" || wget -qO "$tmp_bootstrap" "$base_url/bootstrap.sh"; then + # Run bootstrap.sh in foreground + if bash "$tmp_bootstrap"; then + # Update the last update timestamp + date +%s > "${BOOTSTRAP_DIR:-$HOME/.config/bootstrap}/.last_b_update" 2>/dev/null || true + log_success "Bootstrap CLI successfully updated to version $remote_ver!" + else + log_error "Failed to execute bootstrap installer." + rm -f "$tmp_bootstrap" + exit 1 + fi + else + log_error "Failed to download update installer." + rm -f "$tmp_bootstrap" + exit 1 + fi + rm -f "$tmp_bootstrap" +else + log_success "You are already on the latest version ($local_ver)." + log_info "To force reinstall, run: b up --force" +fi diff --git a/readme.md b/readme.md index c77911c..2a973f7 100644 --- a/readme.md +++ b/readme.md @@ -44,6 +44,13 @@ b con i3 It automatically fuzzy-finds the folder in case there is no exact match. +To check for updates and update the tool manually: + +```bash +b up +# Or to force a reinstall of the CLI files: +b up --force +``` ## Uninstallation @@ -55,6 +62,13 @@ b bye Then reload your shell configuration or run `unset -f b` to clear the function definition from your current terminal session. +## Development + +If you are developing this tool locally: + +1. Clone the repository. +2. Run `./bootstrap.sh` to install the CLI from your local copy. This will also automatically install a Git pre-commit hook (`scripts/pre-commit`) that auto-increments the patch version in the `VERSION` file on each commit. + ## Philosophy These scripts are designed for my own systems first. diff --git a/routes.sh b/routes.sh index 69d102e..9bbfb46 100755 --- a/routes.sh +++ b/routes.sh @@ -83,6 +83,14 @@ for script in "${SCRIPTS[@]}"; do exit 1 fi ;; + up) + if [ -f "$BOOTSTRAP_DIR/commands/up.sh" ]; then + . "$BOOTSTRAP_DIR/commands/up.sh" "$@" + else + log_error "Update command script not found." + exit 1 + fi + ;; bye) if [ -f "$BOOTSTRAP_DIR/commands/uninstall.sh" ]; then . "$BOOTSTRAP_DIR/commands/uninstall.sh" diff --git a/scripts/pre-commit b/scripts/pre-commit new file mode 100644 index 0000000..f55b8c0 --- /dev/null +++ b/scripts/pre-commit @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# Git pre-commit hook to automatically bump the patch version in VERSION file. +# It only increments the version if files other than VERSION are staged for commit. + +set -euo pipefail + +VERSION_FILE="VERSION" + +if [ ! -f "$VERSION_FILE" ]; then + echo "1.0.0" > "$VERSION_FILE" + git add "$VERSION_FILE" + exit 0 +fi + +# Check if there are staged changes other than the VERSION file +if ! git diff --cached --name-only | grep -qv "^$VERSION_FILE$"; then + # No other files staged, skip version bump + exit 0 +fi + +# Read current version +current_version=$(cat "$VERSION_FILE" | tr -d '[:space:]') + +# Basic regex validation for semantic versioning (X.Y.Z) +if [[ ! "$current_version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Error: Current version '$current_version' in $VERSION_FILE is not in X.Y.Z format." >&2 + exit 1 +fi + +# Parse version components +IFS='.' read -r major minor patch <<< "$current_version" + +# Increment patch version +patch=$((patch + 1)) +new_version="$major.$minor.$patch" + +# Write to VERSION file +echo "$new_version" > "$VERSION_FILE" + +# Add VERSION file to the commit +git add "$VERSION_FILE" + +echo "[pre-commit] Automatically bumped version from $current_version to $new_version"