- 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>
409 lines
14 KiB
Bash
Executable File
409 lines
14 KiB
Bash
Executable File
#!/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"
|
||
} |