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>
321 lines
12 KiB
Bash
Executable File
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
|