When configure had nothing left to do, install.sh ended on the kernel check
and the firewall's warnings just above it, so a successful run could be taken
for a failure. Move the end of install.sh to system/finish-install.sh, which
now says the install worked before the reboot advice and configure offer, and
test it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Plain-text replies usually end with one, and the ID check counted it as
part of the ID, so a report that was sent was said to have failed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
Stashing a plugin's mismatched files wasn't enough for LuaSnip: the
interrupted update had also left new files git won't overwrite and a
half-cloned submodule, so lazy.nvim still couldn't check out the new
version, and said nothing. Move such a plugin to
~/.local/state/coredesktop/nvim-plugins/ instead, so Lazy! sync installs
it afresh and any edits to it are kept. A plugin the earlier version of
this step stashed is moved the same way.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
An interrupted plugin update can leave a plugin's files out of step with
its checkout, and lazy.nvim then refuses to update it, every time. Before
syncing, update now stashes such changes, so they can still be recovered
with git stash pop, and puts the plugin's submodules back.
Flathub retired org.mozilla.Thunderbird in favor of
org.mozilla.thunderbird_esr, so update tried to reinstall the old ID on
every run. Install the new one. flatpak update already moves existing
installs over; a migration carries a skip-packages entry for the old ID
over to the new one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A sibling's version is its latest git tag (v1.4.0, or v1.4.0+3 for commits
after it), or, without tags, its commit's date and short hash, like
CoreDesktop's own. The version shown as available is that of the newest
commit on the default branch, which is what update installs.
sibling list shows each sibling's version. sibling check fetches every
sibling at once and shows which have a newer version, and sibling update
[name...] updates the named siblings or all of them. 71-siblings.sh now
runs sibling update, so update and version share one way of updating.
version lists the installed siblings with any newer version and, when run
in a terminal, asks for each one whether to update it now. version --check
and the update notification still only look at CoreDesktop.
The sibling skill docs describe how to release a version by tagging it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
archinstall's "Copy ISO network configuration", which a user who connected
with iwctl may pick, turns on iwd and systemd-networkd. CoreDesktop then
turns on NetworkManager too, and after a restart two services fight over
the Wi-Fi card. system/wifi.sh disables iwd and systemd-networkd, without
stopping them, when NetworkManager is enabled and isn't set to use iwd
itself. install.sh runs it, and a migration runs it on existing machines.
The README tells users to choose NetworkManager in archinstall.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
013acac's update (in use 2026-08-14 to 2026-10-19) copies the new update
over itself with sudo cp while bash is still reading it, so bash reads on
from byte 1484 of the new file: today, the middle of the logo, and the
update dies with a syntax error. Put a line at exactly that byte that
execs the new update, in a here-document a normal run skips. Found by
30-upgrade-from-baseline at ca658dc; the test replays 013acac's update.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
makepkg builds pkg-debug alongside an AUR package, and removing the
package leaves it behind: the migration that dropped asdf-vm left
asdf-vm-debug. Found by 30-upgrade-from-baseline at 0c0ddf5, whose
machine kept it where a fresh install has none.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An AUR package built with makepkg's debug option comes with pkg-debug,
which nothing depends on, so pacman -Rns leaves it behind. voxtype-debug
then conflicts with voxtype-bin-debug, and update fails on machines
installed before voxtype-bin replaced voxtype. Found by
30-upgrade-from-baseline at 0c0ddf5.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
install.sh at every baseline in smoketest/baselines clones from
https://gitea.federated.computer/david/coredesktop.git, which wasn't in
COREDESKTOP_URLS. So 30-upgrade-from-baseline installed today's real
upstream (via Gitea's redirect) instead of the baseline, and failed when
the old install.sh asked for files that have since gone.
Add a test that every URL install.sh has ever cloned from is in the list.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Since a65f07c ran each hook in a subshell, a hook that does its work as
it's sourced (12-migrations, 15-services, 20-aur, 30-flatpak and others)
had its failure lost: the run_update check after source left the
subshell's status at 0, so update said "Done." and exited 0. The smoketest's
40-failing-migration caught it. update now exits the subshell with the
sourced hook's status.
Also, the smoketest scenarios that check whether a migration ran look for
"Running migration " so install.sh's "Running migrations..." heading
doesn't match (11-refresh failed on it).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
run_in_vm prints how long it has run and the log's latest line, so an
hour-long install.sh shows progress without tail -f.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
upstream_set pushes the commit under test from the working copy, so
coredesktop-dev's pre-push hook ran make check on every push to the VM.
Inside the smoketest those checks saw VM_GIT_URL and friends, the
smoketest tests pushed to the real VM and failed, and the hook stopped
the push, failing the installed image's build. The push now uses
--no-verify, and the tests clear the smoketest's variables.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
So a long command (install.sh takes an hour or more) can be followed with
tail -f; run_in_vm prints the log's path when it starts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
install.sh now stops early, with a clear message, if this isn't 64-bit
Arch, it's run as root, there's no internet, or sudo doesn't work.
system/preflight.sh then asks before going on with too little disk space
or / not on btrfs, and offers to switch another login screen to GDM and
PulseAudio to PipeWire. Keeping another login screen puts GDM in
services-off, so install and update stop failing to enable it; a
migration does the same on existing machines. It says nothing when all
is well, as refresh runs install.sh again.
Intel GPUs also get intel-media-driver for hardware video decoding, with
a migration for existing machines.
The mise migration links ~/.local/bin/mise to /usr/bin/mise instead of
deleting it, so shells opened before the update don't print an error at
every prompt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in the shellcheck lint and bats tests. Both sides fixed the same
shellcheck warnings; this keeps the remote's versions of 20-aur.sh,
80-gnome-extensions.sh and .shellcheckrc, which silence false positives
per line rather than for the whole repo.
The Gitea workflow now runs make lint, so it checks the same scripts as
the Makefile, and the README points contributors at make check. The test
helper's REPO, used only by the .bats files, is marked for shellcheck,
and the update test expects the pull to be fast-forward only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
make lint runs shellcheck on every tracked bash script, make test runs
the bats suite in tests/, and make check runs both. The tests cover the
update orchestrator (hook isolation, failure reporting, pull and
free-space handling) and utils.sh (migrations, package install and
removal, services, initramfs build check), using stub commands so they
never touch the real system.
Fixes from the first lint run: 20-aur.sh stops if a cd fails rather
than building or deleting in the wrong directory, and
80-gnome-extensions.sh refuses to rm -rf with an empty path. The rest
are false positives, silenced in .shellcheckrc or per line.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>