Files
coredesktop/install.sh
T
davidandClaude Opus 5.5 bd82063bbb 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>
2026-11-08 11:28:53 -07:00

321 lines
12 KiB
Bash
Executable File

#!/bin/bash -i
#
# Purpose : CoreDesktop installer
# Print the logo
print_logo() {
cat << "EOF"
██
█ ██
██ ██████ █████ ██ █████ █████ ████ ██ █████ ██
██ ██ ██ ███ ███████ ██ ██ ██ ██ ██ ████████ ██ ██ ███████
█████ ██████ ██ ██ ██████ ██ ████████ ██ █ ███████ ██ ██
██ ██████ ███████ ██████ ██ ██████ ██████ ██████ ███████
EOF
}
# Clear screen and show logo
clear
print_logo
cd
# Log the install to ~/.local/state/coredesktop/logs (update deletes old ones).
# It's usually piped into bash, so it can't run itself again under script(1)
# the way update does; send its output through tee instead.
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
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.
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 > /dev/null 2>&1 &
SUDO_KEEPALIVE_PID=$!
on_exit() {
local status=$?
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
# Ensure git is available before attempting to clone anything
if ! command -v git &> /dev/null; then
echo "Installing git..."
sudo pacman -S git --noconfirm
else
echo "git is installed."
fi
# Update or clone the CoreDesktop repo
echo "Getting the latest version of CoreDesktop..."
if [ -d "$HOME/.local/share/coredesktop/.git" ]; then
git -C ~/.local/share/coredesktop pull --ff-only
else
git clone https://gitea.federated.computer/Federated_Computer/coredesktop.git ~/.local/share/coredesktop
fi
cd ~/.local/share/coredesktop
# Source utility functions (provides install_packages and other helpers)
source utils.sh
# 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
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
echo "Error: packages.conf not found!"
exit 1
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, ...)
echo "Adding the Federated Computer package repo..."
ensure_federated_repo
# Bring the system fully up to date before installing anything new
echo "Updating system..."
sudo pacman -Sy --needed --noconfirm archlinux-keyring
sudo pacman -Su --noconfirm
cd ~/
# Install yay AUR helper if not present — required for AUR packages installed later
if ! command -v yay &> /dev/null; then
echo "Installing yay AUR helper..."
sudo pacman -S --needed git base-devel --noconfirm
if [[ -d "yay" ]]; then
echo "yay directory already exists, removing it..."
rm -rf yay
fi
echo "Cloning yay repository..."
git clone https://aur.archlinux.org/yay.git
cd yay
echo "Building yay..."
makepkg -si --noconfirm
cd ..
rm -rf yay
else
echo "yay is already installed."
fi
# Remove replaced packages first, so their replacements don't conflict with them
echo "Removing unwanted packages..."
remove_packages "${REMOVE_PACMAN[@]}"
remove_flatpaks "${REMOVE_FLATPAKS[@]}"
# Install packages grouped by category as defined in packages.conf
echo "Installing system utilities..."
install_packages "${SYSTEM_UTILS[@]}"
echo "Installing development tools..."
install_packages "${DEV_TOOLS[@]}"
echo "Installing system maintenance tools..."
install_packages "${MAINTENANCE[@]}"
echo "Installing desktop environment..."
install_packages "${DESKTOP[@]}"
echo "Installing media packages..."
install_packages "${MEDIA[@]}"
echo "Installing fonts..."
install_packages "${FONTS[@]}"
echo "Installing pipx applications..."
install_pipx_apps "${PIPX[@]}"
# Enable the services in SERVICES, and enable and start those in START_SERVICES,
# then register the SSH Avahi service so the machine is discoverable over mDNS
echo "Configuring services..."
enable_services "${SERVICES[@]}"
enable_services --now "${START_SERVICES[@]}"
sudo cp /usr/share/doc/avahi/ssh.service /etc/avahi/services/
cd ~/.local/share/coredesktop
# Install GNOME shell extensions via the dedicated script
echo "Installing GNOME extensions..."
. gnome/gnome-extensions.sh
# Install Flatpak apps — some programs run better sandboxed as Flatpaks
echo "Installing flatpaks..."
. install-flatpaks.sh
# Install TPM (Tmux Plugin Manager) and configured plugins
echo "Installing tmux plug-in manager..."
. install-tpm.sh
# Install Kickstart Neovim config as the default Neovim configuration
echo "Installing Kickstart Neovim..."
. install-kickstart-nvim.sh
# Install Claude Code and configure desktop notification hooks
echo "Installing Claude Code..."
bash claude.sh
# Run AI agents (opencode, hermes) in a sandbox, and turn on Claude Code's own
echo "Setting up sandboxed AI agents..."
bash ~/.local/share/coredesktop/sandbox/setup.sh
# Configure Bitwarden SSH agent
echo "Configuring Bitwarden SSH agent..."
. bitwarden.sh
# Add the current user to the docker group and start the Docker daemon
# Group membership takes effect at next login (newgrp would start an interactive shell)
sudo usermod -aG docker $USER
sudo systemctl enable docker.service
sudo systemctl start docker.service
# Enable and start Tailscale daemon
if ! systemctl is-enabled tailscaled &> /dev/null; then
sudo systemctl enable tailscaled
fi
if ! systemctl is-active tailscaled &> /dev/null; then
sudo systemctl start tailscaled
fi
# SSH keepalives, login lockout, shutdown timeout, OOM protection, boot, SSH PATH
echo "Applying system defaults..."
bash ~/.local/share/coredesktop/system/defaults.sh
# Leave Wi-Fi to NetworkManager alone if archinstall turned on iwd as well
echo "Checking Wi-Fi..."
bash ~/.local/share/coredesktop/system/wifi.sh
# SSH logins with keys only, not passwords
echo "Configuring the SSH server..."
bash ~/.local/share/coredesktop/system/sshd.sh
# Block incoming connections except SSH, mDNS, LocalSend and Tailscale
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
echo "Installing GPU drivers..."
bash ~/.local/share/coredesktop/system/vulkan.sh
bash ~/.local/share/coredesktop/system/nvidia.sh
# Prompt, zoxide, fzf key bindings, aliases and functions
echo "Configuring the shell..."
bash ~/.local/share/coredesktop/shell/setup.sh
# Keep WinBoat's Windows container from starting at boot (if it's set up)
bash ~/.local/share/coredesktop/system/winboat.sh
# Notify in GNOME when a new version of CoreDesktop is available
echo "Configuring update notifications..."
bash ~/.local/share/coredesktop/system/update-notify.sh
# Pause a Core's email when the mail server stops taking its password
echo "Configuring email password checks..."
bash ~/.local/share/coredesktop/system/email-watch.sh
# Apply GNOME dconf settings only if the settings file has changed since last run
bash ~/.local/share/coredesktop/gnome/apply-gnome-settings.sh
# Fix mDNS configuration if systemd-resolved is managing DNS on this system
if systemctl is-enabled systemd-resolved.service &> /dev/null; then
echo "systemd-resolved is enabled, applying mDNS fix..."
bash ~/.local/share/coredesktop/fixmdns.sh
fi
# Run one-time migrations not yet recorded on this machine. On a fresh install
# they repeat setup done above, which is safe because each one is idempotent.
echo "Running migrations..."
run_migrations
CONFIGURE_HINT="configure connects to your Federated Core, sets up backups and signs you in to the AI agents."
# If the running kernel's modules are gone, a newer kernel was installed during
# this run. configure waits for the reboot: until then, modules it needs, such
# as Tailscale's tun and davfs2's fuse, can't be loaded.
if [ ! -d "/usr/lib/modules/$(uname -r)" ]; then
echo "A newer kernel has been installed. Reboot, then run: configure"
echo "$CONFIGURE_HINT"
exit 0
fi
echo "Kernel is up to date, no reboot needed. You may want to consider a reboot if other significant software was updated or installed."
# Offer configure only if something is still to do, as refresh runs this too.
# This script is usually piped into bash, so ask on the terminal, not stdin.
if ~/.local/bin/configure --list | grep -q '^to do'; then
echo
echo "$CONFIGURE_HINT"
if { : < /dev/tty; } 2> /dev/null && read -rp "Run configure now? [Y/n] " answer < /dev/tty \
&& [[ "$answer" != [nN]* ]]; then
# Straight to the terminal, outside the log: sign-ins need a terminal, and
# their codes and links don't belong in a log
~/.local/bin/configure < /dev/tty > /dev/tty 2>&1 || true
else
echo "To do it later, run: configure"
fi
fi