Log each install and update, and keep only the recent logs
shellcheck / shellcheck (push) Failing after 17s
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>
This commit is contained in:
@@ -210,6 +210,8 @@ The script pulls the latest CoreDesktop, then runs each step in ~/.local/share/c
|
||||
|
||||
If a step fails, the other steps still run, and the failed ones are listed at the end.
|
||||
|
||||
Each run's full output is saved in ~/.local/state/coredesktop/logs/, so you can read what went wrong after the terminal is gone. `update` keeps the logs of the last 10 updates and the last 3 installs or refreshes.
|
||||
|
||||
It is a good idea to reboot after an update especially if you see you have a new Linux kernel installed (and that happens frequently enough for Arch).
|
||||
|
||||
Note: sometimes, for whatever reason, the update barfs. Take a look at [https://archlinux.org](https://archlinux.org) in the "Latest News" section for fixes.
|
||||
|
||||
+20
-3
@@ -21,6 +21,14 @@ clear
|
||||
print_logo
|
||||
cd
|
||||
|
||||
# Log the install to ~/.local/state/coredesktop/logs (update deletes old ones).
|
||||
# It's usually piped into bash, so it can't run itself again under script(1)
|
||||
# the way update does; send its output through tee instead.
|
||||
if mkdir -p -m 700 "$HOME/.local/state/coredesktop/logs"; then
|
||||
INSTALL_LOG="$HOME/.local/state/coredesktop/logs/install-$(date +%Y-%m-%d-%H%M%S).log"
|
||||
exec > >(tee -a "$INSTALL_LOG") 2>&1
|
||||
fi
|
||||
|
||||
# Exit on any error
|
||||
set -e
|
||||
|
||||
@@ -51,9 +59,16 @@ if ! sudo -v; then
|
||||
echo "archinstall, make the user a superuser): https://wiki.archlinux.org/title/Sudo"
|
||||
exit 1
|
||||
fi
|
||||
while kill -0 $$ 2> /dev/null; do sudo -n -v; sleep 60; done 2> /dev/null &
|
||||
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
|
||||
on_exit() {
|
||||
local status=$?
|
||||
kill "$SUDO_KEEPALIVE_PID" 2> /dev/null
|
||||
if (( status != 0 )) && [ -n "$INSTALL_LOG" ]; then
|
||||
echo "The install stopped with an error. The full output is in $INSTALL_LOG"
|
||||
fi
|
||||
}
|
||||
trap on_exit EXIT
|
||||
|
||||
# Ensure git is available before attempting to clone anything
|
||||
if ! command -v git &> /dev/null; then
|
||||
@@ -283,7 +298,9 @@ if ~/.local/bin/configure --list | grep -q '^to do'; then
|
||||
echo "$CONFIGURE_HINT"
|
||||
if { : < /dev/tty; } 2> /dev/null && read -rp "Run configure now? [Y/n] " answer < /dev/tty \
|
||||
&& [[ "$answer" != [nN]* ]]; then
|
||||
~/.local/bin/configure < /dev/tty || true
|
||||
# Straight to the terminal, outside the log: sign-ins need a terminal, and
|
||||
# their codes and links don't belong in a log
|
||||
~/.local/bin/configure < /dev/tty > /dev/tty 2>&1 || true
|
||||
else
|
||||
echo "To do it later, run: configure"
|
||||
fi
|
||||
|
||||
@@ -31,3 +31,35 @@ esac'
|
||||
[ "$status" -eq 0 ]
|
||||
[ -z "$(grep 'pacman -R ' "$STUB_LOG")" ]
|
||||
}
|
||||
|
||||
@test "95-cleanup keeps the newest 10 update logs and 3 install logs" {
|
||||
stub pacman
|
||||
stub sudo
|
||||
stub flatpak
|
||||
logs="$HOME/.local/state/coredesktop/logs"
|
||||
mkdir -p "$logs"
|
||||
for day in 01 02 03 04 05 06 07 08 09 10 11 12; do
|
||||
touch "$logs/update-2026-11-$day-120000.log" "$logs/install-2026-11-$day-120000.log"
|
||||
done
|
||||
touch "$logs/notes.txt"
|
||||
|
||||
run bash "$REPO/updates.d/95-cleanup.sh" < /dev/null
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$(cd "$logs" && echo update-*)" = "$(printf 'update-2026-11-%s-120000.log ' 03 04 05 06 07 08 09 10 11 12 | sed 's/ $//')" ]
|
||||
[ "$(cd "$logs" && echo install-*)" = "install-2026-11-10-120000.log install-2026-11-11-120000.log install-2026-11-12-120000.log" ]
|
||||
[ -f "$logs/notes.txt" ]
|
||||
}
|
||||
|
||||
@test "95-cleanup is fine with no logs or only a few" {
|
||||
stub pacman
|
||||
stub sudo
|
||||
stub flatpak
|
||||
run bash "$REPO/updates.d/95-cleanup.sh" < /dev/null
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
mkdir -p "$HOME/.local/state/coredesktop/logs"
|
||||
touch "$HOME/.local/state/coredesktop/logs/update-2026-11-01-120000.log"
|
||||
run bash "$REPO/updates.d/95-cleanup.sh" < /dev/null
|
||||
[ "$status" -eq 0 ]
|
||||
[ -f "$HOME/.local/state/coredesktop/logs/update-2026-11-01-120000.log" ]
|
||||
}
|
||||
|
||||
+35
-1
@@ -16,7 +16,7 @@ setup() {
|
||||
done
|
||||
|
||||
export RAN="$BATS_TEST_TMPDIR/ran"
|
||||
unset UPDATE_REEXECED UPDATE_PULL_FAILED UPDATE_FORCE
|
||||
unset UPDATE_REEXECED UPDATE_PULL_FAILED UPDATE_FORCE UPDATE_LOG
|
||||
|
||||
stub clear
|
||||
stub git 'exit "${GIT_STATUS:-0}"'
|
||||
@@ -167,6 +167,40 @@ ran() {
|
||||
[ ! -e "$RAN" ]
|
||||
}
|
||||
|
||||
@test "update logs its output to one file, across re-running its new copy" {
|
||||
hook 10-a 'echo "hook a ran"'
|
||||
|
||||
run "$CD/update"
|
||||
[ "$status" -eq 0 ]
|
||||
logs=("$HOME"/.local/state/coredesktop/logs/update-*.log)
|
||||
[ "${#logs[@]}" -eq 1 ]
|
||||
grep -q "Updating CoreDesktop" "${logs[0]}"
|
||||
grep -q "hook a ran" "${logs[0]}"
|
||||
grep -q "^Done\.$" "${logs[0]}"
|
||||
[ "$(stat -c %a "$HOME/.local/state/coredesktop/logs")" = 700 ]
|
||||
}
|
||||
|
||||
@test "when a hook fails, update says where its log is" {
|
||||
hook 10-a 'return 1'
|
||||
|
||||
run "$CD/update"
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" == *"The full output is in $HOME/.local/state/coredesktop/logs/update-"*".log"* ]]
|
||||
}
|
||||
|
||||
@test "clean_log leaves only the text of a terminal session's log" {
|
||||
eval "$(sed -n '/^clean_log() {/,/^}/p' "$REPO/update")"
|
||||
log="$BATS_TEST_TMPDIR/session.log"
|
||||
printf '%s\n' 'Script started on 2026-11-08 10:00:00-07:00 [COMMAND="update"]' > "$log"
|
||||
printf '\033]0;title\007\033[1;32mgreen\033[0m Ünïcode █\r\n' >> "$log"
|
||||
printf '10%%\r50%%\r100%%\r\n' >> "$log"
|
||||
printf '\033[H\033[2J\033[3Jdone\r\n' >> "$log"
|
||||
printf '%s\n' 'Script done on 2026-11-08 10:01:00-07:00 [COMMAND_EXIT_CODE="0"]' >> "$log"
|
||||
|
||||
clean_log "$log"
|
||||
[ "$(cat "$log")" = "$(printf 'green Ünïcode █\n100%%\ndone')" ]
|
||||
}
|
||||
|
||||
@test "an update from before 2026-10-19 that copies this one over itself hands over to it" {
|
||||
# 013acac's update: sudo cp overwrites the file bash is still reading, and
|
||||
# bash reads on in the new one from where it was
|
||||
|
||||
@@ -35,6 +35,31 @@ install_script() {
|
||||
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"
|
||||
@@ -88,7 +113,7 @@ 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 2>/dev/null &
|
||||
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
|
||||
|
||||
@@ -119,6 +144,7 @@ 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
|
||||
|
||||
|
||||
@@ -10,6 +10,20 @@ if command -v paccache &>/dev/null; then
|
||||
sudo paccache -ruk0
|
||||
fi
|
||||
|
||||
# Keep the newest 10 update logs and 3 install logs (refresh runs install.sh
|
||||
# too). Their names sort oldest first.
|
||||
keep_newest() {
|
||||
local keep="$1"
|
||||
shift
|
||||
(( $# > keep )) && rm -f -- "${@:1:$# - keep}"
|
||||
return 0
|
||||
}
|
||||
LOGS="$HOME/.local/state/coredesktop/logs"
|
||||
shopt -s nullglob
|
||||
keep_newest 10 "$LOGS"/update-*.log
|
||||
keep_newest 3 "$LOGS"/install-*.log
|
||||
shopt -u nullglob
|
||||
|
||||
# makepkg builds pkg-debug alongside an AUR package, and removing the package
|
||||
# leaves it behind (asdf-vm-debug). Without its package it's no use to anyone,
|
||||
# so remove it without asking. The exact name: pacman -Q herdr finds herdr-bin
|
||||
|
||||
Reference in New Issue
Block a user