Unix3r

Build. Break. Learn. Repeat.

Starting0%
Skip to content

unix3r/dotfiles

Public
main
U Unix3r Initial commit: sway + waybar dotfiles (Catppuccin Mocha) eb290df
233 lines ยท 9.4 KB Raw History
1#!/usr/bin/env bash
2# โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
3# โ”‚ Dotfiles installer โ€” symlinks config/* into ~/.config โ”‚
4# โ”‚ Run ./install.sh --help for options. โ”‚
5# โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
6set -euo pipefail
7
8DOTFILES="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9CONFIG_SRC="$DOTFILES/config"
10CONFIG_DST="${XDG_CONFIG_HOME:-$HOME/.config}"
11BACKUP_ROOT="${XDG_STATE_HOME:-$HOME/.local/state}/dotfiles/backups"
12BACKUP_DIR="$BACKUP_ROOT/$(date +%Y%m%d-%H%M%S)"
13FONT_DIR="$HOME/.local/share/fonts/JetBrainsMonoNerd"
14FONT_URL="https://github.com/ryanoasis/nerd-fonts/releases/latest/download/JetBrainsMono.tar.xz"
15
16DRY=0 YES=0
17DO_PKGS=0 DO_FONTS=0 DO_LINK=0 DO_CHECK=0 DO_UNINSTALL=0
18MODULES=()
19
20# โ”€โ”€ pretty output โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
21if [[ -t 1 ]]; then
22 B=$'\e[1m' D=$'\e[2m' R=$'\e[31m' G=$'\e[32m' Y=$'\e[33m' M=$'\e[35m' C=$'\e[36m' N=$'\e[0m'
23else
24 B= D= R= G= Y= M= C= N=
25fi
26info() { printf '%s::%s %s\n' "$C$B" "$N" "$*"; }
27ok() { printf ' %sโœ“%s %s\n' "$G" "$N" "$*"; }
28warn() { printf ' %s!%s %s\n' "$Y" "$N" "$*"; }
29err() { printf ' %sโœ—%s %s\n' "$R" "$N" "$*" >&2; }
30run() { if (( DRY )); then printf ' %s[dry-run]%s %s\n' "$D" "$N" "$*"; else "$@"; fi; }
31has() { command -v "$1" >/dev/null 2>&1; }
32ask() { (( YES )) && return 0; read -rp " $1 [y/N] " a; [[ $a == [yY]* ]]; }
33
34banner() {
35 printf '%s' "$M$B"
36 cat <<'ART'
37 __ __ ____ __
38 ____/ /___ / /_/ __(_) /__ _____
39 / __ / __ \/ __/ /_/ / / _ \/ ___/
40/ /_/ / /_/ / /_/ __/ / / __(__ )
41\__,_/\____/\__/_/ /_/_/\___/____/ sway ยท waybar ยท catppuccin
42ART
43 printf '%s\n' "$N"
44}
45
46usage() {
47 cat <<USAGE
48${B}Usage:${N} ./install.sh [options] [module ...]
49
50${B}Options${N}
51 -a, --all Full setup: packages + fonts + symlinks
52 -p, --packages Install dependencies (apt, pacman or dnf)
53 -f, --fonts Download JetBrainsMono Nerd Font into ~/.local/share/fonts
54 -l, --link Symlink configs into ~/.config (default when no option is given)
55 -c, --check Show which required programs are missing
56 -u, --uninstall Remove the symlinks and restore your latest backup
57 -n, --dry-run Print what would happen, change nothing
58 -y, --yes Don't ask for confirmation
59 -h, --help Show this help
60
61${B}Modules${N} (default: all)
62 $(available_modules | tr '\n' ' ')
63
64${B}Examples${N}
65 ./install.sh --all # fresh machine
66 ./install.sh # just link the configs
67 ./install.sh waybar mako # only link these two
68 ./install.sh -n -a # preview everything
69USAGE
70}
71
72available_modules() { find "$CONFIG_SRC" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | sort; }
73
74# โ”€โ”€ package manager detection โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
75detect_pm() {
76 if has apt-get; then echo apt
77 elif has pacman; then echo pacman
78 elif has dnf; then echo dnf
79 else echo unknown; fi
80}
81
82install_packages() {
83 local pm list pkgs=()
84 pm=$(detect_pm)
85 list="$DOTFILES/packages/$pm.txt"
86 info "Installing packages with ${B}$pm${N}"
87 [[ -f $list ]] || { err "No package list for '$pm'. Install the programs from packages/apt.txt by hand."; return 1; }
88 mapfile -t pkgs < <(grep -vE '^\s*(#|$)' "$list")
89
90 # Skip names the repos don't know about instead of failing the whole run
91 local available=() missing=()
92 for p in "${pkgs[@]}"; do
93 case $pm in
94 apt) apt-cache show "$p" >/dev/null 2>&1 && available+=("$p") || missing+=("$p") ;;
95 pacman) pacman -Si "$p" >/dev/null 2>&1 && available+=("$p") || missing+=("$p") ;;
96 dnf) available+=("$p") ;; # dnf --skip-unavailable handles it
97 esac
98 done
99 (( ${#missing[@]} )) && warn "Not in your repos, skipped: ${missing[*]}"
100 printf ' %s%s%s\n' "$D" "${available[*]}" "$N" | fold -s -w 100
101
102 ask "Install ${#available[@]} packages?" || { warn "Skipped packages"; return 0; }
103 case $pm in
104 apt) run sudo apt-get update && run sudo apt-get install -y "${available[@]}" ;;
105 pacman) run sudo pacman -S --needed --noconfirm "${available[@]}" ;;
106 dnf) run sudo dnf install -y --skip-unavailable "${available[@]}" ;;
107 esac
108 ok "Packages installed"
109}
110
111install_fonts() {
112 info "Installing JetBrainsMono Nerd Font"
113 if fc-list 2>/dev/null | grep -qi "JetBrainsMono Nerd"; then
114 ok "Already installed"; return 0
115 fi
116 has curl || { err "curl is required"; return 1; }
117 run mkdir -p "$FONT_DIR"
118 if (( DRY )); then
119 run curl -fL "$FONT_URL" "| tar -xJ -C $FONT_DIR"
120 else
121 curl -fL --progress-bar "$FONT_URL" | tar -xJ -C "$FONT_DIR"
122 fi
123 run fc-cache -f "$FONT_DIR" >/dev/null
124 ok "Font installed to ${FONT_DIR/#$HOME/\~}"
125}
126
127# โ”€โ”€ linking โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
128link_module() {
129 local name=$1 src="$CONFIG_SRC/$1" dst="$CONFIG_DST/$1"
130 [[ -d $src ]] || { err "Unknown module: $name"; return 1; }
131
132 if [[ -L $dst && $(readlink -f "$dst") == "$src" ]]; then
133 ok "$name ${D}(already linked)${N}"; return 0
134 fi
135 if [[ -e $dst || -L $dst ]]; then
136 run mkdir -p "$BACKUP_DIR"
137 run mv "$dst" "$BACKUP_DIR/$name"
138 warn "$name: old config moved to ${BACKUP_DIR/#$HOME/\~}/$name"
139 fi
140 run ln -s "$src" "$dst"
141 ok "$name โ†’ ${dst/#$HOME/\~}"
142}
143
144link_all() {
145 info "Linking configs into ${CONFIG_DST/#$HOME/\~}"
146 run mkdir -p "$CONFIG_DST"
147 for m in "${MODULES[@]}"; do link_module "$m"; done
148 run chmod +x "$CONFIG_SRC"/sway/scripts/*.sh "$CONFIG_SRC"/waybar/scripts/*.sh
149
150 info "Creating folders"
151 local pics
152 pics=$(xdg-user-dir PICTURES 2>/dev/null || echo "$HOME/Pictures")
153 run mkdir -p "$pics/Screenshots" "$pics/Wallpapers"
154 ok "${pics/#$HOME/\~}/Screenshots, ${pics/#$HOME/\~}/Wallpapers"
155
156 if [[ -n ${SWAYSOCK:-} ]] && has swaymsg; then
157 info "Reloading sway"
158 run swaymsg reload >/dev/null && ok "Reloaded"
159 fi
160}
161
162uninstall() {
163 info "Removing symlinks"
164 for m in "${MODULES[@]}"; do
165 local dst="$CONFIG_DST/$m"
166 if [[ -L $dst && $(readlink -f "$dst") == "$CONFIG_SRC/$m" ]]; then
167 run rm "$dst"; ok "removed $m"
168 local latest
169 latest=$(find "$BACKUP_ROOT" -mindepth 2 -maxdepth 2 -name "$m" 2>/dev/null | sort | tail -n1)
170 if [[ -n $latest ]]; then
171 run mv "$latest" "$dst"; ok "restored $m from ${latest/#$HOME/\~}"
172 fi
173 fi
174 done
175}
176
177# โ”€โ”€ dependency check โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
178check() {
179 info "Checking programs"
180 local req=(sway swaybg swayidle swaylock waybar wofi foot mako makoctl grim slurp
181 wl-copy wl-paste jq notify-send brightnessctl playerctl wpctl curl)
182 local opt=(cliphist swappy wtype magick gammastep wlogout kitty nm-applet
183 blueman-applet pavucontrol btop lxpolkit)
184 local miss=0
185 for c in "${req[@]}"; do
186 if has "$c"; then ok "$c"; else err "$c ${D}(required)${N}"; miss=1; fi
187 done
188 for c in "${opt[@]}"; do
189 if has "$c"; then ok "$c"; else warn "$c ${D}(optional)${N}"; fi
190 done
191 if fc-list 2>/dev/null | grep -qi "JetBrainsMono Nerd"; then ok "JetBrainsMono Nerd Font"
192 else warn "JetBrainsMono Nerd Font ${D}(run ./install.sh --fonts)${N}"; fi
193 (( miss )) && warn "Run ${B}./install.sh --packages${N} to install what's missing."
194 return 0
195}
196
197# โ”€โ”€ args โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
198while (( $# )); do
199 case $1 in
200 -a|--all) DO_PKGS=1 DO_FONTS=1 DO_LINK=1 ;;
201 -p|--packages) DO_PKGS=1 ;;
202 -f|--fonts) DO_FONTS=1 ;;
203 -l|--link) DO_LINK=1 ;;
204 -c|--check) DO_CHECK=1 ;;
205 -u|--uninstall) DO_UNINSTALL=1 ;;
206 -n|--dry-run) DRY=1 ;;
207 -y|--yes) YES=1 ;;
208 -h|--help) banner; usage; exit 0 ;;
209 -*) err "Unknown option: $1"; usage; exit 1 ;;
210 *) MODULES+=("$1") ;;
211 esac
212 shift
213done
214
215(( DO_PKGS + DO_FONTS + DO_LINK + DO_CHECK + DO_UNINSTALL )) || DO_LINK=1
216(( ${#MODULES[@]} )) || mapfile -t MODULES < <(available_modules)
217
218banner
219(( DRY )) && warn "Dry run โ€” nothing will be changed"
220(( EUID == 0 )) && { err "Don't run this as root; it will ask for sudo when needed."; exit 1; }
221
222if (( DO_UNINSTALL )); then uninstall; exit 0; fi
223(( DO_PKGS )) && install_packages
224(( DO_FONTS )) && install_fonts
225(( DO_LINK )) && link_all
226(( DO_CHECK )) && check
227
228echo
229info "${G}Done!${N}"
230if (( DO_LINK )); then
231 info "Log out and pick ${B}Sway${N} at the login screen (or type ${B}sway${N} in a TTY)."
232 info "Press ${B}Super + /${N} inside sway for the keybinding cheat sheet."
233fi