Offer to send the log to us when an install or update fails
When update or install.sh fails and there's a terminal, ask whether to send its log to the CoreDesktop team, with the option of reading exactly what would be sent first. Nothing is sent without a yes, and nothing is asked without a terminal. The new report command sends a log later (report, report FILE) or shows what would be sent (report --view). Before sending, the log is cleaned of terminal codes, and the home folder, user name, host name, email addresses, IPv4 addresses and Tailscale names are replaced. CoreDesktop's version, the kernel's, the failed steps and sibling versions go along with it. The request format the receiver on federated.sh implements is described at the top of report. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
load test_helper
|
||||
|
||||
# report, with curl replaced by a stub that saves the fields it was given and
|
||||
# answers with $CURL_REPLY
|
||||
|
||||
setup() {
|
||||
common_setup
|
||||
export USER=alice
|
||||
stub uname '
|
||||
case "$1" in
|
||||
-n) echo lab ;;
|
||||
-r) echo 6.99.1-arch1-1 ;;
|
||||
*) /usr/bin/uname "$@" ;;
|
||||
esac'
|
||||
# Save each -F field: name=<file as name=contents, the log as log.gz
|
||||
stub curl '
|
||||
fields="$BATS_TEST_TMPDIR/fields"
|
||||
mkdir -p "$fields"
|
||||
while (( $# )); do
|
||||
if [ "$1" = -F ]; then
|
||||
case "$2" in
|
||||
log=@*) path="${2#log=@}"; cp "${path%%;*}" "$fields/log.gz" ;;
|
||||
*=\<*) cp "${2#*=<}" "$fields/${2%%=*}" ;;
|
||||
esac
|
||||
shift
|
||||
fi
|
||||
shift
|
||||
done
|
||||
printf "%b" "${CURL_REPLY:-CD-7Q2K\n200}"
|
||||
exit "${CURL_STATUS:-0}"'
|
||||
export COREDESKTOP_TTY="$BATS_TEST_TMPDIR/tty"
|
||||
LOGS="$HOME/.local/state/coredesktop/logs"
|
||||
mkdir -p "$LOGS"
|
||||
LOG="$LOGS/update-2026-11-08-110307.log"
|
||||
printf '%s\n' "Updating CoreDesktop..." "Cloning into '$HOME/.local/share/x'..." \
|
||||
"Done, with errors in: 71-siblings.sh" > "$LOG"
|
||||
}
|
||||
|
||||
# answers LINE...: what the user types at the terminal
|
||||
answers() {
|
||||
printf '%s\n' "$@" > "$COREDESKTOP_TTY"
|
||||
}
|
||||
|
||||
field() {
|
||||
cat "$BATS_TEST_TMPDIR/fields/$1"
|
||||
}
|
||||
|
||||
@test "redaction replaces personal details and keeps version numbers" {
|
||||
printf '%s\n' \
|
||||
"Cloning into '$HOME/.local/share/nvim/lazy/LuaSnip'..." \
|
||||
"[sudo] password for alice: " \
|
||||
"ardop 1.0.4.1.3 is up to date. gridtracker2 2.261108.4-1, python 3.14" \
|
||||
"ssh alice@lab from 192.168.1.20; mail david@federated.computer" \
|
||||
"lab.tail1234.ts.net 100.101.102.103 127.0.0.1" \
|
||||
"alicesmith and labrador stay" > "$LOG"
|
||||
|
||||
run bash "$REPO/report" --view "$LOG"
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$output" = "$(printf '%s\n' \
|
||||
"Cloning into '~/.local/share/nvim/lazy/LuaSnip'..." \
|
||||
"[sudo] password for <user>: " \
|
||||
"ardop 1.0.4.1.3 is up to date. gridtracker2 2.261108.4-1, python 3.14" \
|
||||
"ssh <user>@<host> from <ip>; mail <email>" \
|
||||
"<tailscale-name> <ip> <ip>" \
|
||||
"alicesmith and labrador stay")" ]
|
||||
! grep -q curl "$STUB_LOG"
|
||||
}
|
||||
|
||||
@test "redaction also cleans a terminal session's log" {
|
||||
printf 'Script started on 2026-11-08 [COMMAND="update"]\n\033[1;32mok\033[0m\r\n10%%\r100%%\r\nScript done on 2026-11-08 [COMMAND_EXIT_CODE="1"]\n' > "$LOG"
|
||||
run bash "$REPO/report" --view "$LOG"
|
||||
[ "$output" = "$(printf 'ok\n100%%')" ]
|
||||
}
|
||||
|
||||
@test "after a failed update, report offers to send the log and sends it on yes" {
|
||||
answers y
|
||||
run bash "$REPO/report" --offer "$LOG" 71-siblings.sh 46-neovim.sh
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"Your report's ID is CD-7Q2K"* ]]
|
||||
[ "$(field kind)" = update ]
|
||||
[ "$(field failed)" = "71-siblings.sh, 46-neovim.sh" ]
|
||||
[ "$(field kernel)" = 6.99.1-arch1-1 ]
|
||||
# The log arrives cleaned and redacted
|
||||
[ "$(gzip -dc "$BATS_TEST_TMPDIR/fields/log.gz")" = "$(printf '%s\n' "Updating CoreDesktop..." \
|
||||
"Cloning into '~/.local/share/x'..." "Done, with errors in: 71-siblings.sh")" ]
|
||||
}
|
||||
|
||||
@test "the offer can show the log first, and sends nothing on no" {
|
||||
answers v n
|
||||
run bash "$REPO/report" --offer "$LOG" 71-siblings.sh
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"Cloning into '~/.local/share/x'"* ]]
|
||||
[[ "$output" == *"Not sent. To send it later, run: report $LOG"* ]]
|
||||
! grep -q curl "$STUB_LOG"
|
||||
}
|
||||
|
||||
@test "without a terminal, the offer sends nothing and says nothing" {
|
||||
export COREDESKTOP_TTY=/nonexistent/tty
|
||||
run bash "$REPO/report" --offer "$LOG" 71-siblings.sh
|
||||
[ "$status" -eq 0 ]
|
||||
[ -z "$output" ]
|
||||
! grep -q curl "$STUB_LOG"
|
||||
}
|
||||
|
||||
@test "report sends the newest log" {
|
||||
touch -d '1 hour ago' "$LOG"
|
||||
printf 'install output\n' > "$LOGS/install-2026-11-08-120000.log"
|
||||
answers y
|
||||
run bash "$REPO/report"
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"install-2026-11-08-120000.log"* ]]
|
||||
[ "$(field kind)" = install ]
|
||||
[ -z "$(field failed)" ]
|
||||
}
|
||||
|
||||
@test "a refused report says why and how to try again" {
|
||||
answers y
|
||||
export CURL_REPLY='Too many reports from this address; try again tomorrow.\n429'
|
||||
run bash "$REPO/report" "$LOG"
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" == *"Couldn't send the report: Too many reports from this address"* ]]
|
||||
[[ "$output" == *"To try again later, run: report $LOG"* ]]
|
||||
}
|
||||
|
||||
@test "an unreachable server is reported too" {
|
||||
answers y
|
||||
export CURL_REPLY='curl: (6) Could not resolve host: federated.sh' CURL_STATUS=6
|
||||
run bash "$REPO/report" "$LOG"
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" == *"Couldn't send the report: curl: (6) Could not resolve host"* ]]
|
||||
}
|
||||
|
||||
@test "a value starting with @ or < is sent as text, not read as a file" {
|
||||
answers y
|
||||
run bash "$REPO/report" --offer "$LOG" "@/etc/passwd"
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$(field failed)" = "@/etc/passwd" ]
|
||||
}
|
||||
|
||||
@test "a page that isn't the receiver's answer isn't taken for a report ID" {
|
||||
answers y
|
||||
export CURL_REPLY='#!/bin/bash\necho installing\n200'
|
||||
run bash "$REPO/report" "$LOG"
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" == *"Couldn't send the report"* ]]
|
||||
[[ "$output" != *"Sent."* ]]
|
||||
}
|
||||
+22
-2
@@ -11,7 +11,7 @@ setup() {
|
||||
CD="$HOME/.local/share/coredesktop"
|
||||
mkdir -p "$CD/updates.d" "$HOME/.local/bin"
|
||||
cp "$REPO/update" "$CD/update"
|
||||
for script in refresh sibling version configure audit; do
|
||||
for script in refresh sibling version configure audit report; do
|
||||
echo '#!/bin/bash' > "$CD/$script"
|
||||
done
|
||||
|
||||
@@ -51,7 +51,7 @@ ran() {
|
||||
[[ "$output" == *"Done."* ]]
|
||||
called git -C "$CD" pull --ff-only
|
||||
ran a b
|
||||
for script in update refresh sibling version configure audit; do
|
||||
for script in update refresh sibling version configure audit report; do
|
||||
[ -x "$HOME/.local/bin/$script" ]
|
||||
done
|
||||
}
|
||||
@@ -188,6 +188,26 @@ ran() {
|
||||
[[ "$output" == *"The full output is in $HOME/.local/state/coredesktop/logs/update-"*".log"* ]]
|
||||
}
|
||||
|
||||
@test "when a step fails, update offers to send its log, naming what failed" {
|
||||
printf '#!/bin/bash\nprintf "%%s\\n" "$@" > "$BATS_TEST_TMPDIR/report-args"\n' > "$CD/report"
|
||||
hook 10-a 'return 1'
|
||||
hook 20-b 'return 1'
|
||||
|
||||
run "$CD/update"
|
||||
[ "$status" -eq 1 ]
|
||||
log=("$HOME"/.local/state/coredesktop/logs/update-*.log)
|
||||
[ "$(cat "$BATS_TEST_TMPDIR/report-args")" = "$(printf '%s\n' --offer "${log[0]}" 10-a.sh 20-b.sh)" ]
|
||||
}
|
||||
|
||||
@test "update doesn't offer to send its log when nothing failed" {
|
||||
printf '#!/bin/bash\ntouch "$BATS_TEST_TMPDIR/report-ran"\n' > "$CD/report"
|
||||
hook 10-a 'true'
|
||||
|
||||
run "$CD/update"
|
||||
[ "$status" -eq 0 ]
|
||||
[ ! -e "$BATS_TEST_TMPDIR/report-ran" ]
|
||||
}
|
||||
|
||||
@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"
|
||||
|
||||
Reference in New Issue
Block a user