migrated from github
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
# scripts
|
||||||
|
|
||||||
|
zsh <(curl -fsSL https://raw.githubusercontent.com/hillbillyer/scripts/main/mac-update.sh)
|
||||||
|
|||||||
67
health-check/health-check.sh
Normal file
67
health-check/health-check.sh
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# ==============================
|
||||||
|
# Configuration
|
||||||
|
# ==============================
|
||||||
|
|
||||||
|
NTFY_URL="https://ntfy.hillbillyer.dev/health-alert"
|
||||||
|
DISK_THRESHOLD=95
|
||||||
|
RAM_THRESHOLD=90
|
||||||
|
STATE_FILE="$HOME/hillbillyer/health-check/proxmox_health_state"
|
||||||
|
|
||||||
|
HOSTNAME=$(hostname)
|
||||||
|
|
||||||
|
# ==============================
|
||||||
|
# Disk Check (Ignore tmpfs, etc.)
|
||||||
|
# ==============================
|
||||||
|
|
||||||
|
disk_alert=0
|
||||||
|
disk_message=""
|
||||||
|
|
||||||
|
while read -r source fstype size used avail pcent mount; do
|
||||||
|
usage_percent=$(echo "$pcent" | sed 's/%//')
|
||||||
|
|
||||||
|
# Skip unwanted filesystem types
|
||||||
|
if [[ "$fstype" =~ ^(tmpfs|devtmpfs|overlay|squashfs)$ ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$usage_percent" -ge "$DISK_THRESHOLD" ]; then
|
||||||
|
disk_alert=1
|
||||||
|
disk_message+="Disk alert on $HOSTNAME: $mount ($fstype) is ${pcent} full\n"
|
||||||
|
fi
|
||||||
|
done < <(df -hT | tail -n +2)
|
||||||
|
|
||||||
|
# ==============================
|
||||||
|
# RAM Check
|
||||||
|
# ==============================
|
||||||
|
|
||||||
|
ram_used_percent=$(free | awk '/Mem:/ {printf("%.0f"), $3/$2 * 100}')
|
||||||
|
|
||||||
|
ram_alert=0
|
||||||
|
ram_message=""
|
||||||
|
|
||||||
|
if [ "$ram_used_percent" -ge "$RAM_THRESHOLD" ]; then
|
||||||
|
ram_alert=1
|
||||||
|
ram_message="RAM alert on $HOSTNAME: Memory usage is ${ram_used_percent}%\n"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ==============================
|
||||||
|
# State Handling (Prevent Spam)
|
||||||
|
# ==============================
|
||||||
|
|
||||||
|
previous_state=""
|
||||||
|
[ -f "$STATE_FILE" ] && previous_state=$(cat "$STATE_FILE")
|
||||||
|
|
||||||
|
current_state="disk:$disk_alert ram:$ram_alert"
|
||||||
|
|
||||||
|
if [ "$current_state" != "$previous_state" ]; then
|
||||||
|
if [ "$disk_alert" -eq 1 ] || [ "$ram_alert" -eq 1 ]; then
|
||||||
|
message="${disk_message}${ram_message}"
|
||||||
|
echo -e "$message" | curl -s \
|
||||||
|
-H "Title: Usage Alert on $HOSTNAME" \
|
||||||
|
-H "Priority: urgent" \
|
||||||
|
-d @- "$NTFY_URL"
|
||||||
|
fi
|
||||||
|
echo "$current_state" > "$STATE_FILE"
|
||||||
|
fi
|
||||||
174
mac-update.sh
Executable file
174
mac-update.sh
Executable file
@@ -0,0 +1,174 @@
|
|||||||
|
#!/bin/zsh
|
||||||
|
# Interactive arrow-key menu for choosing a macOS full installer version.
|
||||||
|
# Works on Apple Silicon & Intel, no extra deps.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
export LC_ALL=C
|
||||||
|
|
||||||
|
# --- normalize versions into X.Y.Z ---
|
||||||
|
normalize_ver() {
|
||||||
|
local v="${1//[^0-9.]/}" # strip non-numeric/dots
|
||||||
|
local -a p
|
||||||
|
p=(${(s/./)v})
|
||||||
|
printf "%d.%d.%d" "${p[1]:-0}" "${p[2]:-0}" "${p[3]:-0}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- compare versions: returns 1 if v1>v2, 0 if equal, -1 if v1<v2 ---
|
||||||
|
version_cmp() {
|
||||||
|
local v1="$(normalize_ver "$1")"
|
||||||
|
local v2="$(normalize_ver "$2")"
|
||||||
|
local -a A B
|
||||||
|
A=(${(s/./)v1}); B=(${(s/./)v2})
|
||||||
|
for i in 1 2 3; do
|
||||||
|
(( ${A[i]} > ${B[i]} )) && { echo 1; return; }
|
||||||
|
(( ${A[i]} < ${B[i]} )) && { echo -1; return; }
|
||||||
|
done
|
||||||
|
echo 0
|
||||||
|
}
|
||||||
|
version_ge() { [[ "$(version_cmp "$1" "$2")" != "-1" ]]; }
|
||||||
|
version_gt() { [[ "$(version_cmp "$1" "$2")" == "1" ]]; }
|
||||||
|
|
||||||
|
# --- current version ---
|
||||||
|
current_version_raw=$(sw_vers -productVersion)
|
||||||
|
current_version=$(normalize_ver "$current_version_raw")
|
||||||
|
|
||||||
|
autoload -Uz colors && colors
|
||||||
|
|
||||||
|
hide_cursor() { printf '\e[?25l'; }
|
||||||
|
show_cursor() { printf '\e[?25h'; }
|
||||||
|
clear_screen() { printf '\e[2J\e[H'; }
|
||||||
|
cleanup() { stty "$_STTY_ORIG" 2>/dev/null || true; show_cursor; printf '\e[0m'; }
|
||||||
|
trap cleanup EXIT INT TERM
|
||||||
|
|
||||||
|
if [[ -t 0 && -t 1 ]]; then
|
||||||
|
_STTY_ORIG=$(stty -g)
|
||||||
|
stty -echo -icanon min 1 time 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- fetch list ---
|
||||||
|
raw=$(/usr/sbin/softwareupdate --list-full-installers 2>/dev/null)
|
||||||
|
lines=("${(@f)$(printf "%s\n" "$raw" | grep -E '^\* Title: ')}")
|
||||||
|
(( ${#lines[@]} == 0 )) && { echo "No installers found."; exit 1; }
|
||||||
|
|
||||||
|
parsed=$(
|
||||||
|
printf "%s\n" "${lines[@]}" | /usr/bin/awk '
|
||||||
|
/^\* Title:/ {
|
||||||
|
version=""; title=""; build=""; size="";
|
||||||
|
if (match($0, /^\* Title: [^,]*/)) { title = substr($0, RSTART+9, RLENGTH-9) }
|
||||||
|
if (match($0, /Version: [0-9][0-9.]*/)) { version = substr($0, RSTART+9, RLENGTH-9) }
|
||||||
|
if (match($0, /Build: [^,]*/)) { build = substr($0, RSTART+7, RLENGTH-7) }
|
||||||
|
if (match($0, /Size: [0-9]+KiB/)) { size = substr($0, RSTART+6, RLENGTH-6) }
|
||||||
|
printf("%s\t%s\t%s\t%s\n", version, title, build, size);
|
||||||
|
}'
|
||||||
|
)
|
||||||
|
|
||||||
|
typeset -a versions versions_norm titles builds sizes
|
||||||
|
for row in "${(@f)parsed}"; do
|
||||||
|
IFS=$'\t' read -r v t b s <<< "$row"
|
||||||
|
[[ -n "$v" ]] || continue
|
||||||
|
if ! version_ge "$v" "$current_version"; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
versions+=("$v")
|
||||||
|
versions_norm+=("$(normalize_ver "$v")")
|
||||||
|
titles+=("$t")
|
||||||
|
builds+=("$b")
|
||||||
|
sizes+=("$s")
|
||||||
|
done
|
||||||
|
(( ${#versions[@]} == 0 )) && { echo "No newer/equal versions found."; exit 1; }
|
||||||
|
|
||||||
|
human_gib() {
|
||||||
|
local in="$1" n
|
||||||
|
[[ "$in" == *KiB ]] && n="${in%KiB}" || n="$in"
|
||||||
|
[[ "$n" == <-> ]] || { printf "%s" "$in"; return; }
|
||||||
|
/usr/bin/awk -v kib="$n" 'BEGIN { printf("%.2f GiB", kib/1048576) }'
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- UI state ---
|
||||||
|
pos=1; count=${#versions[@]}
|
||||||
|
: ${LINES:=24}; : ${COLUMNS:=80}
|
||||||
|
min_visible=5; visible=$(( LINES - 6 ))
|
||||||
|
(( visible < min_visible )) && visible=$min_visible
|
||||||
|
(( visible > count )) && visible=$count
|
||||||
|
start=1
|
||||||
|
|
||||||
|
make_divider() {
|
||||||
|
# nice clean ASCII divider
|
||||||
|
printf '%*s' "$COLUMNS" '' | tr ' ' '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
draw() {
|
||||||
|
clear_screen
|
||||||
|
local divider="$(make_divider)"
|
||||||
|
|
||||||
|
print -P "%F{yellow} Current macOS: ${current_version_raw} | Select macOS version | ↑/↓ move | Enter select | q quit %f"
|
||||||
|
print -r -- "$divider"
|
||||||
|
|
||||||
|
(( pos < start )) && start=$pos
|
||||||
|
(( pos >= start + visible )) && start=$(( pos - visible + 1 ))
|
||||||
|
(( start < 1 )) && start=1
|
||||||
|
|
||||||
|
local end=$(( start + visible - 1 ))
|
||||||
|
(( end > count )) && end=$count
|
||||||
|
|
||||||
|
for ((i=start; i<=end; i++)); do
|
||||||
|
if (( i == pos )); then
|
||||||
|
printf "\e[7m➤ %-*s\e[0m\n" $((COLUMNS-4)) "${versions[i]}"
|
||||||
|
else
|
||||||
|
printf " %-*s\n" $((COLUMNS-4)) "${versions[i]}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
print -r -- "$divider"
|
||||||
|
local sg=$(human_gib "${sizes[pos]}")
|
||||||
|
print -P "%F{yellow}Version:%f ${versions[pos]} %F{yellow}Title:%f ${titles[pos]} %F{yellow}Build:%f ${builds[pos]} %F{yellow}Size:%f ${sg}"
|
||||||
|
}
|
||||||
|
|
||||||
|
hide_cursor
|
||||||
|
while true; do
|
||||||
|
draw
|
||||||
|
read -k 1 key || key=""
|
||||||
|
case "$key" in
|
||||||
|
$'\n'|$'\r') break ;;
|
||||||
|
q|Q) cleanup; echo "No selection."; exit 2 ;;
|
||||||
|
k) (( pos > 1 )) && (( pos-- )) ;;
|
||||||
|
j) (( pos < count )) && (( pos++ )) ;;
|
||||||
|
$'\e')
|
||||||
|
read -k 1 -t 0.05 key2 || key2=""
|
||||||
|
if [[ "$key2" == "[" ]]; then
|
||||||
|
read -k 1 -t 0.05 key3 || key3=""
|
||||||
|
case "$key3" in
|
||||||
|
A) (( pos > 1 )) && (( pos-- )) ;;
|
||||||
|
B) (( pos < count )) && (( pos++ )) ;;
|
||||||
|
H) pos=1 ;;
|
||||||
|
F) pos=$count ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
show_cursor
|
||||||
|
selected_version="${versions[pos]}"
|
||||||
|
selected_version_norm="${versions_norm[pos]}"
|
||||||
|
echo "$selected_version"
|
||||||
|
|
||||||
|
# Final sanity check
|
||||||
|
if ! version_ge "$selected_version" "$current_version"; then
|
||||||
|
print -P "%F{red}Selected version ($selected_version) is older than current ($current_version_raw). Aborting.%f"
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Major vs minor
|
||||||
|
selected_major="${${(s/./)selected_version_norm}[1]}"
|
||||||
|
current_major="${${(s/./)current_version}[1]}"
|
||||||
|
|
||||||
|
if [[ "$selected_major" == "$current_major" ]]; then
|
||||||
|
print -P "%F{green}Minor update to $selected_version...%f"
|
||||||
|
sudo softwareupdate --install --all --force --restart
|
||||||
|
else
|
||||||
|
print -P "%F{cyan}Major upgrade to $selected_version...%f"
|
||||||
|
sudo softwareupdate --fetch-full-installer --full-installer-version "$selected_version"
|
||||||
|
current_user=$(stat -f%Su /dev/console)
|
||||||
|
sudo /Applications/Install\ macOS\ *.app/Contents/Resources/startosinstall --agreetolicense --nointeraction --rebootdelay 10 --forcequitapps --user "$current_user" --passprompt
|
||||||
|
fi
|
||||||
133
ubuntu-update.sh
Normal file
133
ubuntu-update.sh
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Setup
|
||||||
|
mkdir -p "$HOME/hillbillyer/health-check"
|
||||||
|
LOGFILE="$HOME/hillbillyer/ubuntu-updates.log"
|
||||||
|
ERRFILE=$(mktemp)
|
||||||
|
UPGRADES_TMP=$(mktemp)
|
||||||
|
NTFY_TOPIC="https://ntfy.hillbillyer.dev/machine-updates"
|
||||||
|
HOSTNAME=$(hostname)
|
||||||
|
touch $HOME/hillbillyer/custom-commands.sh
|
||||||
|
CUSTOM_SCRIPT="$HOME/hillbillyer/custom-commands.sh"
|
||||||
|
UPDATE_PATH="$HOME/update.sh"
|
||||||
|
HEALTH_PATH="$HOME/hillbillyer/health-check/health-check.sh"
|
||||||
|
|
||||||
|
# Logging start
|
||||||
|
{
|
||||||
|
echo "===== $(date '+%Y-%m-%d %H:%M:%S') ====="
|
||||||
|
echo "Running apt update && full-upgrade on $HOSTNAME"
|
||||||
|
} >> "$LOGFILE"
|
||||||
|
|
||||||
|
# Refresh package list
|
||||||
|
apt update >> "$LOGFILE" 2>>"$ERRFILE"
|
||||||
|
|
||||||
|
# ==============================
|
||||||
|
# Update health-check folder from GitHub
|
||||||
|
# ==============================
|
||||||
|
|
||||||
|
apt install unzip -y
|
||||||
|
REPO_ZIP="https://github.com/hillbillyer/scripts/archive/refs/heads/main.zip"
|
||||||
|
TARGET_DIR="$HOME/hillbillyer"
|
||||||
|
FOLDER_NAME="health-check"
|
||||||
|
TMP_DIR=$(mktemp -d)
|
||||||
|
|
||||||
|
# Download the repo ZIP into a temporary folder
|
||||||
|
curl -L -o "$TMP_DIR/repo.zip" "$REPO_ZIP"
|
||||||
|
|
||||||
|
# Extract only the health-check folder from the ZIP
|
||||||
|
unzip -q "$TMP_DIR/repo.zip" "scripts-main/$FOLDER_NAME/*" -d "$TMP_DIR"
|
||||||
|
|
||||||
|
# Remove existing health-check folder on the server
|
||||||
|
rm -rf "$TARGET_DIR/$FOLDER_NAME"
|
||||||
|
|
||||||
|
# Move the new health-check folder into place
|
||||||
|
mv "$TMP_DIR/scripts-main/$FOLDER_NAME" "$TARGET_DIR/"
|
||||||
|
|
||||||
|
# Clean up temp folder
|
||||||
|
rm -rf "$TMP_DIR"
|
||||||
|
|
||||||
|
echo "Updated $TARGET_DIR/$FOLDER_NAME from GitHub."
|
||||||
|
|
||||||
|
# Capture list of upgradable packages BEFORE full-upgrade
|
||||||
|
UPGRADES=$(apt list --upgradable 2>/dev/null | awk -F/ 'NR>1 {print $1}' | paste -sd, -)
|
||||||
|
echo "Will upgrade: ${UPGRADES:-<none>}" >> "$LOGFILE"
|
||||||
|
|
||||||
|
# Run upgrades
|
||||||
|
{
|
||||||
|
apt full-upgrade -y
|
||||||
|
apt autoremove -y
|
||||||
|
apt clean
|
||||||
|
} >> "$LOGFILE" 2>>"$ERRFILE"
|
||||||
|
|
||||||
|
APT_SUCCESS=$?
|
||||||
|
|
||||||
|
# Custom Commands Section
|
||||||
|
CUSTOM_SUCCESS=0
|
||||||
|
CUSTOM_OUTPUT=""
|
||||||
|
|
||||||
|
if [ -f "$CUSTOM_SCRIPT" ]; then
|
||||||
|
echo "Running custom update script: $CUSTOM_SCRIPT" >> "$LOGFILE"
|
||||||
|
CUSTOM_OUTPUT=$(bash "$CUSTOM_SCRIPT" 2>&1)
|
||||||
|
CUSTOM_SUCCESS=$?
|
||||||
|
{
|
||||||
|
echo "Custom script output:"
|
||||||
|
echo "$CUSTOM_OUTPUT"
|
||||||
|
} >> "$LOGFILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Compose NTFY message
|
||||||
|
if [ $APT_SUCCESS -eq 0 ] && [ $CUSTOM_SUCCESS -eq 0 ]; then
|
||||||
|
if [ -n "$UPGRADES" ]; then
|
||||||
|
MESSAGE="✅ $HOSTNAME updated successfully. Updated: $UPGRADES"
|
||||||
|
else
|
||||||
|
MESSAGE="✅ $HOSTNAME updated successfully. No packages were updated."
|
||||||
|
fi
|
||||||
|
if [ -f "$CUSTOM_SCRIPT" ]; then
|
||||||
|
MESSAGE="$MESSAGE (Custom script ran successfully.)"
|
||||||
|
fi
|
||||||
|
elif [ $APT_SUCCESS -ne 0 ]; then
|
||||||
|
ERROR_MSG=$(<"$ERRFILE")
|
||||||
|
MESSAGE="❌ $HOSTNAME apt update/upgrade failed: $ERROR_MSG"
|
||||||
|
elif [ $CUSTOM_SUCCESS -ne 0 ]; then
|
||||||
|
MESSAGE="❌ $HOSTNAME custom update script failed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Send notification
|
||||||
|
curl -s -X POST -H "Title: Server Update" -d "$MESSAGE" "$NTFY_TOPIC" >/dev/null
|
||||||
|
|
||||||
|
# Run Health Check
|
||||||
|
bash $HOME/hillbillyer/health-check/health-check.sh
|
||||||
|
|
||||||
|
# ==============================
|
||||||
|
# Update Cron Jobs (Tagged Only)
|
||||||
|
# ==============================
|
||||||
|
|
||||||
|
UPDATE_JOB="0 3 * * * /usr/bin/bash $UPDATE_PATH # HILLBILLYER_UPDATE"
|
||||||
|
HEALTH_JOB="*/5 * * * * /usr/bin/bash $HEALTH_PATH # HILLBILLYER_HEALTH"
|
||||||
|
|
||||||
|
TMP_FILE=$(mktemp)
|
||||||
|
|
||||||
|
# Remove only our tagged jobs (leave everything else untouched)
|
||||||
|
crontab -l 2>/dev/null | \
|
||||||
|
grep -v "# HILLBILLYER_UPDATE" | \
|
||||||
|
grep -v "# HILLBILLYER_HEALTH" > "$TMP_FILE"
|
||||||
|
|
||||||
|
# Add fresh tagged entries
|
||||||
|
echo "$UPDATE_JOB" >> "$TMP_FILE"
|
||||||
|
echo "$HEALTH_JOB" >> "$TMP_FILE"
|
||||||
|
|
||||||
|
# Install updated crontab
|
||||||
|
crontab "$TMP_FILE"
|
||||||
|
|
||||||
|
rm "$TMP_FILE"
|
||||||
|
|
||||||
|
echo "Cron jobs replaced successfully." >> "$LOGFILE"
|
||||||
|
|
||||||
|
# Append result to log
|
||||||
|
{
|
||||||
|
echo "$MESSAGE"
|
||||||
|
echo ""
|
||||||
|
} >> "$LOGFILE"
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
rm -f "$ERRFILE" "$UPGRADES_TMP"
|
||||||
Reference in New Issue
Block a user