Last active
December 29, 2023 01:17
-
-
Save tgran2028/7f00f2f5b96ace099916d547040b22e1 to your computer and use it in GitHub Desktop.
Update all installed crates at $CARGO_HOME to latest version
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# --- | |
# @name update-cargo-binaries.sh | |
# --- | |
# @description This script updates installed Cargo crates to their latest versions. | |
# It checks for the presence of required commands, verifies the CARGO_HOME environment variable, | |
# and updates each crate in parallel up to a specified limit. | |
# | |
# +----------------+----------------+----------------+----------------+----------------+----------------+ | |
set -euo pipefail | |
# Check for required commands: curl, jq, and cargo | |
for cmd in curl jq cargo; do | |
if ! command -v "$cmd" &>/dev/null; then | |
echo "Error: $cmd is not installed." >&2 | |
exit 1 | |
fi | |
done | |
# Check if CARGO_HOME environment variable is set | |
if [ -z "$CARGO_HOME" ]; then | |
echo "Error: CARGO_HOME is not set." | |
exit 1 | |
fi | |
# Function to check if a process with a given PID is running | |
# Args: | |
# pid: The process ID to check | |
check_pid() { | |
local pid="$1" | |
if ps -p "$pid" > /dev/null 2>&1; then | |
return 0 | |
else | |
return 1 | |
fi | |
} | |
# Function to get the latest version of a crate from crates.io | |
# Args: | |
# crate_name: Name of the crate | |
latest_crate_version() { | |
local crate_name="$1" | |
curl --silent "https://crates.io/api/v1/crates/$crate_name" | jq -e -rM '.versions[0].num' || { | |
echo "$(date): Failed to get the latest version of the crate $crate_name" | |
exit 1 | |
} | |
} | |
# Function to get the installed version of a crate | |
# Args: | |
# crate_name: Name of the crate | |
installed_crate_version() { | |
local crate_name="$1" | |
cargo install --list \ | |
| grep "^$crate_name\s" \ | |
| awk '{print $2}' \ | |
| sed -e 's/^ *//' -e 's/ *$//' \ | |
| sed 's/:$//' \ | |
| sed 's/^v//' | |
} | |
# Function to create a JSON object with crate version information | |
# Args: | |
# crate_name: Name of the crate | |
crate_versions_json() { | |
local crate_name="$1" | |
jq --null-input \ | |
--arg name "$crate_name" \ | |
--arg latest "$(latest_crate_version "$crate_name")" \ | |
--arg installed "$(installed_crate_version "$crate_name")" \ | |
--argjson requires_update "$([[ "$latest_version" != "$installed_version" ]] && echo true || echo false)" \ | |
'{name: $name, latest: $latest, installed: $installed, requires_update: $requires_update}' | |
} | |
# Function to check if the latest version of a crate is installed | |
# Args: | |
# crate_name: Name of the crate | |
is_latest_version_installed() { | |
local crate_name="$1" | |
local json_data | |
if [ -z "$json_data" ]; then | |
requires_update=$(crate_versions_json "$crate_name" | jq -rM '.requires_update') | |
else | |
requires_update=$(echo "$json_data" | jq -rM '.requires_update') | |
fi | |
echo "$requires_update" | |
} | |
# Function to get all installed crates | |
get_all_installed_crates() { | |
cargo install --list \ | |
| grep -v "^\s" \ | |
| awk '{print $1}' \ | |
| sed -e 's/^ *//' -e 's/ *$//' \ | |
| sed 's/:$//' | |
} | |
# Main function to update cargo binaries | |
update_cargo_binaries() { | |
local log_dir=~/.local/var/log/cargo/install | |
local pid versions_json installed_version latest_version crate | |
# Maximum number of concurrent updates | |
declare -i max_concurrent_updates=5 | |
declare -i job_count=0 | |
mkdir -p "$log_dir" | |
# Loop through all installed crates | |
for crate in $(get_all_installed_crates); do | |
# Check if the maximum number of concurrent updates has been reached | |
while [ "$job_count" -ge "$max_concurrent_updates" ]; do | |
sleep 5 | |
job_count=$(jobs -p | wc -l) || { | |
echo "$(date): Failed to get the number of background jobs" | |
exit 1 | |
} | |
done | |
versions_json="$(crate_versions_json "$crate")" | |
latest_version=$(echo "$versions_json" | jq -rM '.latest') | |
installed_version=$(echo "$versions_json" | jq -rM '.installed') | |
# Check if an update is required | |
if [ "$latest_version" != "$installed_version" ]; then | |
printf "%s: %s -> %s\n" "$crate" "$installed_version" "$latest_version" | |
# Update the crate in the background | |
cargo install \ | |
--force \ | |
--locked \ | |
--all-features \ | |
--jobs 2 \ | |
--message-format json-diagnostic-short \ | |
"$crate" \ | |
>"$log_dir/$crate.json" 2>&1 & | |
((job_count++)) | |
else | |
printf "%s: %s is the latest version\n" "$crate" "$installed_version" | |
fi | |
done | |
# Wait for all background jobs to finish | |
for pid in $(jobs -pr); do | |
wait "$pid" | |
done | |
} | |
# Run the update function if the script is not sourced | |
if [ "$0" = "${BASH_SOURCE[0]}" ]; then | |
update_cargo_binaries | |
else | |
# Export functions if the script is sourced | |
export -f check_pid | |
export -f latest_crate_version | |
export -f installed_crate_version | |
export -f is_latest_version_installed | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment