numerous updates

This commit is contained in:
2026-10-21 08:33:19 -06:00
parent 474a678d45
commit 0905f858a9
27 changed files with 651 additions and 5 deletions
+54 -1
View File
@@ -93,6 +93,48 @@ The printing service (cups) is turned on. This may need more work to "just work"
mDNS is installed and enabled so the your can find and SSH to your machine on your LAN using {machinename}.local. mDNS is installed and enabled so the your can find and SSH to your machine on your LAN using {machinename}.local.
### Firewall
The ufw firewall is on. It blocks incoming connections except SSH (22), mDNS (5353/udp), LocalSend (53317) and anything arriving over Tailscale. ufw-docker is installed too, because ports published by Docker containers otherwise get around ufw. To let a container's port through, use `sudo ufw-docker allow {container} {port}`. To open a port for something else, e.g. a dev server you want to reach from another machine, use `sudo ufw allow {port}/tcp`. `sudo ufw status` shows the current rules.
### Shell
Your ~/.bashrc sources ~/.local/share/coredesktop/shell/bashrc, which sets up the starship prompt, zoxide (`z {part of a path}` jumps to a directory you've used before), fzf key bindings (Ctrl-R searches history, Ctrl-T inserts a file path), eza for `ls`/`ll`/`la`/`lt`, and the aliases and functions in [shell/](shell/). If you already have ~/.config/starship.toml, your prompt config is used. Anything you add to ~/.bashrc after the CoreDesktop line overrides it.
### Snapshots
If / is btrfs (the archinstall default), Snapper takes a snapshot of / before every `update` and keeps the last 5. /home is a separate subvolume and isn't included, so rolling back never touches your files. `sudo snapper -c root list` shows the snapshots.
These snapshots can't be picked from the boot menu. To roll back after a bad update, boot the Arch ISO and replace the @ subvolume with a snapshot:
```
mount -o subvolid=5 /dev/{root partition} /mnt
mv /mnt/@ /mnt/@.broken
btrfs subvolume snapshot /mnt/@.snapshots/{number}/snapshot /mnt/@
reboot
```
If the disk is encrypted, open it with `cryptsetup open` first and mount the opened device. Once the system works again, free the space the broken root used:
```
sudo mount -o subvolid=5 /dev/{root partition} /mnt
sudo btrfs subvolume delete /mnt/@.broken
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.
### System defaults
- SSH connections notice a dropped link within a minute instead of hanging (override per host in ~/.ssh/config).
- Logins allow 10 wrong passwords before locking the account for 2 minutes (Arch's default is 3 tries, then 10 minutes).
- Shutdown waits at most 10 seconds for a service that won't stop, instead of 90.
- If memory runs out, systemd-oomd closes the app causing it instead of the whole desktop freezing.
- Boot no longer waits for the network.
- Commands run over SSH (`ssh {machine} {command}`) can find tools in ~/.local/bin and mise.
## Keep your System Up-to-date ## Keep your System Up-to-date
From time-to-time, you'll want to update your system. Hopefully you'll do this more than once-a-month. From time-to-time, you'll want to update your system. Hopefully you'll do this more than once-a-month.
@@ -103,7 +145,16 @@ To update the system type into the terminal:
update update
``` ```
The script does three things. It runs 'flatpak update' to update your Flatpak apps, it runs 'gem update' to update your Rails environment, and it runs 'sudo pacman -Syu' to update your Arch and Gnome installation. The script pulls the latest CoreDesktop, then runs each step in ~/.local/share/coredesktop/updates.d/ in order. Among other things it:
- 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)
- updates the Arch keyring, then Arch and GNOME, then AUR packages, Flatpaks, Ruby, 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
- lists packages that are no longer needed, and the command to remove them
If a step fails, the other steps still run, and the failed ones are listed at the end.
It is a good idea to reboot after an update especially if you see you have a new Linux kernel installed (and that happens frequently enough for Arch). It is a good idea to reboot after an update especially if you see you have a new Linux kernel installed (and that happens frequently enough for Arch).
@@ -141,6 +192,8 @@ If you want to use mDNS networking on your LAN (the ability to log in to other l
This whole "project" is licensed under the MIT License. Want to help make CoreDesktop better? Awesome! We welcome any and all help. Just contribute a PR and we're off to a better CoreDesktop. This whole "project" is licensed under the MIT License. Want to help make CoreDesktop better? Awesome! We welcome any and all help. Just contribute a PR and we're off to a better CoreDesktop.
Changes to install.sh only reach new installs. If existing machines need the change too, also add a migration: a script named `migrations/$(date +%s).sh` that `update` runs once on each machine. It may also run on fresh installs, so make it safe to repeat.
## Bonus ## Bonus
Bonus: here's a [nice song to listen to while you're installing](https://music.youtube.com/watch?v=y-5o2f9wzmw&si=_z9kcKHRwj-KTs7w). Bonus: here's a [nice song to listen to while you're installing](https://music.youtube.com/watch?v=y-5o2f9wzmw&si=_z9kcKHRwj-KTs7w).
+27 -1
View File
@@ -70,7 +70,8 @@ echo "Starting full system setup..."
# Bring the system fully up to date before installing anything new # Bring the system fully up to date before installing anything new
echo "Updating system..." echo "Updating system..."
sudo pacman -Syu --noconfirm sudo pacman -Sy --needed --noconfirm archlinux-keyring
sudo pacman -Su --noconfirm
cd ~/ cd ~/
@@ -168,6 +169,26 @@ if ! systemctl is-active tailscaled &> /dev/null; then
sudo systemctl start tailscaled sudo systemctl start tailscaled
fi fi
# SSH keepalives, login lockout, shutdown timeout, OOM protection, boot, SSH PATH
echo "Applying system defaults..."
bash ~/.local/share/coredesktop/system/defaults.sh
# Block incoming connections except SSH, mDNS, LocalSend and Tailscale
echo "Configuring the firewall..."
bash ~/.local/share/coredesktop/system/firewall.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
# Apply GNOME dconf settings only if the settings file has changed since last run # Apply GNOME dconf settings only if the settings file has changed since last run
bash ~/.local/share/coredesktop/gnome/apply-gnome-settings.sh bash ~/.local/share/coredesktop/gnome/apply-gnome-settings.sh
@@ -177,6 +198,11 @@ if systemctl is-enabled systemd-resolved.service &> /dev/null; then
bash ~/.local/share/coredesktop/fixmdns.sh bash ~/.local/share/coredesktop/fixmdns.sh
fi 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
# Check whether the running kernel matches the installed modules — if not, a # Check whether the running kernel matches the installed modules — if not, a
# newer kernel was installed during this run and a reboot is required # newer kernel was installed during this run and a reboot is required
if ! ls /usr/lib/modules/$(uname -r) > /dev/null 2>&1; then if ! ls /usr/lib/modules/$(uname -r) > /dev/null 2>&1; then
+4
View File
@@ -1,5 +1,7 @@
#!/bin/bash #!/bin/bash
# Rename the coreupdate and corerefresh commands to update and refresh
COREDESKTOP="$HOME/.local/share/coredesktop" COREDESKTOP="$HOME/.local/share/coredesktop"
source "$COREDESKTOP/utils.sh" source "$COREDESKTOP/utils.sh"
@@ -15,3 +17,5 @@ if [ -f ~/.local/bin/corerefresh ]; then
install_script "$COREDESKTOP/refresh" ~/.local/bin/refresh install_script "$COREDESKTOP/refresh" ~/.local/bin/refresh
sudo rm -f ~/.local/bin/corerefresh sudo rm -f ~/.local/bin/corerefresh
fi fi
exit 0
+4
View File
@@ -0,0 +1,4 @@
#!/bin/bash
# Hook CoreDesktop's shell setup into ~/.bashrc
bash "$HOME/.local/share/coredesktop/shell/setup.sh"
+4
View File
@@ -0,0 +1,4 @@
#!/bin/bash
# SSH keepalives, login lockout, shutdown timeout, OOM protection, boot and SSH PATH
bash "$HOME/.local/share/coredesktop/system/defaults.sh"
+4
View File
@@ -0,0 +1,4 @@
#!/bin/bash
# Turn on the ufw firewall
bash "$HOME/.local/share/coredesktop/system/firewall.sh"
+4
View File
@@ -0,0 +1,4 @@
#!/bin/bash
# Set up Snapper for pre-update snapshots on btrfs
bash "$HOME/.local/share/coredesktop/system/snapper.sh"
+4
View File
@@ -0,0 +1,4 @@
#!/bin/bash
# Install Vulkan drivers for Intel and AMD GPUs
bash "$HOME/.local/share/coredesktop/system/vulkan.sh"
+4
View File
@@ -1,6 +1,7 @@
# System utilities # System utilities
SYSTEM_UTILS=( SYSTEM_UTILS=(
age age
bash-completion
bluez bluez
bluez-utils bluez-utils
borg borg
@@ -48,6 +49,8 @@ SYSTEM_UTILS=(
tailscale tailscale
traceroute traceroute
tree tree
ufw
ufw-docker
unzip unzip
usbutils usbutils
vi vi
@@ -128,6 +131,7 @@ MEDIA=(
FONTS=( FONTS=(
noto-fonts noto-fonts
noto-fonts-emoji noto-fonts-emoji
ttf-nerd-fonts-symbols
) )
# Services to enable # Services to enable
+31
View File
@@ -0,0 +1,31 @@
# CoreDesktop aliases, sourced by shell/bashrc
# Files
if command -v eza &> /dev/null; then
alias ls='eza --group-directories-first --icons=auto'
alias ll='eza -lh --group-directories-first --icons=auto'
alias la='eza -lha --group-directories-first --icons=auto'
alias lt='eza --tree --level=2 --long --icons=auto --git'
fi
# Fuzzy-find a file with a preview, or open the one you pick in your editor
alias ff="fzf --preview 'bat --style=numbers --color=always {}'"
alias eff='${EDITOR:-nvim} "$(ff)"'
alias decompress='tar -xzf'
# Directories
alias ..='cd ..'
alias ...='cd ../..'
alias ....='cd ../../..'
# Tools
alias g='git'
alias lg='lazygit'
alias d='docker'
alias ld='lazydocker'
# Git
alias gcm='git commit -m'
alias gcam='git commit -a -m'
alias gcad='git commit -a --amend'
+37
View File
@@ -0,0 +1,37 @@
# CoreDesktop shell setup, sourced from ~/.bashrc (added by shell/setup.sh).
# Anything in ~/.bashrc after the line that sources this file overrides it.
# Only for interactive shells
[[ $- != *i* ]] && return
COREDESKTOP_SHELL="$HOME/.local/share/coredesktop/shell"
# History: keep a lot of it, skip duplicates, and don't let shells overwrite
# each other's history on exit
shopt -s histappend
HISTCONTROL=ignoreboth
HISTSIZE=32768
HISTFILESIZE="$HISTSIZE"
if [[ ! -v BASH_COMPLETION_VERSINFO && -f /usr/share/bash-completion/bash_completion ]]; then
source /usr/share/bash-completion/bash_completion
fi
source "$COREDESKTOP_SHELL/aliases"
source "$COREDESKTOP_SHELL/functions"
# Prompt. Uses CoreDesktop's starship config unless you have your own.
if [[ $TERM != "dumb" ]] && command -v starship &> /dev/null; then
[[ -f "$HOME/.config/starship.toml" ]] || export STARSHIP_CONFIG="$COREDESKTOP_SHELL/starship.toml"
eval "$(starship init bash)"
fi
# z <part of a path> jumps to a directory you've visited before
if command -v zoxide &> /dev/null; then
eval "$(zoxide init bash)"
fi
# Ctrl-R fuzzy history search, Ctrl-T to insert a file path, Alt-C to cd
if [[ -f /usr/share/fzf/key-bindings.bash ]]; then
source /usr/share/fzf/key-bindings.bash
fi
+78
View File
@@ -0,0 +1,78 @@
# CoreDesktop shell functions, sourced by shell/bashrc
# n opens Neovim in the current directory, or on the files given
n() { if [ "$#" -eq 0 ]; then command nvim . ; else command nvim "$@"; fi; }
# open a file or URL with its default app, detached from the terminal
open() (
xdg-open "$@" > /dev/null 2>&1 &
)
# compress <dir> makes <dir>.tar.gz (decompress is an alias for tar -xzf)
compress() { tar -czf "${1%/}.tar.gz" "${1%/}"; }
# SSH port forwarding
# fip <host> <port>... forward each localhost port to the same port on host
# dip <port>... stop forwarding those ports
# lip list active forwards
fip() {
(( $# < 2 )) && echo "Usage: fip <host> <port1> [port2] ..." && return 1
local host="$1"
shift
for port in "$@"; do
ssh -f -N -L "${port}:localhost:${port}" "$host" && echo "Forwarding localhost:$port -> $host:$port"
done
}
dip() {
(( $# == 0 )) && echo "Usage: dip <port1> [port2] ..." && return 1
for port in "$@"; do
pkill -f "ssh.*-L ${port}:localhost:${port}" && echo "Stopped forwarding port $port" || echo "No forwarding on port $port"
done
}
lip() {
pgrep -af "ssh.*-L [0-9]+:localhost:[0-9]+" || echo "No active forwards"
}
# Git worktrees
# ga <branch> create ../<repo>--<branch> on a new branch and cd into it
# gd from inside that worktree, remove it and its branch
ga() {
if [[ -z "$1" ]]; then
echo "Usage: ga [branch name]"
return 1
fi
local branch="$1"
local base="$(basename "$PWD")"
local wt_path="../${base}--${branch}"
git worktree add -b "$branch" "$wt_path" || return 1
command -v mise &> /dev/null && mise trust "$wt_path"
cd "$wt_path"
}
gd() {
local cwd base branch root worktree answer
cwd="$(pwd)"
worktree="$(basename "$cwd")"
# split on first `--`
root="${worktree%%--*}"
branch="${worktree#*--}"
# Protect against accidentally nuking a non-worktree directory
if [[ "$root" == "$worktree" ]]; then
echo "Not in a worktree made by ga (expected a <repo>--<branch> directory)."
return 1
fi
read -rp "Remove worktree $worktree and branch $branch? [y/N] " answer
[[ $answer == [yY]* ]] || return 0
cd "../$root"
git worktree remove "$cwd" --force || return 1
git branch -D "$branch"
}
+13
View File
@@ -0,0 +1,13 @@
#!/bin/bash
#
# Purpose : Hook CoreDesktop's shell setup (prompt, zoxide, fzf key bindings,
# aliases and functions in shell/) into ~/.bashrc
LINE='[[ -f "$HOME/.local/share/coredesktop/shell/bashrc" ]] && source "$HOME/.local/share/coredesktop/shell/bashrc"'
if grep -qF "$LINE" ~/.bashrc 2>/dev/null; then
echo "CoreDesktop shell setup already in .bashrc"
else
echo "$LINE" >> ~/.bashrc
echo "CoreDesktop shell setup added to .bashrc"
fi
+32
View File
@@ -0,0 +1,32 @@
add_newline = true
command_timeout = 200
format = "[$directory$git_branch$git_status]($style)$character"
[character]
error_symbol = "[✗](bold cyan)"
success_symbol = "[❯](bold cyan)"
[directory]
truncation_length = 2
truncation_symbol = "…/"
repo_root_style = "bold cyan"
repo_root_format = "[$repo_root]($repo_root_style)[$path]($style)[$read_only]($read_only_style) "
[git_branch]
format = "[$branch]($style) "
style = "italic cyan"
[git_status]
format = '[$all_status]($style)'
style = "cyan"
ahead = "⇡${count} "
diverged = "⇕⇡${ahead_count}⇣${behind_count} "
behind = "⇣${count} "
conflicted = " "
up_to_date = " "
untracked = "? "
modified = " "
stashed = ""
staged = ""
renamed = ""
deleted = ""
+2 -1
View File
@@ -60,8 +60,9 @@ Existing coredesktop hooks use a numeric prefix to control execution order:
| File | Purpose | | File | Purpose |
|---------------------------|-----------------------------------------| |---------------------------|-----------------------------------------|
| `00-rename-migration.sh` | Legacy command renames | | `09-snapshot.sh` | Snapper snapshot of / (btrfs only) |
| `10-pacman.sh` | Arch system update | | `10-pacman.sh` | Arch system update |
| `12-migrations.sh` | One-time migrations from `migrations/` |
| `15-services.sh` | Enable new systemd services | | `15-services.sh` | Enable new systemd services |
| `20-aur.sh` | AUR packages | | `20-aur.sh` | AUR packages |
| `30-flatpak.sh` | Flatpak apps | | `30-flatpak.sh` | Flatpak apps |
+65
View File
@@ -0,0 +1,65 @@
#!/bin/bash
#
# Purpose : System defaults adapted from Omarchy: SSH keepalives, a gentler
# login lockout, faster shutdown, out-of-memory protection, faster
# boot, and a PATH for commands run over SSH
set -e
# SSH client: notice a dropped connection within a minute instead of hanging
# until TCP gives up. Settings in ~/.ssh/config still win per host.
echo "Configuring SSH keepalives..."
sudo install -d -m 755 /etc/ssh/ssh_config.d
sudo tee /etc/ssh/ssh_config.d/20-coredesktop-keepalive.conf > /dev/null << 'EOF'
# CoreDesktop: notice dropped connections quickly instead of hanging until TCP
# times out. Settings in ~/.ssh/config take precedence over these defaults.
Host *
ServerAliveInterval 15
ServerAliveCountMax 3
ConnectTimeout 10
EOF
# Login lockout: Arch locks an account for 10 minutes after 3 wrong passwords.
# Allow 10 tries and unlock after 2 minutes.
echo "Relaxing the failed-login lockout..."
sudo sed -i -E \
-e 's/^#?[[:space:]]*deny[[:space:]]*=.*/deny = 10/' \
-e 's/^#?[[:space:]]*unlock_time[[:space:]]*=.*/unlock_time = 120/' \
/etc/security/faillock.conf
# Shutdown: don't wait 90 seconds on a service or app that won't stop
echo "Shortening the shutdown timeout..."
sudo install -d -m 755 /etc/systemd/system.conf.d /etc/systemd/user.conf.d
printf '[Manager]\nDefaultTimeoutStopSec=10s\n' \
| sudo tee /etc/systemd/system.conf.d/10-coredesktop-timeout.conf > /dev/null
printf '[Manager]\nDefaultTimeoutStopSec=10s\n' \
| sudo tee /etc/systemd/user.conf.d/10-coredesktop-timeout.conf > /dev/null
# Out of memory: let systemd-oomd kill the app causing memory pressure instead
# of the whole session freezing. GNOME launches apps into app.slice, so GNOME
# Shell itself is never a candidate.
echo "Enabling out-of-memory protection..."
sudo install -d -m 755 /etc/systemd/user/app.slice.d
sudo tee /etc/systemd/user/app.slice.d/10-coredesktop-oomd.conf > /dev/null << 'EOF'
[Slice]
ManagedOOMMemoryPressure=kill
ManagedOOMSwap=kill
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now systemd-oomd.service
# Boot: nothing on the desktop needs to wait for the network to come up
echo "Masking NetworkManager-wait-online..."
sudo systemctl mask NetworkManager-wait-online.service
# `ssh host command` runs without a login shell, so ~/.bashrc never sets PATH
# and tools in ~/.local/bin or installed with mise aren't found. PAM sets the
# environment for every login, including those. @{HOME} expands per user.
if ! grep -qE '^PATH[[:space:]]' /etc/security/pam_env.conf; then
echo "Adding user tool paths for SSH commands..."
sudo tee -a /etc/security/pam_env.conf > /dev/null << 'EOF'
# CoreDesktop: give SSH commands and other non-shell logins the user-level tool paths
PATH DEFAULT=/usr/local/sbin:/usr/local/bin:/usr/bin:@{HOME}/.local/share/mise/shims:@{HOME}/.local/bin
EOF
fi
+31
View File
@@ -0,0 +1,31 @@
#!/bin/bash
#
# Purpose : Turn on the ufw firewall. Incoming connections are blocked except
# for the services CoreDesktop runs, and ufw-docker stops ports
# published by Docker containers from getting around the firewall.
set -e
source "$HOME/.local/share/coredesktop/utils.sh"
install_packages ufw ufw-docker
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp comment 'SSH'
sudo ufw allow 5353/udp comment 'mDNS'
sudo ufw allow 53317 comment 'LocalSend'
sudo ufw allow in on tailscale0 comment 'Tailscale'
sudo ufw --force enable
sudo systemctl enable ufw.service
# ufw-docker only installs its rules while ufw is active, and they take effect
# once ufw restarts
if ! sudo grep -q 'BEGIN UFW AND DOCKER' /etc/ufw/after.rules; then
sudo ufw-docker install
sudo systemctl restart ufw.service
fi
sudo ufw status verbose
+67
View File
@@ -0,0 +1,67 @@
#!/bin/bash
#
# Purpose : Install NVIDIA's driver when the machine has an NVIDIA GPU and no
# NVIDIA driver yet (i.e. archinstall's proprietary option wasn't
# picked). Turing (GTX 16xx / RTX 20xx) and newer get the open kernel
# modules; Maxwell, Pascal and Volta get the 580xx legacy driver from
# the AUR. Older cards stay on nouveau.
#
# Run from install.sh only, never from an update: silently moving a
# working machine off nouveau is too risky to do mid-update.
set -e
source "$HOME/.local/share/coredesktop/utils.sh"
# Read device IDs from sysfs: lspci needs pciutils and wakes suspended GPUs.
# Turing is the first generation with GSP firmware (open modules) and the first
# with device IDs at 0x1e00 or above; Maxwell starts at 0x1340.
GPU=none
for device in /sys/bus/pci/devices/*; do
[[ $(< "$device/vendor") == 0x10de && $(< "$device/class") == 0x03* ]] || continue
id=$(( $(< "$device/device") ))
if (( id >= 0x1e00 )); then
GPU=open
elif (( id >= 0x1340 )) && [ "$GPU" = none ]; then
GPU=legacy
fi
done
if [ "$GPU" = none ]; then
echo "No NVIDIA GPU supported by the current drivers found."
exit 0
fi
if is_installed nvidia-utils || is_installed nvidia-580xx-utils; then
echo "NVIDIA driver already installed."
exit 0
fi
# DKMS builds the module for every installed kernel, so each needs its headers
PACKAGES=()
for kernel in linux linux-lts linux-zen linux-hardened; do
if is_installed "$kernel"; then
PACKAGES+=("$kernel-headers")
fi
done
if [ "$GPU" = open ]; then
PACKAGES+=(nvidia-open-dkms nvidia-utils libva-nvidia-driver)
else
PACKAGES+=(nvidia-580xx-dkms nvidia-580xx-utils)
fi
echo "Installing NVIDIA driver..."
install_packages "${PACKAGES[@]}"
# Load the driver from the initramfs so the GPU is ready when GDM starts
sudo install -d -m 755 /etc/mkinitcpio.conf.d
echo 'MODULES+=(nvidia nvidia_modeset nvidia_uvm nvidia_drm)' \
| sudo tee /etc/mkinitcpio.conf.d/nvidia.conf > /dev/null
sudo mkinitcpio -P
# GDM only offers Wayland on NVIDIA when these are enabled, since without them
# the GPU loses its memory contents across suspend
sudo systemctl enable nvidia-suspend.service nvidia-hibernate.service nvidia-resume.service
echo "NVIDIA driver installed. Reboot to start using it."
+54
View File
@@ -0,0 +1,54 @@
#!/bin/bash
#
# Purpose : Configure Snapper so update can snapshot / before each upgrade
# (updates.d/09-snapshot.sh). Only applies when / is btrfs, which is
# archinstall's default. /home is a separate subvolume and is not
# included in the snapshots.
if [ "$(findmnt -no FSTYPE /)" != "btrfs" ]; then
echo "/ is not btrfs; skipping Snapper setup."
exit 0
fi
set -e
source "$HOME/.local/share/coredesktop/utils.sh"
install_packages snapper
if [ ! -f /etc/snapper/configs/root ]; then
echo "Creating Snapper config for /..."
# create-config makes its own /.snapshots subvolume and fails if the path
# already exists. archinstall mounts a separate @.snapshots subvolume there,
# so follow the Arch wiki: unmount it, let snapper create its config, then
# swap snapper's nested subvolume back out for the @.snapshots mount.
if mountpoint -q /.snapshots; then
sudo umount /.snapshots
sudo rmdir /.snapshots
sudo snapper -c root create-config /
sudo btrfs subvolume delete /.snapshots
sudo mkdir /.snapshots
sudo mount /.snapshots
else
if [ -e /.snapshots ] && ! sudo rmdir /.snapshots 2>/dev/null; then
echo "/.snapshots exists and isn't an empty directory. Move it aside and run this again."
exit 1
fi
sudo snapper -c root create-config /
fi
# Keep only the last 5 pre-update snapshots, and no hourly timeline. Only set
# on a config we created, so a config you made yourself keeps its settings.
sudo snapper -c root set-config \
TIMELINE_CREATE=no \
NUMBER_CLEANUP=yes \
NUMBER_MIN_AGE=0 \
NUMBER_LIMIT=5 \
NUMBER_LIMIT_IMPORTANT=5
sudo systemctl disable --now snapper-timeline.timer 2> /dev/null || true
else
echo "Snapper config for / already exists."
fi
sudo systemctl enable --now snapper-cleanup.timer
+25
View File
@@ -0,0 +1,25 @@
#!/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).
set -e
source "$HOME/.local/share/coredesktop/utils.sh"
# Read vendor IDs from sysfs: lspci needs pciutils and wakes suspended GPUs
PACKAGES=()
for device in /sys/bus/pci/devices/*; do
[[ $(< "$device/class") == 0x03* ]] || continue
case "$(< "$device/vendor")" in
0x8086) PACKAGES+=(vulkan-intel) ;;
0x1002) PACKAGES+=(vulkan-radeon) ;;
esac
done
if (( ${#PACKAGES[@]} > 0 )); then
mapfile -t PACKAGES < <(printf '%s\n' "${PACKAGES[@]}" | sort -u)
install_packages "${PACKAGES[@]}"
else
echo "No Intel or AMD GPU found."
fi
+10
View File
@@ -48,6 +48,16 @@ if [[ -z "$UPDATE_REEXECED" ]]; then
exec ~/.local/bin/update exec ~/.local/bin/update
fi fi
# An upgrade that runs out of disk space part way through can leave the system
# half-upgraded. Set UPDATE_FORCE=1 to skip this check.
MIN_FREE_GIB=10
FREE_KIB="$(df -k --output=avail / | tail -n 1 | tr -d ' ')"
if [[ -z "$UPDATE_FORCE" && "$FREE_KIB" =~ ^[0-9]+$ ]] && (( FREE_KIB < MIN_FREE_GIB * 1024 * 1024 )); then
echo "Only $(( FREE_KIB / 1024 / 1024 )) GiB free on /; updating needs at least $MIN_FREE_GIB GiB."
echo "Free up space (sudo paccache -rk1 clears old cached packages), or run: UPDATE_FORCE=1 update"
exit 1
fi
# Ask for the password once, then keep sudo's cached credentials fresh until # Ask for the password once, then keep sudo's cached credentials fresh until
# update exits, so long-running hooks don't stop for a password again. # update exits, so long-running hooks don't stop for a password again.
sudo -v || exit 1 sudo -v || exit 1
+15
View File
@@ -0,0 +1,15 @@
#!/bin/bash
# Snapshot / before the system update so a bad upgrade can be rolled back.
# system/snapper.sh creates the "root" config on btrfs machines and keeps the
# last 5 snapshots.
is_installed() {
command -v snapper &>/dev/null && [ -f /etc/snapper/configs/root ]
}
run_update() {
echo "Snapshotting / before updating..."
sudo snapper -c root create -c number -d "Before CoreDesktop update" \
&& sudo snapper -c root cleanup number
}
+19 -1
View File
@@ -6,12 +6,28 @@ source "$COREDESKTOP/utils.sh"
source "$COREDESKTOP/packages.conf" source "$COREDESKTOP/packages.conf"
source "$COREDESKTOP/remove-packages.conf" source "$COREDESKTOP/remove-packages.conf"
STATUS=0
echo "Removing unwanted packages..." echo "Removing unwanted packages..."
remove_packages "${REMOVE_PACMAN[@]}" remove_packages "${REMOVE_PACMAN[@]}"
remove_flatpaks "${REMOVE_FLATPAKS[@]}" remove_flatpaks "${REMOVE_FLATPAKS[@]}"
# 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..."
sudo pacman -Sy --needed --noconfirm archlinux-keyring || STATUS=1
# script(1) keeps pacman's progress bars while logging its output for the
# initramfs check
echo "Updating Arch..." echo "Updating Arch..."
sudo pacman -Syu --noconfirm mkdir -p "$(dirname "$PACMAN_UPDATE_LOG")"
: > "$PACMAN_UPDATE_LOG"
sudo script -qefc "pacman -Su --noconfirm" "$PACMAN_UPDATE_LOG" || STATUS=1
if initramfs_build_failed; then
echo -e "\e[31mRebuilding the initramfs failed. Don't reboot until it's fixed - see the output above or $PACMAN_UPDATE_LOG.\e[0m"
STATUS=1
fi
echo "Checking for new packages..." echo "Checking for new packages..."
install_packages "${SYSTEM_UTILS[@]}" install_packages "${SYSTEM_UTILS[@]}"
@@ -20,3 +36,5 @@ install_packages "${MAINTENANCE[@]}"
install_packages "${DESKTOP[@]}" install_packages "${DESKTOP[@]}"
install_packages "${MEDIA[@]}" install_packages "${MEDIA[@]}"
install_packages "${FONTS[@]}" install_packages "${FONTS[@]}"
exit $STATUS
+15
View File
@@ -0,0 +1,15 @@
#!/bin/bash
# Run one-time changes from migrations/ that haven't run on this machine yet.
# This is how machines installed before a change to install.sh catch up, so a
# change that should reach existing machines needs a migration as well. To add
# one, create migrations/$(date +%s).sh; it runs with bash, once, on success.
#
# Runs after 10-pacman.sh so migrations install packages against fresh
# package databases.
COREDESKTOP="$HOME/.local/share/coredesktop"
source "$COREDESKTOP/utils.sh"
run_migrations
+9
View File
@@ -8,3 +8,12 @@ if command -v paccache &>/dev/null; then
sudo paccache -rk2 sudo paccache -rk2
sudo paccache -ruk0 sudo paccache -ruk0
fi fi
# Report packages that were installed as dependencies and are no longer needed.
# Not removed automatically: some may be tools you use directly.
ORPHANS="$(pacman -Qtdq 2>/dev/null)"
if [ -n "$ORPHANS" ]; then
echo "Packages no longer needed by anything:"
echo "$ORPHANS" | sed 's/^/ /'
echo "To remove them: sudo pacman -Rns \$(pacman -Qtdq)"
fi
+5 -1
View File
@@ -1,6 +1,10 @@
#!/bin/bash #!/bin/bash
source "$HOME/.local/share/coredesktop/utils.sh"
if initramfs_build_failed; then
echo -e "\e[31mDon't reboot yet: the initramfs failed to rebuild (see $PACMAN_UPDATE_LOG).\e[0m"
# If the running kernel's modules are gone, a newer kernel was installed # If the running kernel's modules are gone, a newer kernel was installed
if [ ! -d "/usr/lib/modules/$(uname -r)" ]; then elif [ ! -d "/usr/lib/modules/$(uname -r)" ]; then
echo "A newer kernel has been installed. Please reboot when convenient." echo "A newer kernel has been installed. Please reboot when convenient."
fi fi
+34
View File
@@ -73,6 +73,40 @@ enable_services() {
done done
} }
# Run each script in migrations/ that hasn't run on this machine yet, oldest
# first. A migration is recorded as done only when it succeeds, and the first
# failure stops the run so a later migration never runs ahead of an earlier one.
run_migrations() {
local dir="${COREDESKTOP:-$HOME/.local/share/coredesktop}/migrations"
local state="$HOME/.local/state/coredesktop/migrations"
local file name
mkdir -p "$state"
for file in "$dir"/*.sh; do
[ -f "$file" ] || continue
name="$(basename "$file")"
[ -f "$state/$name" ] && continue
echo "Running migration $name..."
if ! bash "$file"; then
echo "Migration $name failed; it will run again on the next update."
return 1
fi
touch "$state/$name"
done
}
# Output of the last update's pacman -Su, written by updates.d/10-pacman.sh
PACMAN_UPDATE_LOG="$HOME/.local/state/coredesktop/pacman-update.log"
# True if the last update rebuilt the initramfs and mkinitcpio reported a
# failure. Rebooting then can leave the machine unbootable.
initramfs_build_failed() {
[ -f "$PACMAN_UPDATE_LOG" ] || return 1
grep -q "Updating linux initcpios" "$PACMAN_UPDATE_LOG" || return 1
grep -q "==> ERROR" "$PACMAN_UPDATE_LOG" || ! grep -q "Image generation successful" "$PACMAN_UPDATE_LOG"
}
# Install a script atomically (copy to a temp file, then rename over the target) # 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 # so a copy of it that is running right now keeps reading the old file
install_script() { install_script() {