shellcheck / shellcheck (push) Failing after 17s
Each run's output now goes to ~/.local/state/coredesktop/logs/, so what failed can be read after the terminal is gone. In a terminal, update runs itself under script(1), so everything it runs still sees a terminal, then strips color codes and progress redraws from the log; otherwise it uses tee. install.sh, usually piped into bash, uses tee, and runs configure straight on the terminal, outside the log. Both name the log when something fails. 95-cleanup.sh keeps the newest 10 update logs and 3 install logs. The sudo keepalive loops no longer share the logged output, so their sleep can't hold the log open after the run ends. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
154 lines
7.0 KiB
Bash
Executable File
154 lines
7.0 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Updates from before 2026-10-19 (013acac's) copy this file over themselves
|
|
# while bash is still reading them, then read on from byte 1484 of the new copy.
|
|
# The exec line below starts at exactly that byte, so they run this update
|
|
# afresh; the rest of the block pads it into place, and in a normal run the
|
|
# block is a here-document nothing reads. Keep the exec line at byte 1484:
|
|
# tests/update.bats checks it.
|
|
: << 'FROM_OLD_UPDATE'
|
|
This block is padding for old updates. This block is padding for old updates.
|
|
This block is padding for old updates. This block is padding for old updates.
|
|
This block is padding for old updates. This block is padding for old updates.
|
|
This block is padding for old updates. This block is padding for old updates.
|
|
This block is padding for old updates. This block is padding for old updates.
|
|
This block is padding for old updates. This block is padding for old updates.
|
|
This block is padding for old updates. This block is padding for old updates.
|
|
This block is padding for old updates. This block is padding for old updates.
|
|
This block is padding for old updates. This block is padding for old updates.
|
|
This block is padding for old updates. This block is padding for old updates.
|
|
This block is padding for old updates. This block is padding for old updates.
|
|
This block is padding for old updates. This block is padding for old updates.
|
|
This block is padding for old updates. This block is padding for old updates.
|
|
...................
|
|
exec ~/.local/bin/update
|
|
FROM_OLD_UPDATE
|
|
|
|
# The whole script is wrapped in { ...; exit; } so bash parses all of it before
|
|
# running anything. That way, replacing this file mid-run (e.g. by git pull and
|
|
# the copy below) can't make the running copy read a mix of old and new text.
|
|
{
|
|
|
|
# Install a script atomically (copy to a temp file, then rename over the target)
|
|
install_script() {
|
|
local tmp
|
|
tmp="$(mktemp "$2.XXXXXX")" && cp "$1" "$tmp" && chmod 755 "$tmp" && mv -f "$tmp" "$2"
|
|
}
|
|
|
|
# Make a log written by script(1) readable: drop its header and footer, color
|
|
# and cursor codes, and all but the last redraw of each progress line. LC_ALL=C
|
|
# so the ranges below mean bytes, whatever the locale.
|
|
clean_log() {
|
|
LC_ALL=C sed -i -E '1{/^Script started on /d}; /^Script done on .*COMMAND_EXIT_CODE/d;
|
|
s/\x1b\][^\x07\x1b]*(\x07|\x1b\\)//g; s#\x1b\[[0-9;?]*[ -/]*[@-~]##g;
|
|
s/\x1b[()][0-9A-Za-z]//g; s/\x1b[=>]//g; s/\r+$//; s/.*\r//' "$1"
|
|
}
|
|
|
|
# Log each update to ~/.local/state/coredesktop/logs (95-cleanup.sh deletes
|
|
# old ones). In a terminal, run under script(1), so everything still sees a
|
|
# terminal and colors, menus and prompts work as before; otherwise use tee.
|
|
# UPDATE_LOG tells the copy re-run below that it's already logging.
|
|
if [[ -z "$UPDATE_LOG" ]] && mkdir -p -m 700 "$HOME/.local/state/coredesktop/logs"; then
|
|
UPDATE_LOG="$HOME/.local/state/coredesktop/logs/update-$(date +%Y-%m-%d-%H%M%S).log"
|
|
export UPDATE_LOG
|
|
if [ -t 0 ] && [ -t 1 ] && command -v script > /dev/null; then
|
|
SHELL=/bin/bash script -q -e -f -c "$(printf '%q ' "$0" "$@")" "$UPDATE_LOG"
|
|
status=$?
|
|
clean_log "$UPDATE_LOG"
|
|
exit "$status"
|
|
fi
|
|
exec > >(tee -a "$UPDATE_LOG") 2>&1
|
|
fi
|
|
|
|
# Print the logo
|
|
print_logo() {
|
|
cat << "EOF"
|
|
|
|
|
|
██
|
|
█ ██
|
|
██ ██████ █████ ██ █████ █████ ████ ██ █████ ██
|
|
██ ██ ██ ███ ███████ ██ ██ ██ ██ ██ ████████ ██ ██ ███████
|
|
█████ ██████ ██ ██ ██████ ██ ████████ ██ █ ███████ ██ ██
|
|
██ ██████ ███████ ██████ ██ ██████ ██████ ██████ ███████
|
|
|
|
|
|
EOF
|
|
}
|
|
|
|
# Clear screen and show logo
|
|
clear
|
|
print_logo
|
|
|
|
COREDESKTOP="$HOME/.local/share/coredesktop"
|
|
|
|
echo "Updating CoreDesktop..."
|
|
if [[ -z "$UPDATE_REEXECED" ]] && ! git -C "$COREDESKTOP" pull --ff-only; then
|
|
echo "WARNING: could not pull the latest CoreDesktop; running the update hooks already on disk."
|
|
export UPDATE_PULL_FAILED=1
|
|
fi
|
|
mkdir -p "$COREDESKTOP/updates.d"
|
|
install_script "$COREDESKTOP/update" ~/.local/bin/update
|
|
install_script "$COREDESKTOP/refresh" ~/.local/bin/refresh
|
|
install_script "$COREDESKTOP/sibling" ~/.local/bin/sibling
|
|
install_script "$COREDESKTOP/version" ~/.local/bin/version
|
|
install_script "$COREDESKTOP/configure" ~/.local/bin/configure
|
|
install_script "$COREDESKTOP/audit" ~/.local/bin/audit
|
|
|
|
if [[ -z "$UPDATE_REEXECED" ]]; then
|
|
export UPDATE_REEXECED=1
|
|
exec ~/.local/bin/update
|
|
fi
|
|
|
|
# An upgrade that runs out of disk space part way through can leave the system
|
|
# half-upgraded. Set UPDATE_FORCE=1 to skip this check.
|
|
MIN_FREE_GIB=10
|
|
FREE_KIB="$(df -k --output=avail / | tail -n 1 | tr -d ' ')"
|
|
if [[ -z "$UPDATE_FORCE" && "$FREE_KIB" =~ ^[0-9]+$ ]] && (( FREE_KIB < MIN_FREE_GIB * 1024 * 1024 )); then
|
|
echo "Only $(( FREE_KIB / 1024 / 1024 )) GiB free on /; updating needs at least $MIN_FREE_GIB GiB."
|
|
echo "Free up space (sudo paccache -rk1 clears old cached packages), or run: UPDATE_FORCE=1 update"
|
|
exit 1
|
|
fi
|
|
|
|
# Ask for the password once, then keep sudo's cached credentials fresh until
|
|
# update exits, so long-running hooks don't stop for a password again.
|
|
sudo -v || exit 1
|
|
while kill -0 $$ 2>/dev/null; do sudo -n -v; sleep 60; done > /dev/null 2>&1 &
|
|
SUDO_KEEPALIVE_PID=$!
|
|
trap 'kill "$SUDO_KEEPALIVE_PID" 2>/dev/null' EXIT
|
|
|
|
FAILED=()
|
|
[[ -n "$UPDATE_PULL_FAILED" ]] && FAILED+=("git pull")
|
|
|
|
# Each hook runs in its own subshell so a cd, set -e, exit, or stray variable
|
|
# in one hook can't leak into or abort the hooks after it.
|
|
for plugin in "$COREDESKTOP/updates.d/"*.sh; do
|
|
[ -f "$plugin" ] || continue
|
|
(
|
|
unset -f is_installed run_update
|
|
source "$plugin"
|
|
# A hook without run_update does its work as it's sourced, so its
|
|
# last command's status is the hook's
|
|
status=$?
|
|
if declare -f run_update > /dev/null; then
|
|
if ! declare -f is_installed > /dev/null || is_installed; then
|
|
run_update
|
|
fi
|
|
else
|
|
exit "$status"
|
|
fi
|
|
)
|
|
# Checked separately: under `( ... ) ||`, bash would ignore set -e inside the hook
|
|
[ $? -eq 0 ] || FAILED+=("$(basename "$plugin")")
|
|
done
|
|
|
|
if (( ${#FAILED[@]} > 0 )); then
|
|
echo "Done, with errors in: ${FAILED[*]}"
|
|
[ -n "$UPDATE_LOG" ] && echo "The full output is in $UPDATE_LOG"
|
|
exit 1
|
|
fi
|
|
|
|
echo "Done."
|
|
exit 0
|
|
}
|