Files
dotfiles/lib/update_checker.sh
Eric Turner 3ddad4b543 feat: add core library functions for package and profile management
- Add package_manager.sh for handling system package installations
- Add profile_manager.sh for managing machine-specific configurations
- Add update_checker.sh for version control and updates

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-02 20:21:25 -06:00

409 lines
14 KiB
Bash
Executable File
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# Package Update Checker for Dotfiles
# Efficiently checks for updates without running expensive update commands
UPDATE_LOG="$HOME/.dotfiles/.update.log"
UPDATE_CACHE="$HOME/.dotfiles/.update_cache"
LAST_CHECK_FILE="$HOME/.dotfiles/.last_update_check"
# Function to log update operations
update_log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$UPDATE_LOG"
}
# Function to check if we should check for updates (avoid too frequent checks)
should_check_updates() {
local current_time=$(date +%s)
local last_check_time=0
local force_check=${1:-false}
if [[ -f "$LAST_CHECK_FILE" ]]; then
last_check_time=$(cat "$LAST_CHECK_FILE")
fi
# If force check, always allow
if [[ "$force_check" == "true" ]]; then
return 0
fi
# Only check if more than 6 hours (21600 seconds) have passed
local time_diff=$((current_time - last_check_time))
if [[ $time_diff -lt 21600 ]]; then
return 1
fi
return 0
}
# Function to check npm packages for updates
check_npm_updates() {
local packages_to_check=()
local updates_available=false
update_log "Checking npm packages for updates"
# Get profile-aware package list
if [[ -f "$HOME/.dotfiles/lib/profile_manager.sh" ]]; then
source "$HOME/.dotfiles/lib/profile_manager.sh"
local binary_packages=$(get_profile_packages "binary")
local npm_packages=$(get_profile_packages "npm")
# Add binary packages that are npm-based
for package in $binary_packages; do
case "$package" in
"claude-code") packages_to_check+=("@anthropic-ai/claude-code") ;;
"gemini-cli") packages_to_check+=("@google/gemini-cli") ;;
"task-master") packages_to_check+=("task-master-ai") ;;
esac
done
# Add npm packages
for package in $npm_packages; do
packages_to_check+=("$package")
done
else
# Fallback list
packages_to_check=("@anthropic-ai/claude-code" "@google/gemini-cli" "task-master-ai" "pm2")
fi
if [[ ${#packages_to_check[@]} -eq 0 ]]; then
echo "No npm packages to check"
return 1
fi
echo "🔍 Checking npm packages for updates..."
# Use npm outdated to check for updates (much faster than npm update)
local outdated_output
if command -v npm >/dev/null 2>&1; then
# npm outdated returns non-zero if packages are outdated, but that's expected
outdated_output=$(npm outdated -g --json 2>/dev/null || true)
if [[ -n "$outdated_output" && "$outdated_output" != "{}" ]]; then
echo "$outdated_output" > "$UPDATE_CACHE.npm"
# Parse and display outdated packages
for package in "${packages_to_check[@]}"; do
if echo "$outdated_output" | grep -q "\"$package\""; then
local current_version=$(echo "$outdated_output" | jq -r ".\"$package\".current // \"unknown\"" 2>/dev/null || echo "unknown")
local wanted_version=$(echo "$outdated_output" | jq -r ".\"$package\".wanted // \"unknown\"" 2>/dev/null || echo "unknown")
local latest_version=$(echo "$outdated_output" | jq -r ".\"$package\".latest // \"unknown\"" 2>/dev/null || echo "unknown")
echo " 📦 $package: $current_version$latest_version"
updates_available=true
update_log "Update available for $package: $current_version$latest_version"
fi
done
else
echo " ✅ All npm packages are up to date"
update_log "All npm packages are up to date"
fi
else
echo " ⚠️ npm not available"
update_log "npm not available for update check"
fi
if [[ "$updates_available" == "true" ]]; then
return 0
else
return 1
fi
}
# Function to check GitHub packages for updates
check_github_updates() {
local updates_available=false
update_log "Checking GitHub packages for updates"
# Get profile-aware GitHub packages
local github_packages
if [[ -f "$HOME/.dotfiles/lib/profile_manager.sh" ]]; then
source "$HOME/.dotfiles/lib/profile_manager.sh"
github_packages=$(get_profile_packages "github")
else
github_packages="fzf bat ripgrep fd delta"
fi
if [[ -z "$github_packages" ]]; then
echo "No GitHub packages to check"
return 1
fi
echo "🐙 Checking GitHub packages for updates..."
for package in $github_packages; do
local repo=""
local current_version=""
local cmd_name="$package"
case "$package" in
"fzf")
repo="junegunn/fzf"
current_version=$(fzf --version 2>/dev/null | cut -d' ' -f1 || echo "unknown")
;;
"bat")
repo="sharkdp/bat"
current_version=$(bat --version 2>/dev/null | grep -o 'bat [0-9.]*' | cut -d' ' -f2 || echo "unknown")
;;
"ripgrep")
repo="BurntSushi/ripgrep"
cmd_name="rg"
current_version=$(rg --version 2>/dev/null | head -1 | grep -o '[0-9.]*' || echo "unknown")
;;
"fd")
repo="sharkdp/fd"
current_version=$(fd --version 2>/dev/null | grep -o '[0-9.]*' || echo "unknown")
;;
"delta")
repo="dandavison/delta"
current_version=$(delta --version 2>/dev/null | grep -o '[0-9.]*' || echo "unknown")
;;
esac
if [[ -n "$repo" ]] && command -v "$cmd_name" >/dev/null 2>&1; then
# Get latest release from GitHub API
local latest_version=$(curl -s "https://api.github.com/repos/$repo/releases/latest" | grep -o '"tag_name": "v*[^"]*"' | cut -d'"' -f4 | sed 's/^v//')
if [[ -n "$latest_version" && "$latest_version" != "$current_version" ]]; then
echo " 📦 $package: $current_version$latest_version"
updates_available=true
update_log "Update available for $package: $current_version$latest_version"
echo "$package|$current_version|$latest_version|$repo" >> "$UPDATE_CACHE.github"
else
echo "$package: $current_version (latest)"
fi
elif [[ -n "$repo" ]]; then
echo " ⚠️ $package not installed"
fi
done
if [[ "$updates_available" == "true" ]]; then
return 0
else
return 1
fi
}
# Function to perform actual updates
update_npm_packages() {
echo "🔄 Updating npm packages..."
if [[ -f "$UPDATE_CACHE.npm" ]]; then
local outdated_output=$(cat "$UPDATE_CACHE.npm")
# Get list of packages that need updating from our profile
local packages_to_update=()
if [[ -f "$HOME/.dotfiles/lib/profile_manager.sh" ]]; then
source "$HOME/.dotfiles/lib/profile_manager.sh"
local binary_packages=$(get_profile_packages "binary")
local npm_packages=$(get_profile_packages "npm")
for package in $binary_packages; do
case "$package" in
"claude-code")
if echo "$outdated_output" | grep -q "@anthropic-ai/claude-code"; then
packages_to_update+=("@anthropic-ai/claude-code")
fi
;;
"gemini-cli")
if echo "$outdated_output" | grep -q "@google/gemini-cli"; then
packages_to_update+=("@google/gemini-cli")
fi
;;
"task-master")
if echo "$outdated_output" | grep -q "task-master-ai"; then
packages_to_update+=("task-master-ai")
fi
;;
esac
done
for package in $npm_packages; do
if echo "$outdated_output" | grep -q "\"$package\""; then
packages_to_update+=("$package")
fi
done
fi
if [[ ${#packages_to_update[@]} -gt 0 ]]; then
echo " Updating: ${packages_to_update[*]}"
npm update -g "${packages_to_update[@]}"
update_log "Updated npm packages: ${packages_to_update[*]}"
else
echo " No packages to update"
fi
# Clean up cache
rm -f "$UPDATE_CACHE.npm"
else
echo " No update information available"
fi
}
# Function to update GitHub packages
update_github_packages() {
echo "🐙 Updating GitHub packages..."
if [[ -f "$UPDATE_CACHE.github" ]]; then
while IFS='|' read -r package current_version latest_version repo; do
echo " Updating $package: $current_version$latest_version"
# Source package manager for installation functions
if [[ -f "$HOME/.dotfiles/lib/package_manager.sh" ]]; then
source "$HOME/.dotfiles/lib/package_manager.sh"
case "$package" in
"fzf")
# Reinstall fzf
if install_github_package "fzf" "$repo" "clone_and_install" "$HOME/.fzf" "" "" "$HOME/.fzf/install --all"; then
echo " ✅ Updated $package"
update_log "Updated $package to $latest_version"
else
echo " ❌ Failed to update $package"
fi
;;
"bat"|"ripgrep"|"fd"|"delta")
local cmd_name="$package"
[[ "$package" == "ripgrep" ]] && cmd_name="rg"
local pattern="x86_64-unknown-linux-gnu"
if [[ "$OSTYPE" == "darwin"* ]]; then
pattern="x86_64-apple-darwin"
fi
if install_github_package "$package" "$repo" "release_binary" "$HOME/.local/bin" "$cmd_name" "$pattern"; then
echo " ✅ Updated $package"
update_log "Updated $package to $latest_version"
else
echo " ❌ Failed to update $package"
fi
;;
esac
fi
done < "$UPDATE_CACHE.github"
# Clean up cache
rm -f "$UPDATE_CACHE.github"
else
echo " No update information available"
fi
}
# Function to show update status
show_update_status() {
echo "🔍 Package Update Status"
echo "======================="
if [[ -f "$LAST_CHECK_FILE" ]]; then
local last_check=$(cat "$LAST_CHECK_FILE")
local last_check_date=$(date -d "@$last_check" 2>/dev/null || echo "Unknown")
echo "Last checked: $last_check_date"
else
echo "Last checked: Never"
fi
echo ""
# Check for cached update information
local updates_pending=false
if [[ -f "$UPDATE_CACHE.npm" ]]; then
echo "📦 npm updates pending:"
local outdated_output=$(cat "$UPDATE_CACHE.npm")
echo "$outdated_output" | jq -r 'to_entries[] | " • \(.key): \(.value.current) → \(.value.latest)"' 2>/dev/null || echo " • Updates available (run dotupdate to see details)"
updates_pending=true
fi
if [[ -f "$UPDATE_CACHE.github" ]]; then
echo "🐙 GitHub updates pending:"
while IFS='|' read -r package current_version latest_version repo; do
echo "$package: $current_version$latest_version"
done < "$UPDATE_CACHE.github"
updates_pending=true
fi
if [[ "$updates_pending" == "false" ]]; then
echo "✅ No pending updates"
echo ""
echo "💡 Run 'dotupdatecheck' to check for new updates"
else
echo ""
echo "💡 Run 'dotupdate' to install pending updates"
fi
}
# Main update check function
check_for_updates() {
local force_check=${1:-false}
local show_output=${2:-true}
if ! should_check_updates "$force_check"; then
if [[ "$show_output" == "true" ]]; then
echo " Skipping update check (checked recently)"
echo " Use 'dotupdatecheck --force' to force check"
fi
return 0
fi
update_log "Starting update check (force: $force_check)"
# Clean old cache files
rm -f "$UPDATE_CACHE.npm" "$UPDATE_CACHE.github"
local npm_updates=false
local github_updates=false
if check_npm_updates; then
npm_updates=true
fi
echo ""
if check_github_updates; then
github_updates=true
fi
# Update last check time
echo "$(date +%s)" > "$LAST_CHECK_FILE"
if [[ "$npm_updates" == "true" || "$github_updates" == "true" ]]; then
echo ""
echo "💡 Updates available! Run 'dotupdate' to install them."
update_log "Updates found and cached"
return 0
else
echo ""
echo "✅ All packages are up to date!"
update_log "No updates available"
return 1
fi
}
# Main update function
update_packages() {
echo "🔄 Updating packages..."
update_log "Starting package updates"
local updated_something=false
if [[ -f "$UPDATE_CACHE.npm" ]]; then
update_npm_packages
updated_something=true
echo ""
fi
if [[ -f "$UPDATE_CACHE.github" ]]; then
update_github_packages
updated_something=true
echo ""
fi
if [[ "$updated_something" == "false" ]]; then
echo "No cached updates found. Run 'dotupdatecheck' first."
return 1
fi
echo "🎉 Package updates completed!"
update_log "Package updates completed"
}