improve version and mirror updating
shellcheck / shellcheck (push) Failing after 14s

This commit is contained in:
2026-11-04 07:08:44 -07:00
parent 7f37a1b9ee
commit c3ac9f49d0
10 changed files with 175 additions and 2 deletions
+4 -2
View File
@@ -170,13 +170,14 @@ The installer adds the Vulkan driver for Intel and AMD GPUs, and Intel's video d
Besides installing software, CoreDesktop changes these settings. They're listed so you know what you're running, and so you can undo them (see "Uninstalling").
- **/etc/pacman.conf**: adds the `[federated]` repository and trusts its signing key (see Pacman above).
- **Mirrors**: reflector rewrites /etc/pacman.d/mirrorlist once a week with fast HTTPS mirrors that synced with Arch in the last 12 hours, using /etc/xdg/reflector/reflector.conf. To choose your own mirrors, delete that file's first (CoreDesktop) line and edit the rest; to stop CoreDesktop changing your mirrors at all, add `reflector` to ~/.config/coredesktop/skip-packages.
- **SSH**: sshd is on, with key login only (/etc/ssh/sshd_config.d/10-coredesktop-keys-only.conf). The SSH client keepalives are in /etc/ssh/ssh_config.d/20-coredesktop-keepalive.conf.
- **Firewall**: ufw is on, with the rules under Firewall above, and ufw-docker's rules in /etc/ufw/after.rules.
- **Login lockout**: deny and unlock_time in /etc/security/faillock.conf.
- **systemd**: a 10-second stop timeout (/etc/systemd/system.conf.d/ and /etc/systemd/user.conf.d/10-coredesktop-timeout.conf), systemd-oomd for apps (/etc/systemd/user/app.slice.d/10-coredesktop-oomd.conf), and NetworkManager-wait-online is masked.
- **/etc/security/pam_env.conf**: a PATH line, so SSH commands find ~/.local/bin and mise.
- **mDNS**: Avahi is on, nss-mdns is added to /etc/nsswitch.conf, and SSH is advertised in /etc/avahi/services/ssh.service.
- **Services turned on**: Avahi, Bluetooth, CUPS (printing), fwupd, GDM, NetworkManager, sshd, Docker, Tailscale, ufw and systemd-oomd. Your user is added to the docker group.
- **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).
@@ -196,6 +197,7 @@ The script pulls the latest CoreDesktop, then runs each step in ~/.local/share/c
- stops if there's less than 10 GiB free on / (run `UPDATE_FORCE=1 update` to skip the check)
- snapshots / first, if Snapper is set up (see Snapshots above)
- switches to up-to-date mirrors first if yours is more than a day behind Arch, as it would otherwise miss new packages without saying so
- updates the Arch keyring, then Arch and GNOME, then AUR packages, Flatpaks, the languages you installed with mise, the pipx apps CoreDesktop installs, GNOME extensions and firmware
- warns you not to reboot if the initramfs failed to rebuild
- runs any one-time migrations, the way existing machines pick up new CoreDesktop setup
@@ -232,7 +234,7 @@ Video driver Mesa 26.2.6
Last update 2026-10-22 13:43
```
The kernel and video driver (NVIDIA's if it's installed, otherwise Mesa, which drives Intel and AMD graphics) come from Arch, so they're shown separately. "version" tells you if Arch has a newer kernel or video driver, or if a newer version of CoreDesktop is available; "update" installs them. If a newer kernel is waiting for a reboot, it says that too.
The kernel and video driver (NVIDIA's if it's installed, otherwise Mesa, which drives Intel and AMD graphics) come from Arch, so they're shown separately. "version" tells you if Arch has a newer kernel or video driver, or if a newer version of CoreDesktop is available; "update" installs them. If a newer kernel is waiting for a reboot, it says that too. It can only see what your package mirror has, so if the mirror is more than a day behind Arch it warns you; "update" then switches to up-to-date mirrors.
You don't have to check by hand: a few minutes after you log in, and every few hours after that, CoreDesktop checks for a new version and shows a notification if there is one. Click it to run "update" in a terminal, or dismiss it. Once dismissed, you won't be notified again until there's an even newer version.
+4
View File
@@ -219,6 +219,10 @@ bash ~/.local/share/coredesktop/system/sshd.sh
echo "Configuring the firewall..."
bash ~/.local/share/coredesktop/system/firewall.sh
# Keep pacman on mirrors that are up to date with Arch
echo "Configuring package mirrors..."
bash ~/.local/share/coredesktop/system/mirrors.sh
# Snapshot / before each update (btrfs only)
echo "Configuring Snapper..."
bash ~/.local/share/coredesktop/system/snapper.sh
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
# Machines installed before system/mirrors.sh can be stuck on a mirror that
# stopped syncing, which hides new packages such as kernels. Set up reflector
# to keep the mirrorlist fresh, and pick fresh mirrors now.
bash "$HOME/.local/share/coredesktop/system/mirrors.sh"
+3
View File
@@ -112,6 +112,8 @@ MAINTENANCE=(
gnome-extra
gnome-keyring
pacman-contrib
# Keeps the mirrorlist on up-to-date mirrors (system/mirrors.sh)
reflector
)
# Desktop environment
@@ -164,6 +166,7 @@ SERVICES=(
fwupd-refresh.timer
gdm.service
NetworkManager.service
reflector.timer
sshd.service
)
+52
View File
@@ -0,0 +1,52 @@
#!/bin/bash
#
# Purpose : Keep pacman's mirrorlist on mirrors that are up to date with Arch.
# A mirror that stops syncing doesn't cause an error: update and
# version just stop seeing new packages, such as a new kernel.
#
# reflector.timer picks fresh mirrors once a week, and update picks new ones
# straight away when the first mirror falls a day behind (updates.d/10-pacman.sh).
# To choose mirrors yourself, edit /etc/xdg/reflector/reflector.conf: once it
# doesn't start with the CoreDesktop line below, this leaves it alone. To stop
# CoreDesktop changing the mirrorlist at all, add reflector to
# ~/.config/coredesktop/skip-packages.
COREDESKTOP="$HOME/.local/share/coredesktop"
source "$COREDESKTOP/utils.sh"
CONF=/etc/xdg/reflector/reflector.conf
MARKER="# CoreDesktop: choose up-to-date mirrors (system/mirrors.sh)"
install_packages reflector || exit 1
# Skipped in skip-packages
is_installed reflector || exit 0
# Ours, or reflector's own that no one has edited, is ours to replace (pacman
# translates UNMODIFIED, hence LC_ALL=C)
if head -n 1 "$CONF" 2> /dev/null | grep -qxF "$MARKER" \
|| LC_ALL=C pacman -Qii reflector 2> /dev/null | grep -qE "^UNMODIFIED[[:space:]]+$CONF$"; then
# Mirrors that synced in the last 12 hours, fastest first. --latest rates only
# the 20 most recently synced, as rating every mirror takes minutes.
sudo tee "$CONF" > /dev/null << EOF
$MARKER
# Delete the line above to choose your own; CoreDesktop then leaves this alone.
--save /etc/pacman.d/mirrorlist
--protocol https
--age 12
--completion-percent 100
--latest 20
--sort rate
EOF
else
echo "Keeping your own mirror choices in $CONF."
fi
enable_services reflector.timer
# A failed refresh leaves the mirrorlist as it was
echo "Choosing up-to-date mirrors..."
if ! sudo systemctl start reflector.service; then
echo "Couldn't choose new mirrors; keeping the ones in /etc/pacman.d/mirrorlist."
fi
exit 0
+47
View File
@@ -258,3 +258,50 @@ pacman_log() {
run initramfs_build_failed
[ "$status" -eq 0 ]
}
# --- mirrors ----------------------------------------------------------------
setup_mirrors() {
export MIRRORLIST="$BATS_TEST_TMPDIR/mirrorlist"
cat > "$MIRRORLIST" << 'LIST'
## Worldwide
#Server = https://commented.example/archlinux/$repo/os/$arch
Server = https://first.example/archlinux/$repo/os/$arch
Server = https://second.example/$repo/os/$arch
LIST
# lastsync is $SYNCED, a Unix time
stub curl 'echo "$SYNCED"; [ -n "$SYNCED" ]'
source "$REPO/utils.sh"
}
@test "first_mirror is the first Server line that isn't commented out" {
setup_mirrors
[ "$(first_mirror)" = "https://first.example/archlinux" ]
}
@test "mirror_age_hours reads the first mirror's lastsync" {
setup_mirrors
export SYNCED="$(( $(date +%s) - 30 * 3600 ))"
run mirror_age_hours
[ "$status" -eq 0 ]
[ "$output" = 30 ]
called curl -sf --max-time 10 https://first.example/archlinux/lastsync
}
@test "mirror_age_hours fails when the mirror can't be reached or has no lastsync" {
setup_mirrors
export SYNCED=""
run mirror_age_hours
[ "$status" -ne 0 ]
export SYNCED="<html>Not Found</html>"
run mirror_age_hours
[ "$status" -ne 0 ]
}
@test "mirror_age_hours fails without a mirrorlist" {
setup_mirrors
rm "$MIRRORLIST"
run mirror_age_hours
[ "$status" -ne 0 ]
not_called curl
}
+21
View File
@@ -22,6 +22,10 @@ setup() {
stub pacman 'grep "^$2 " <<< "$INSTALLED" || exit 1'
stub checkupdates 'printf "%s" "$PENDING"; [ -n "$PENDING" ] || exit 2'
export INSTALLED="mesa 1:26.2.6-1" PENDING=""
# The mirror synced $SYNCED_HOURS ago (an hour by default)
export MIRRORLIST="$BATS_TEST_TMPDIR/mirrorlist" SYNCED_HOURS=1
echo 'Server = https://mirror.example/archlinux/$repo/os/$arch' > "$MIRRORLIST"
stub curl 'echo "$(( $(date +%s) - SYNCED_HOURS * 3600 ))"'
}
@test "version shows the kernel and Mesa, with nothing newer to install" {
@@ -31,6 +35,7 @@ setup() {
[[ "$output" == *"Kernel 9.9.1-arch1-1 (newer kernel installed, reboot to use it)"$'\n'* ]]
[[ "$output" == *"Video driver Mesa 26.2.6"$'\n'* ]]
[[ "$output" != *"is available"* ]]
[[ "$output" != *"mirror"* ]]
}
@test "version suggests update for a newer kernel and video driver" {
@@ -68,3 +73,19 @@ setup() {
[[ "$output" == *"Kernel 9.9.1-arch1-1"* ]]
[[ "$output" != *"is available"* ]]
}
@test "version warns when the package mirror is behind Arch" {
export SYNCED_HOURS=73
run bash "$CD/version"
[ "$status" -eq 0 ]
[[ "$output" == *"Your package mirror (https://mirror.example/archlinux) last synced with Arch 3 days ago"* ]]
[[ "$output" == *"Run update"* ]]
}
@test "version doesn't warn about a mirror it can't reach" {
rm "$STUB_BIN/curl"
stub curl 'exit 7'
run bash "$CD/version"
[ "$status" -eq 0 ]
[[ "$output" != *"mirror"* ]]
}
+7
View File
@@ -17,6 +17,13 @@ remove_flatpaks "${REMOVE_FLATPAKS[@]}"
echo "Checking the Federated Computer package repo..."
ensure_federated_repo || STATUS=1
# A stale mirror hides new packages instead of failing, so pick fresh ones
# before syncing. mirrors.sh also sets up reflector on machines without it.
if age="$(mirror_age_hours)" && (( age >= MIRROR_STALE_HOURS )); then
echo "Your package mirror ($(first_mirror)) last synced with Arch $age hours ago."
bash "$COREDESKTOP/system/mirrors.sh"
fi
# A machine that hasn't updated in a while can have a keyring too old to verify
# the new packages. Update it first; the -Su below completes this -Sy.
echo "Updating Arch keyring..."
+23
View File
@@ -199,6 +199,29 @@ initramfs_build_failed() {
(( succeeded == 0 || succeeded < built ))
}
# pacman downloads from the first mirror in this list (system/mirrors.sh keeps
# it fresh). Overridable for the tests.
MIRRORLIST="${MIRRORLIST:-/etc/pacman.d/mirrorlist}"
# A mirror further behind Arch than this is stale; most sync every few hours
MIRROR_STALE_HOURS=24
# The first mirror in MIRRORLIST, without its /$repo/os/$arch
first_mirror() {
sed -nE 's/^[[:space:]]*Server[[:space:]]*=[[:space:]]*//p' "$MIRRORLIST" 2> /dev/null \
| head -n 1 | sed -E 's#/\$repo.*##'
}
# Print how many hours ago the first mirror last synced with Arch, from the
# timestamp mirrors publish in lastsync. Fails if it couldn't tell.
mirror_age_hours() {
local mirror lastsync
mirror="$(first_mirror)"
[ -n "$mirror" ] || return 1
lastsync="$(curl -sf --max-time 10 "$mirror/lastsync" 2> /dev/null)" || return 1
[[ "$lastsync" =~ ^[0-9]+$ ]] || return 1
echo $(( ($(date +%s) - lastsync) / 3600 ))
}
# Install a script atomically (copy to a temp file, then rename over the target)
# so a copy of it that is running right now keeps reading the old file
install_script() {
+8
View File
@@ -124,3 +124,11 @@ echo "CoreDesktop $version"
echo "Kernel $kernel"
[ -n "$driver" ] && echo "Video driver $driver"
echo "Last update $last_update"
# Packages newer than the mirror's last sync aren't shown above, or installed
if age="$(mirror_age_hours)" && (( age >= MIRROR_STALE_HOURS )); then
if (( age >= 48 )); then ago="$(( age / 24 )) days"; else ago="$age hours"; fi
echo
echo -e "\e[33mYour package mirror ($(first_mirror)) last synced with Arch $ago ago, so"
echo -e "newer packages may be missing. Run update to switch to up-to-date mirrors.\e[0m"
fi