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:
@@ -185,7 +185,7 @@ Besides installing software, CoreDesktop changes these settings. They're listed
|
||||
- **Services turned on**: Avahi, Bluetooth, CUPS (printing), fwupd, GDM, NetworkManager, reflector (weekly), sshd, Docker, Tailscale, ufw and systemd-oomd. Your user is added to the docker group.
|
||||
- **Snapper** (btrfs only): a config for / in /etc/snapper/configs/root.
|
||||
- **NVIDIA** (only if it installs the driver): /etc/mkinitcpio.conf.d/nvidia.conf and NVIDIA's suspend services.
|
||||
- **Your home folder**: lines in ~/.bashrc (CoreDesktop's shell setup, ~/.local/bin on PATH, the Bitwarden SSH agent), the commands in ~/.local/bin (update, refresh, sibling, version, configure, audit), GNOME extensions and their settings, Claude Code's ~/.claude/settings.json if you had none, a user timer that checks for updates, and a user service that pauses your Core's email if its password stops working (see Configure).
|
||||
- **Your home folder**: lines in ~/.bashrc (CoreDesktop's shell setup, ~/.local/bin on PATH, the Bitwarden SSH agent), the commands in ~/.local/bin (update, refresh, sibling, version, configure, audit, report), GNOME extensions and their settings, Claude Code's ~/.claude/settings.json if you had none, a user timer that checks for updates, and a user service that pauses your Core's email if its password stops working (see Configure).
|
||||
- **configure** adds more, only for what you set up: your Nextcloud files mount and network storage in /etc/fstab (SMB passwords in /etc/coredesktop/smb, SFTP mounts as user services named coredesktop-sftp-*), printers in CUPS, GNOME Online Accounts, passwords in your GNOME keyring, and the backup timer.
|
||||
|
||||
## Keep your System Up-to-date
|
||||
@@ -212,6 +212,8 @@ If a step fails, the other steps still run, and the failed ones are listed at th
|
||||
|
||||
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.
|
||||
|
||||
If an install or update fails, it offers to send its log to the CoreDesktop team so we can find and fix the problem. Nothing is sent unless you say yes, and you can read exactly what would be sent first: your user name, host name, home folder, email addresses, IP addresses and Tailscale names are taken out. Along with the log go CoreDesktop's version, the kernel's, the names of the steps that failed and your sibling projects' versions. You get a report ID to mention if you contact us. To send a log later, run `report` (the newest log) or `report <file>`; `report --view` shows what would be sent without sending anything.
|
||||
|
||||
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.
|
||||
@@ -357,7 +359,7 @@ Then remove CoreDesktop's own files:
|
||||
```
|
||||
systemctl --user disable --now coredesktop-update-check.timer coredesktop-backup.timer coredesktop-email-watch.service
|
||||
rm -f ~/.config/systemd/user/coredesktop-*
|
||||
rm -f ~/.local/bin/{update,refresh,sibling,version,configure,audit}
|
||||
rm -f ~/.local/bin/{update,refresh,sibling,version,configure,audit,report}
|
||||
rm -rf ~/.local/share/coredesktop ~/.local/share/siblings ~/.local/state/coredesktop ~/.config/coredesktop
|
||||
```
|
||||
|
||||
|
||||
+14
-1
@@ -27,6 +27,7 @@ cd
|
||||
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
|
||||
TEE_PID=$!
|
||||
fi
|
||||
|
||||
# Exit on any error
|
||||
@@ -66,6 +67,17 @@ on_exit() {
|
||||
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"
|
||||
# Offer to send the log to us, once tee has written all of it (a second at
|
||||
# most). report asks on the terminal, so this needs one.
|
||||
local report="$HOME/.local/share/coredesktop/report" i
|
||||
if [ -f "$report" ] && { : < /dev/tty; } 2> /dev/null; then
|
||||
exec > /dev/tty 2>&1
|
||||
for (( i = 0; i < 10; i++ )); do
|
||||
kill -0 "$TEE_PID" 2> /dev/null || break
|
||||
sleep 0.1
|
||||
done
|
||||
bash "$report" --offer "$INSTALL_LOG" install.sh
|
||||
fi
|
||||
fi
|
||||
}
|
||||
trap on_exit EXIT
|
||||
@@ -91,7 +103,7 @@ cd ~/.local/share/coredesktop
|
||||
# Source utility functions (provides install_packages and other helpers)
|
||||
source utils.sh
|
||||
|
||||
# Install the refresh, update, sibling, version, configure and audit scripts to the user's bin
|
||||
# Install the refresh, update, sibling, version, configure, audit and report scripts to the user's bin
|
||||
mkdir -p ~/.local/bin
|
||||
install_script ~/.local/share/coredesktop/refresh ~/.local/bin/refresh
|
||||
install_script ~/.local/share/coredesktop/update ~/.local/bin/update
|
||||
@@ -99,6 +111,7 @@ install_script ~/.local/share/coredesktop/sibling ~/.local/bin/sibling
|
||||
install_script ~/.local/share/coredesktop/version ~/.local/bin/version
|
||||
install_script ~/.local/share/coredesktop/configure ~/.local/bin/configure
|
||||
install_script ~/.local/share/coredesktop/audit ~/.local/bin/audit
|
||||
install_script ~/.local/share/coredesktop/report ~/.local/bin/report
|
||||
|
||||
# Source the package list (defines SYSTEM_UTILS, DEV_TOOLS, DESKTOP, etc. arrays)
|
||||
if [ ! -f "packages.conf" ]; then
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Purpose : Send an install or update log to the CoreDesktop team, so they can
|
||||
# work out what went wrong and fix it
|
||||
#
|
||||
# report Send the newest log, after showing what will be sent
|
||||
# report FILE Send FILE
|
||||
# report --view [FILE] Show what would be sent, and send nothing
|
||||
# report --offer FILE [STEP...]
|
||||
# What update and install.sh run when they fail: ask on
|
||||
# the terminal whether to send FILE. STEP names what failed.
|
||||
#
|
||||
# Nothing is sent unless the user says yes. Before sending, the log is cleaned
|
||||
# of terminal codes and personal details are replaced: the home folder, user
|
||||
# name, host name, email addresses, IPv4 addresses and Tailscale names.
|
||||
#
|
||||
# What's sent (the receiver on federated.sh implements the other side): an
|
||||
# HTTPS POST of multipart/form-data to $COREDESKTOP_REPORT_URL, with fields
|
||||
# kind install or update, from the log's name
|
||||
# version CoreDesktop's version, e.g. 2026.11.08 (a82bbfc)
|
||||
# failed what failed, e.g. "71-siblings.sh, 46-neovim.sh" (may be empty)
|
||||
# kernel the running kernel, from uname -r
|
||||
# siblings the installed sibling projects, one "name version" per line
|
||||
# log the cleaned, redacted log, gzipped (log.gz, application/gzip)
|
||||
# A 2xx response's body is the report's ID, on one line: up to 40 letters,
|
||||
# digits and dashes. Any other response's
|
||||
# body is a message for the user.
|
||||
|
||||
COREDESKTOP="$HOME/.local/share/coredesktop"
|
||||
LOGS="$HOME/.local/state/coredesktop/logs"
|
||||
REPORT_URL="${COREDESKTOP_REPORT_URL:-https://federated.sh/report}"
|
||||
TTY="${COREDESKTOP_TTY:-/dev/tty}"
|
||||
# The receiver refuses bigger reports
|
||||
MAX_BYTES=$(( 10 * 1024 * 1024 ))
|
||||
|
||||
usage() {
|
||||
cat << EOF
|
||||
Usage: report [FILE] Send a log (the newest, if none is named)
|
||||
report --view [FILE] Show what would be sent, and send nothing
|
||||
EOF
|
||||
}
|
||||
|
||||
newest_log() {
|
||||
local logs
|
||||
mapfile -t logs < <(ls -t "$LOGS"/*.log 2> /dev/null)
|
||||
[ "${#logs[@]}" -gt 0 ] && echo "${logs[0]}"
|
||||
}
|
||||
|
||||
# Escape $1 for use in a sed regular expression
|
||||
regex_escape() {
|
||||
printf '%s' "$1" | sed 's/[][\.*^$/]/\\&/g'
|
||||
}
|
||||
|
||||
# Print log $1 as it would be sent: without script(1)'s header and footer,
|
||||
# terminal codes or progress redraws, and with personal details replaced.
|
||||
# LC_ALL=C so the ranges mean bytes, whatever the locale. IPv4 addresses are
|
||||
# replaced twice because a match takes the character after an address, which
|
||||
# can be the one before the next; dotted versions such as 1.0.4.1.3 are kept.
|
||||
redact() {
|
||||
local home user host
|
||||
home="$(regex_escape "$HOME")"
|
||||
user="$(regex_escape "${USER:-$(id -un)}")"
|
||||
host="$(regex_escape "$(uname -n)")"
|
||||
LC_ALL=C sed -E \
|
||||
-e '1{/^Script started on /d}' -e '/^Script done on .*COMMAND_EXIT_CODE/d' \
|
||||
-e 's/\x1b\][^\x07\x1b]*(\x07|\x1b\\)//g' -e 's#\x1b\[[0-9;?]*[ -/]*[@-~]##g' \
|
||||
-e 's/\x1b[()][0-9A-Za-z]//g' -e 's/\x1b[=>]//g' -e 's/\r+$//' -e 's/.*\r//' \
|
||||
-e "s#${home}([/[:space:]'\":]|\$)#~\\1#g" \
|
||||
-e 's/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/<email>/g' \
|
||||
-e 's/[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\.ts\.net/<tailscale-name>/g' \
|
||||
-e 's/(^|[^0-9.])([0-9]{1,3}\.){3}[0-9]{1,3}([^0-9.]|$)/\1<ip>\3/g' \
|
||||
-e 's/(^|[^0-9.])([0-9]{1,3}\.){3}[0-9]{1,3}([^0-9.]|$)/\1<ip>\3/g' \
|
||||
-e "s/(^|[^A-Za-z0-9_-])${user}([^A-Za-z0-9_-]|\$)/\\1<user>\\2/g" \
|
||||
-e "s/(^|[^A-Za-z0-9_-])${host}([^A-Za-z0-9_-]|\$)/\\1<host>\\2/g" \
|
||||
"$1"
|
||||
}
|
||||
|
||||
coredesktop_version() {
|
||||
git -C "$COREDESKTOP" log -1 --format='%cd (%h)' --date=format:%Y.%m.%d 2> /dev/null || echo unknown
|
||||
}
|
||||
|
||||
# "name version" for each installed sibling, without its URL, which can name a person
|
||||
sibling_versions() {
|
||||
[ -f "$COREDESKTOP/sibling" ] || return 0
|
||||
bash "$COREDESKTOP/sibling" list 2> /dev/null | grep -v '^No sibling' \
|
||||
| awk '{ $NF = ""; sub(/ +$/, ""); print }'
|
||||
}
|
||||
|
||||
# show_redacted FILE: page through what would be sent
|
||||
show_redacted() {
|
||||
if command -v less > /dev/null && [ -t 1 ]; then
|
||||
redact "$1" | less
|
||||
else
|
||||
redact "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
# send FILE [STEP...]: send FILE's report and print its ID
|
||||
send() {
|
||||
local file="$1" kind tmp response code body
|
||||
shift
|
||||
kind="$(basename "$file")"
|
||||
kind="${kind%%-*}"
|
||||
[[ "$kind" == install || "$kind" == update ]] || kind=other
|
||||
|
||||
tmp="$(mktemp -d)" || return 1
|
||||
redact "$file" | gzip -9 > "$tmp/log.gz"
|
||||
if (( $(stat -c %s "$tmp/log.gz") > MAX_BYTES )); then
|
||||
echo "This log is too big to send ($(( $(stat -c %s "$tmp/log.gz") / 1024 / 1024 )) MB compressed)."
|
||||
rm -rf "$tmp"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local failed
|
||||
failed="$(printf '%s, ' "$@")"
|
||||
failed="${failed%, }"
|
||||
|
||||
echo "Sending..."
|
||||
# Values go in with -F name=<file, which sends them as they are: with
|
||||
# name=value, curl would treat a value starting with @ or < as a file
|
||||
printf '%s' "$kind" > "$tmp/kind"
|
||||
coredesktop_version | tr -d '\n' > "$tmp/version"
|
||||
printf '%s' "$failed" > "$tmp/failed"
|
||||
uname -r | tr -d '\n' > "$tmp/kernel"
|
||||
sibling_versions > "$tmp/siblings"
|
||||
response="$(curl -sS --max-time 120 -A CoreDesktop-report \
|
||||
-F "kind=<$tmp/kind" -F "version=<$tmp/version" -F "failed=<$tmp/failed" \
|
||||
-F "kernel=<$tmp/kernel" -F "siblings=<$tmp/siblings" \
|
||||
-F "log=@$tmp/log.gz;type=application/gzip;filename=log.gz" \
|
||||
-w '\n%{http_code}' "$REPORT_URL" 2>&1)"
|
||||
local status=$?
|
||||
rm -rf "$tmp"
|
||||
|
||||
code="${response##*$'\n'}"
|
||||
body="${response%$'\n'*}"
|
||||
[[ "$response" == *$'\n'* ]] || body="$response"
|
||||
# A 2xx that isn't a report ID is some other page, not the receiver
|
||||
if (( status == 0 )) && [[ "$code" == 2?? && "$body" =~ ^[A-Za-z0-9-]{1,40}$ ]]; then
|
||||
echo "Sent. Thank you! Your report's ID is $body; mention it if you contact us about this."
|
||||
return 0
|
||||
fi
|
||||
echo "Couldn't send the report: ${body:-no response}"
|
||||
echo "To try again later, run: report $file"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Open the terminal once, on file descriptor 3, for ask to read answers from.
|
||||
# Fails if there's no terminal to ask on.
|
||||
open_tty() {
|
||||
{ exec 3< "$TTY"; } 2> /dev/null
|
||||
}
|
||||
|
||||
# ask QUESTION: read an answer from the terminal into $answer
|
||||
ask() {
|
||||
read -rp "$1" answer <&3
|
||||
}
|
||||
|
||||
# offer FILE [STEP...]: ask whether to send FILE, show it first if asked
|
||||
offer() {
|
||||
local file="$1"
|
||||
[ -f "$file" ] || return 0
|
||||
open_tty || return 0
|
||||
echo
|
||||
echo "Send this log to the CoreDesktop team, so we can look into what went wrong?"
|
||||
echo "Personal details such as your user name, host name and email addresses are"
|
||||
echo "taken out first."
|
||||
while ask "[v] view what would be sent, [y] send, [N] don't send: "; do
|
||||
case "$answer" in
|
||||
[vV]*) show_redacted "$file" ;;
|
||||
[yY]*) send "$@"; return 0 ;;
|
||||
*) break ;;
|
||||
esac
|
||||
done
|
||||
echo "Not sent. To send it later, run: report $file"
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
-h|--help) usage ;;
|
||||
--offer) shift; offer "$@" ;;
|
||||
--view)
|
||||
file="${2:-$(newest_log)}"
|
||||
[ -f "$file" ] || { echo "No log to show."; exit 1; }
|
||||
show_redacted "$file"
|
||||
;;
|
||||
-*) usage; exit 1 ;;
|
||||
*)
|
||||
file="${1:-$(newest_log)}"
|
||||
[ -f "$file" ] || { echo "No log found in $LOGS."; exit 1; }
|
||||
echo "Log: $file"
|
||||
if ! open_tty; then
|
||||
# No terminal to ask on: running report is itself the request to send
|
||||
send "$file"
|
||||
exit
|
||||
fi
|
||||
while true; do
|
||||
ask "[v] view what would be sent, [y] send, [N] don't send: " || answer=n
|
||||
case "$answer" in
|
||||
[vV]*) show_redacted "$file" ;;
|
||||
[yY]*) send "$file"; exit ;;
|
||||
*) echo "Not sent."; exit 0 ;;
|
||||
esac
|
||||
done
|
||||
;;
|
||||
esac
|
||||
@@ -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"
|
||||
|
||||
@@ -94,6 +94,7 @@ 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
|
||||
install_script "$COREDESKTOP/report" ~/.local/bin/report
|
||||
|
||||
if [[ -z "$UPDATE_REEXECED" ]]; then
|
||||
export UPDATE_REEXECED=1
|
||||
@@ -144,7 +145,11 @@ done
|
||||
|
||||
if (( ${#FAILED[@]} > 0 )); then
|
||||
echo "Done, with errors in: ${FAILED[*]}"
|
||||
[ -n "$UPDATE_LOG" ] && echo "The full output is in $UPDATE_LOG"
|
||||
if [ -n "$UPDATE_LOG" ]; then
|
||||
echo "The full output is in $UPDATE_LOG"
|
||||
# Ask, on the terminal if there is one, whether to send it to us
|
||||
[ -x ~/.local/bin/report ] && ~/.local/bin/report --offer "$UPDATE_LOG" "${FAILED[@]}"
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
Reference in New Issue
Block a user