Check the machine before installing, and keep old shells' mise working
shellcheck / shellcheck (push) Successful in 11s

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>
This commit is contained in:
2026-10-31 08:25:46 -06:00
co-authored by Claude Opus 5.5
parent cb66ce9719
commit a102e6fe0b
8 changed files with 295 additions and 10 deletions
+3 -1
View File
@@ -60,6 +60,8 @@ wget -qO- https://gitea.federated.computer/Federated_Computer/coredesktop/raw/br
```
3) Enter your password when asked, once at the start. Be patient: it takes a while.
First it checks the machine. It stops if this isn't Arch, if you run it as root, if your user can't use sudo, or if there's no internet. It asks before going on if there's less than about 60 GB free, or if / isn't on btrfs (then there are no snapshots before updates, and only reinstalling Arch with btrfs changes that). If the login screen isn't GNOME's, or sound goes through PulseAudio, it offers to switch to GNOME's login screen and to PipeWire.
4) At the end, it offers to run `configure`, which connects to your Federated Core, sets up backups and signs you in to the AI agents (see "Configure" below). If the install brought in a newer kernel, it asks you to reboot first; run `configure` after the reboot.
5) If something fails, just run the command again. It skips what's already done.
@@ -149,7 +151,7 @@ sudo umount /mnt
### GPU drivers
The installer adds the Vulkan driver for Intel and AMD GPUs. If there's an NVIDIA GPU and archinstall's proprietary driver wasn't chosen, it installs NVIDIA's driver: the open kernel modules for GTX 16xx / RTX 20xx and newer, or the 580xx legacy driver for older cards back to the GTX 900 series. Reboot afterwards.
The installer adds the Vulkan driver for Intel and AMD GPUs, and Intel's video decoding driver, so videos and calls don't decode on the CPU (AMD's comes with Mesa). If there's an NVIDIA GPU and archinstall's proprietary driver wasn't chosen, it installs NVIDIA's driver: the open kernel modules for GTX 16xx / RTX 20xx and newer, or the 580xx legacy driver for older cards back to the GTX 900 series. Reboot afterwards.
### System defaults
+28 -1
View File
@@ -24,10 +24,33 @@ cd
# Exit on any error
set -e
# What can't be fixed from here, checked before anything changes. The rest of
# the checks need the repo (system/preflight.sh, below).
if [ "$(id -u)" -eq 0 ]; then
echo "Run this as your own user, not as root. It asks for your password when it needs sudo."
exit 1
fi
if [ ! -f /etc/arch-release ] || ! command -v pacman &> /dev/null; then
echo "CoreDesktop needs Arch Linux. See \"Installing Arch\" in the README."
exit 1
fi
if [ "$(uname -m)" != x86_64 ]; then
echo "CoreDesktop needs a 64-bit PC (x86_64); this is $(uname -m)."
exit 1
fi
if ! curl -fsS --max-time 15 -o /dev/null https://archlinux.org; then
echo "Couldn't reach archlinux.org. Connect to the internet and run this again."
exit 1
fi
# Ask for the password once, then keep sudo's cached credentials fresh until
# the install exits, so long builds don't stop for a password again. Unlike a
# passwordless sudoers entry, nothing is left behind if the install is killed.
sudo -v
if ! sudo -v; then
echo "Couldn't use sudo. If your password was right, your user needs sudo rights (in"
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 &
SUDO_KEEPALIVE_PID=$!
trap 'kill "$SUDO_KEEPALIVE_PID" 2> /dev/null' EXIT
@@ -70,6 +93,10 @@ fi
source packages.conf
source remove-packages.conf
# Disk space, btrfs, display manager and sound server: stops, asks, or offers
# to fix what it finds
bash ~/.local/share/coredesktop/system/preflight.sh
echo "Starting full system setup..."
# Federated Computer's own packages (Federated Calendar, ...)
+8 -5
View File
@@ -1,9 +1,10 @@
#!/bin/bash
# mise now comes from pacman, and shell/bashrc activates it, so remove the copy
# mise.run installed in ~/.local/bin and the line it added to ~/.bashrc. Ruby and
# Rails are no longer installed for everyone, only with configure languages:
# machines that have them keep them, and update keeps Rails on new Rubies.
# mise now comes from pacman, and shell/bashrc activates it, so replace the copy
# mise.run installed in ~/.local/bin with a link to it, and remove the line
# mise.run added to ~/.bashrc. Ruby and Rails are no longer installed for
# everyone, only with configure languages: machines that have them keep them,
# and update keeps Rails on new Rubies.
LANGUAGES_CONF="$HOME/.config/coredesktop/languages"
DEFAULT_GEMS="$HOME/.default-gems"
@@ -19,7 +20,9 @@ if [ -f ~/.bashrc ] && grep -qE '\.local/bin/mise activate' ~/.bashrc; then
rm -f "$tmp"
[ $status -eq 0 ] || exit 1
fi
rm -f ~/.local/bin/mise
# A link, not nothing: shells opened before this update still run the old
# copy's path before every prompt, and would print an error until closed
ln -sfn /usr/bin/mise ~/.local/bin/mise || exit 1
# Ruby that CoreDesktop installed: count it as chosen with configure languages.
# From home, so a project's mise.toml in the current directory doesn't count.
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
# Intel GPUs get their video decoding driver (intel-media-driver) too, so
# video and calls don't decode on the CPU
bash "$HOME/.local/share/coredesktop/system/vulkan.sh"
+14
View File
@@ -0,0 +1,14 @@
#!/bin/bash
# update turns on GDM, which fails every time while another login screen is
# on. Keep that one, as install.sh now offers, by marking GDM as turned off.
SERVICES_OFF="$HOME/.config/coredesktop/services-off"
dm="$(readlink /etc/systemd/system/display-manager.service 2> /dev/null)"
dm="$(basename "${dm:-none}")"
[ "$dm" = none ] || [ "$dm" = gdm.service ] && exit 0
grep -qsx "system gdm.service" "$SERVICES_OFF" && exit 0
mkdir -p "$(dirname "$SERVICES_OFF")" || exit 1
echo "system gdm.service" >> "$SERVICES_OFF"
echo "Your login screen is ${dm%.service}, so update no longer tries to turn on GNOME's (GDM)."
+109
View File
@@ -0,0 +1,109 @@
#!/bin/bash
#
# Purpose : Check the machine before install.sh sets it up, and offer to fix
# what can be fixed: too little disk space, / not on btrfs, a login
# screen other than GNOME's GDM, PulseAudio instead of PipeWire.
# Exits 1 to stop the install. refresh runs install.sh again, so it
# says nothing on a machine where all is well.
COREDESKTOP="$HOME/.local/share/coredesktop"
source "$COREDESKTOP/utils.sh"
SERVICES_OFF="$HOME/.config/coredesktop/services-off"
# install.sh is usually piped into bash, so ask on the terminal
TTY="${COREDESKTOP_TTY:-/dev/tty}"
# ask <question> <default y|n> <answer without a terminal y|n>
ask() {
local answer
if ! { : < "$TTY"; } 2> /dev/null; then
[ "$3" = y ]
return
fi
if [ "$2" = y ]; then
read -rp "$1 [Y/n] " answer < "$TTY"
[[ "$answer" != [nN]* ]]
else
read -rp "$1 [y/N] " answer < "$TTY"
[[ "$answer" == [yY]* ]]
fi
}
# Set up before: install.sh records migrations as its last step
already_installed() {
[ -d "$HOME/.local/state/coredesktop/migrations" ]
}
check_disk() {
local need=60 free
already_installed && need=10
free="$(df -k --output=avail / | tail -n 1 | tr -d ' ')"
[[ "$free" =~ ^[0-9]+$ ]] || return 0
free=$(( free / 1024 / 1024 ))
(( free < need )) || return 0
echo "Only $free GB is free on /, and this needs about $need GB."
ask "Continue anyway?" n n
}
# Can't be fixed without reinstalling Arch, so only asked about the first time
check_btrfs() {
local fstype
already_installed && return 0
fstype="$(findmnt -no FSTYPE /)"
[ "$fstype" = btrfs ] && return 0
echo "/ is on $fstype, not btrfs, so update can't take a snapshot first, and a bad"
echo "update can't be rolled back (see Snapshots in the README). Only reinstalling Arch"
echo "with btrfs, archinstall's default, changes that."
ask "Continue without snapshots?" y y
}
# CoreDesktop turns on GDM, which fails while another login screen is on
check_display_manager() {
local dm
dm="$(readlink /etc/systemd/system/display-manager.service 2> /dev/null)"
dm="$(basename "${dm:-none}")"
[ "$dm" = none ] || [ "$dm" = gdm.service ] && return 0
grep -qsx "system gdm.service" "$SERVICES_OFF" && return 0
echo "This machine's login screen is ${dm%.service}, not GNOME's (GDM)."
if ask "Switch to GNOME's login screen? It takes over when you restart." y n; then
# enable_services turns GDM on later in the install
sudo systemctl disable "$dm" || return 1
else
mkdir -p "$(dirname "$SERVICES_OFF")"
echo "system gdm.service" >> "$SERVICES_OFF"
echo "Keeping ${dm%.service}. Choose GNOME from its session menu when you log in."
echo "To switch later: sudo systemctl disable $dm && sudo systemctl enable gdm"
echo "then delete the line \"system gdm.service\" from $SERVICES_OFF"
fi
}
# PipeWire replaces PulseAudio; GNOME's screen sharing needs it, and calls in
# Jitsi and Element work best with it
check_sound() {
local plugins=()
is_installed pulseaudio || return 0
echo "This machine plays sound through PulseAudio. PipeWire replaces it: GNOME's"
echo "screen sharing needs it, and calls in Jitsi and Element work best with it."
if ! ask "Switch to PipeWire?" y n; then
echo "Keeping PulseAudio."
return 0
fi
# Its modules (bluetooth, ...) need it, so they go first; PipeWire does
# what they did
mapfile -t plugins < <(pacman -Qq | grep '^pulseaudio-')
if (( ${#plugins[@]} > 0 )); then
sudo pacman -R --noconfirm "${plugins[@]}" || return 1
fi
# --ask=4 answers yes to removing pulseaudio, which pipewire-pulse replaces
sudo pacman -S --needed --noconfirm --ask=4 pipewire pipewire-pulse pipewire-alsa wireplumber || return 1
echo "Switched to PipeWire. It takes over when you log out and in again."
}
check_disk || exit 1
check_btrfs || exit 1
check_display_manager || exit 1
check_sound || exit 1
exit 0
+6 -3
View File
@@ -1,7 +1,9 @@
#!/bin/bash
#
# Purpose : Install the Vulkan driver for each Intel or AMD GPU in the machine.
# NVIDIA's Vulkan driver comes with nvidia-utils (see nvidia.sh).
# Purpose : Install the Vulkan driver for each Intel or AMD GPU in the machine,
# and Intel's video decoding driver (VA-API), so video and calls
# don't decode on the CPU. AMD's comes with mesa, NVIDIA's with its
# driver (see nvidia.sh).
set -e
@@ -12,7 +14,8 @@ PACKAGES=()
for device in /sys/bus/pci/devices/*; do
[[ $(< "$device/class") == 0x03* ]] || continue
case "$(< "$device/vendor")" in
0x8086) PACKAGES+=(vulkan-intel) ;;
# intel-media-driver covers Broadwell (2014) and newer
0x8086) PACKAGES+=(vulkan-intel intel-media-driver) ;;
0x1002) PACKAGES+=(vulkan-radeon) ;;
esac
done
+122
View File
@@ -0,0 +1,122 @@
#!/usr/bin/env bats
load test_helper
# A machine where all is well: lots of space, / on btrfs, GDM or no login
# screen yet, no PulseAudio. Tests change one thing each.
setup() {
common_setup
mkdir -p "$HOME/.local/share"
ln -s "$REPO" "$HOME/.local/share/coredesktop"
export FREE_KIB=$((200 * 1024 * 1024)) FSTYPE=btrfs DM="" INSTALLED=""
stub df 'printf "Avail\n%s\n" "$FREE_KIB"'
stub findmnt 'echo "$FSTYPE"'
stub readlink '[ -n "$DM" ] && echo "/usr/lib/systemd/system/$DM"'
stub pacman '
case "$1" in
-Qi) [[ " $INSTALLED " == *" $2 "* ]] ;;
-Qq) for p in $INSTALLED; do echo "$p"; done ;;
esac'
stub sudo
# No terminal unless a test gives answers
export COREDESKTOP_TTY="$BATS_TEST_TMPDIR/no-tty/tty"
}
answer() {
export COREDESKTOP_TTY="$BATS_TEST_TMPDIR/answer"
echo "$1" > "$COREDESKTOP_TTY"
}
already_installed() {
mkdir -p "$HOME/.local/state/coredesktop/migrations"
}
@test "preflight says nothing and changes nothing when all is well" {
run bash "$REPO/system/preflight.sh"
[ "$status" -eq 0 ]
[ -z "$output" ]
not_called sudo
}
@test "preflight stops on too little disk space unless told to go on" {
export FREE_KIB=$((30 * 1024 * 1024))
answer n
run bash "$REPO/system/preflight.sh"
[ "$status" -eq 1 ]
[[ "$output" == *"Only 30 GB is free"* ]]
answer y
run bash "$REPO/system/preflight.sh"
[ "$status" -eq 0 ]
}
@test "preflight needs less space on a machine that's already set up" {
export FREE_KIB=$((30 * 1024 * 1024))
already_installed
run bash "$REPO/system/preflight.sh"
[ "$status" -eq 0 ]
[ -z "$output" ]
}
@test "preflight warns once that / isn't btrfs, and goes on without a terminal" {
export FSTYPE=ext4
run bash "$REPO/system/preflight.sh"
[ "$status" -eq 0 ]
[[ "$output" == *"/ is on ext4, not btrfs"* ]]
already_installed
run bash "$REPO/system/preflight.sh"
[ "$status" -eq 0 ]
[ -z "$output" ]
}
@test "preflight switches another login screen to GDM when asked to" {
export DM=sddm.service
answer y
run bash "$REPO/system/preflight.sh"
[ "$status" -eq 0 ]
called sudo systemctl disable sddm.service
}
@test "preflight keeps another login screen, and update then leaves GDM off" {
export DM=sddm.service
answer n
run bash "$REPO/system/preflight.sh"
[ "$status" -eq 0 ]
not_called sudo
grep -qx "system gdm.service" "$HOME/.config/coredesktop/services-off"
# Not asked again
run bash "$REPO/system/preflight.sh"
[ -z "$output" ]
}
@test "preflight changes no login screen without a terminal to ask on" {
export DM=sddm.service
run bash "$REPO/system/preflight.sh"
[ "$status" -eq 0 ]
not_called sudo
}
@test "preflight replaces PulseAudio and its modules with PipeWire when asked to" {
export INSTALLED="pulseaudio pulseaudio-bluetooth"
answer y
run bash "$REPO/system/preflight.sh"
[ "$status" -eq 0 ]
called sudo pacman -R --noconfirm pulseaudio-bluetooth
called sudo pacman -S --needed --noconfirm --ask=4 pipewire pipewire-pulse pipewire-alsa wireplumber
}
@test "preflight keeps PulseAudio when told to, or without a terminal" {
export INSTALLED="pulseaudio"
answer n
run bash "$REPO/system/preflight.sh"
[ "$status" -eq 0 ]
[[ "$output" == *"Keeping PulseAudio."* ]]
not_called sudo
export COREDESKTOP_TTY="$BATS_TEST_TMPDIR/no-tty/tty"
run bash "$REPO/system/preflight.sh"
[ "$status" -eq 0 ]
not_called sudo
}