Skip to content

Instantly share code, notes, and snippets.

@Cloud0310
Created December 7, 2025 11:30
Show Gist options
  • Select an option

  • Save Cloud0310/4e1ee15ff24539db0102473cefa291d3 to your computer and use it in GitHub Desktop.

Select an option

Save Cloud0310/4e1ee15ff24539db0102473cefa291d3 to your computer and use it in GitHub Desktop.
#!/bin/bash
set -euo pipefail
# --- Configuration ---
GITHUB_REPO="MetaCubex/mihomo"
CONFIG_REMOTE_URL=""
CONFIG_LOCAL_FILE="/etc/mihomo/config.yaml"
MIHOMO_API_URL="http://localhost:9090"
MIHOMO_API_TOKEN=""
# --- Dependency Check ---
if [[ $EUID -ne 0 ]]; then
echo "Error: This script must be run as root."
exit 1
fi
for cmd in jq curl awk; do
if ! command -v $cmd &> /dev/null; then
echo "Error: '$cmd' is not installed. Please install it to proceed." >&2
exit 1
fi
done
# --- PART 1: Binary Update Logic ---
update_binary() {
echo "--- [Binary Update] Checking for latest release ---"
# 1. Get Local Version
# Output format: Mihomo Meta v1.19.17 linux amd64 ...
# We extract the 3rd field: v1.19.17
if command -v mihomo &> /dev/null; then
LOCAL_VER=$(mihomo -v | grep "Mihomo Meta" | awk '{print $3}')
echo "Current Local Version: $LOCAL_VER"
else
LOCAL_VER="none"
echo "Current Local Version: None (Not installed)"
fi
# 2. Determine Architecture
ARCH_NAME=$(uname --machine)
FILE_PREFIX=""
if [ "$ARCH_NAME" == "x86_64" ]; then
FILE_PREFIX="mihomo-linux-amd64-v3"
elif [ "$ARCH_NAME" == "aarch64" ]; then
FILE_PREFIX="mihomo-linux-arm64"
else
echo "Error: Unsupported architecture: $ARCH_NAME"
exit 1
fi
# 3. Determine OS Family & Extension
if [ -f /etc/os-release ]; then
. /etc/os-release
else
echo "Error: /etc/os-release not found."
exit 1
fi
FILE_SUFFIX=""
DISTRO_TYPE=""
if [[ "$ID" == "arch" || "$ID_LIKE" == *"arch"* ]]; then
FILE_SUFFIX="pkg.tar.zst"
DISTRO_TYPE="arch"
elif [[ "$ID" == "debian" || "$ID_LIKE" == *"debian"* ]]; then
FILE_SUFFIX=".deb"
DISTRO_TYPE="debian"
else
echo "Error: Could not determine strictly Arch or Debian lineage."
exit 1
fi
# 4. Fetch GitHub API
echo "Querying GitHub API..."
API_URL="https://api.github.com/repos/$GITHUB_REPO/releases/latest"
RESPONSE=$(curl -s -L \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"$API_URL")
# 5. Parse Remote Version and Download URL
# We extract both the tag_name (version) and the specific asset URL
PARSED_DATA=$(echo "$RESPONSE" | jq -r --arg prefix "$FILE_PREFIX" --arg suffix "$FILE_SUFFIX" \
'{
tag: .tag_name,
url: (.assets | map(select(.name | startswith($prefix) and endswith($suffix))) | .[0].browser_download_url)
}')
REMOTE_VER=$(echo "$PARSED_DATA" | jq -r '.tag')
DOWNLOAD_URL=$(echo "$PARSED_DATA" | jq -r '.url')
if [ "$REMOTE_VER" == "null" ] || [ -z "$REMOTE_VER" ]; then
echo "Error: Could not determine remote version."
exit 1
fi
echo "Latest Remote Version: $REMOTE_VER"
# 6. Compare and Update
if [ "$LOCAL_VER" == "$REMOTE_VER" ]; then
echo "✅ System is already running the latest version. Skipping binary update."
return 0
fi
if [ "$DOWNLOAD_URL" == "null" ] || [ -z "$DOWNLOAD_URL" ]; then
echo "Error: No matching asset found for architecture/OS."
exit 1
fi
echo "⚡ Update available ($LOCAL_VER -> $REMOTE_VER). Downloading..."
# 7. Install logic
if [ "$DISTRO_TYPE" == "arch" ]; then
echo "Detected Arch Linux. Installing via pacman..."
TEMP_PKG=$(mktemp --suffix=.pkg.tar.zst)
curl -L -o "$TEMP_PKG" "$DOWNLOAD_URL"
pacman -U --noconfirm "$TEMP_PKG"
rm -f "$TEMP_PKG"
elif [ "$DISTRO_TYPE" == "debian" ]; then
echo "Detected Debian/Ubuntu. Downloading and installing via dpkg..."
TEMP_DEB=$(mktemp --suffix=.deb)
curl -L -o "$TEMP_DEB" "$DOWNLOAD_URL"
dpkg -i "$TEMP_DEB"
rm -f "$TEMP_DEB"
fi
echo "Binary update complete."
# Force a service restart later because binary changed
BINARY_UPDATED=1
}
# --- PART 2: Configuration Update Logic ---
trigger_internal_upgrades() {
echo "Triggering Mihomo UI and Geo data upgrades..."
# We check if service is up before curling
if systemctl is-active --quiet mihomo.service; then
curl -sfL -X POST -H "Authorization: Bearer $MIHOMO_API_TOKEN" "${MIHOMO_API_URL}/upgrade/ui" || echo "Warning: Failed to upgrade UI"
curl -sfL -X POST -H "Authorization: Bearer $MIHOMO_API_TOKEN" "${MIHOMO_API_URL}/upgrade/geo" || echo "Warning: Failed to upgrade GeoIP"
echo "Internal upgrades triggered."
else
echo "Service is not running, skipping API triggers."
fi
}
update_config() {
echo "--- [Config Update] Checking remote config ---"
mkdir -p "$(dirname "$CONFIG_LOCAL_FILE")"
SHOULD_RESTART=0
# If binary was just updated, we MUST restart regardless of config change
if [ "$BINARY_UPDATED" == "1" ]; then
SHOULD_RESTART=1
fi
# Case 1: Initial Download
if [ ! -f "$CONFIG_LOCAL_FILE" ]; then
echo "Local file not found. Performing initial download..."
curl --noproxy "*" -fLo "$CONFIG_LOCAL_FILE" "$CONFIG_REMOTE_URL"
SHOULD_RESTART=1
else
# Case 2: Conditional Update
LAST_MOD_HEADER=$(date -Rur "$CONFIG_LOCAL_FILE" | sed 's/+0000/GMT/')
TEMP_FILE=$(mktemp)
trap 'rm -f "$TEMP_FILE"' RETURN
HTTP_STATUS=$(curl --noproxy "*" -sL -w '%{http_code}' -o "$TEMP_FILE" -H "If-Modified-Since: $LAST_MOD_HEADER" "$CONFIG_REMOTE_URL")
if [ "$HTTP_STATUS" == "200" ]; then
echo "Remote config is newer. Updating..."
if [ -s "$TEMP_FILE" ]; then
mv "$CONFIG_LOCAL_FILE" "${CONFIG_LOCAL_FILE}.bak"
mv "$TEMP_FILE" "$CONFIG_LOCAL_FILE"
SHOULD_RESTART=1
else
echo "Error: Downloaded empty file."
fi
elif [ "$HTTP_STATUS" == "304" ]; then
echo "Config is up to date."
else
echo "Error fetching config. HTTP Status: $HTTP_STATUS"
fi
fi
# Restart Service if needed
if [ "$SHOULD_RESTART" -eq 1 ]; then
echo "Restarting mihomo service..."
systemctl restart mihomo.service
sleep 5
trigger_internal_upgrades
else
echo "No changes required."
fi
}
# --- Main Execution ---
BINARY_UPDATED=0
# 1. Update Binary
update_binary
# 2. Update Config
update_config
echo "All tasks finished."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment