diff --git a/.github/workflows/pr-test.yml b/.github/workflows/pr-test.yml index 0a02d0e..a1a49a7 100644 --- a/.github/workflows/pr-test.yml +++ b/.github/workflows/pr-test.yml @@ -51,9 +51,9 @@ jobs: - uses: actions/checkout@v4 - name: Full install - run: make + run: make basic - # Verify the install actually landed, not just that `make` exited 0. + # Verify the install actually landed, not just that `make basic` exited 0. - name: Verify install run: | test -L ~/.config/nvim/init.lua diff --git a/Makefile b/Makefile index 86f3a0b..bcbae4a 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,18 @@ +# A bare `make` asks which config sets this machine should use, then installs +# with that selection. Use `make basic` for the non-interactive equivalent. +.DEFAULT_GOAL := picky + +picky: + @bash ./install-scripts/pick-sets.sh + @$(MAKE) --no-print-directory full-install + +# Enable just the "basic" set and install, skipping the picker. This is what +# CI runs, and the right target for any unattended install. +basic: + @bash ./install-scripts/pick-sets.sh basic + @$(MAKE) --no-print-directory full-install + +# Install using whatever sets are already enabled, without asking. full-install: @bash ./install-scripts/01-install-packages.sh @bash ./install-scripts/02-move-files.sh @@ -12,15 +27,4 @@ refresh: @bash ./install-scripts/02-move-files.sh @exec fish -picky: - @bash ./install-scripts/pick-sets.sh - @bash ./install-scripts/01-install-packages.sh - @bash ./install-scripts/02-move-files.sh - @fish ./install-scripts/03-fisher-install.fish - @fish ./install-scripts/04-fish-plugins.fish - @fish ./install-scripts/05-tmux-plugins.fish - @fish ./install-scripts/06-vim-setup.fish - @bash ./install-scripts/07-last-touches.sh - @exec fish - -.PHONY: full-install refresh picky +.PHONY: picky basic full-install refresh diff --git a/README.md b/README.md index fd5cda0..c3a3243 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ A comprehensive dotfiles setup for a modern development environment featuring Ne - **Fish Shell**: Lightweight `simple.fish` prompt, cross-machine history sync, aliases, and productivity functions - **Tmux**: Custom keybindings, mouse support, and a modular status bar with toggleable gadgets - **Cross-platform**: Supports macOS, Linux (Arch, Debian/Ubuntu, Alpine, Fedora/RHEL, Gentoo), and FreeBSD -- **Config Sets**: Pick and choose which groups of config get deployed via an interactive `make picky` dialog +- **Config Sets**: Pick and choose which groups of config get deployed via the interactive checklist a bare `make` opens - **Automated Setup**: One-command installation via Makefile ## Quick Install @@ -17,7 +17,16 @@ A comprehensive dotfiles setup for a modern development environment featuring Ne make ``` -The setup will automatically detect your operating system and install all necessary packages and configurations. +This opens an interactive checklist of the available config sets, then runs +the full install using your selection. The setup automatically detects your +operating system and installs all necessary packages and configurations. + +To install just the `basic` set without being asked anything — the right +choice for an unattended install, and what CI runs: + +```bash +make basic +``` To re-sync just the config file symlinks (without reinstalling packages or plugins): @@ -25,10 +34,11 @@ To re-sync just the config file symlinks (without reinstalling packages or plugi make refresh ``` -To interactively choose which config sets get deployed, then run the full install using that selection: +To re-run the full install using the sets you already picked, without the +checklist: ```bash -make picky +make full-install ``` ## Repository Layout @@ -53,7 +63,7 @@ config/ manifest os # "Linux" — hidden from `make picky` elsewhere apply.sh - macos/ # macOS keyboard repeat settings + macos/ # macOS system settings (`defaults write`) description manifest os # "Darwin" — hidden from `make picky` elsewhere @@ -61,7 +71,7 @@ config/ # further sets live alongside these, following the same layout install-scripts/ # Numbered setup scripts run by the Makefile lib/sets.sh # Shared helpers for discovering/enabling sets - pick-sets.sh # `make picky` — the dialog checklist + pick-sets.sh # the dialog checklist; takes set names to skip it scripts/ # Misc helper scripts (e.g. Gentoo kernel upgrade) tests/ # Dockerfiles used by CI to test installs per distro ``` @@ -73,8 +83,17 @@ Config files are grouped into **sets** under `config/sets//`: - **`basic`** — everything this repo has always deployed (Neovim, Fish, Tmux) - **`kde`** — sets the KDE Plasma keyboard repeat rate to 50/s with a 250ms delay (via `kwriteconfig5`/`6` on `kcminputrc`) -- **`macos`** — sets the macOS keyboard repeat rate as fast as possible with - the shortest delay (via `defaults write`) +- **`macos`** — the macOS system settings that differ from stock, applied via + `defaults write`: fastest keyboard repeat rate with the shortest delay, all + automatic text substitution off, text replacements, the input-source and + Quick Note hotkeys disabled, dark mode, a faster trackpad, seconds in the + menu bar clock, no Dock recents, the built-in drag-to-edge window tiling + off (Rectangle handles that), Finder in list view opening on `~` with a + status bar, screenshots to the clipboard, personalised ads off, and — if + passwordless `sudo` is available — a display that never sleeps. It also + installs **Rectangle** and **Maccy** via Homebrew Cask and applies their + shortcuts (Cmd+Shift+arrows for window halves, Cmd+Shift+V for clipboard + history) Each set is independent and additive — enabling `kde` or `macos` doesn't disturb `basic`. `kde` and `macos` each declare an `os` file restricting @@ -83,16 +102,23 @@ them to their platform, so `make picky` only offers `kde` on Linux and wrong OS anyway (e.g. a shared `sets.conf`), it's skipped with a warning at deploy time instead of failing. -Run `make picky` to get an interactive checklist (space to toggle, enter to -confirm) of every set found under `config/sets/`. Confirming saves your -selection to `~/.config/dotfiles/sets.conf` and immediately kicks off the -full install (packages, plugins, config deployment, last touches) using it -— the same steps `make full-install` runs. Cancelling (Esc) leaves your -existing selection untouched and stops there, without installing anything. +A bare `make` (or `make picky`, the same target by name) gives you an +interactive checklist (space to toggle, enter to confirm) of every set found +under `config/sets/`. Confirming saves your selection to +`~/.config/dotfiles/sets.conf` and immediately kicks off the full install +(packages, plugins, config deployment, last touches) using it — the same +steps `make full-install` runs. Cancelling (Esc) leaves your existing +selection untouched and stops there, without installing anything. + +`make basic` is the non-interactive equivalent: it enables just the `basic` +set and installs, skipping the checklist entirely (and skipping the `dialog` +dependency the checklist would otherwise pull in). CI uses it, and so should +any unattended install. + The saved selection is remembered by future `make refresh` / `make -full-install` runs too, so you only need to pick once per machine. If -you've never run `make picky`, everything defaults to just `basic`, -matching this repo's historical behavior. +full-install` runs too, so you only need to pick once per machine. If you've +never picked, everything defaults to just `basic`, matching this repo's +historical behavior. ### Adding a new set diff --git a/config/sets/macos/apply.sh b/config/sets/macos/apply.sh index 844e00a..cd4ba55 100755 --- a/config/sets/macos/apply.sh +++ b/config/sets/macos/apply.sh @@ -1,15 +1,233 @@ #!/bin/bash set -euo pipefail -# Sets the macOS keyboard repeat rate as fast as possible and the initial -# repeat delay as short as possible — below the range the System Settings -# slider exposes. Log out/in (or restart affected apps) for it to fully -# take effect everywhere. +# macOS settings that diverge from stock defaults on this machine. +# Each section below is independent and safe to re-run. +# +# Some of these only take effect in apps launched afterwards; the Dock, +# Finder and SystemUIServer are restarted at the end to pick up the rest. if [ "$(uname)" != "Darwin" ] || ! command -v defaults >/dev/null 2>&1; then - echo "Warning: not macOS (or 'defaults' unavailable); skipping keyboard repeat settings." >&2 + echo "Warning: not macOS (or 'defaults' unavailable); skipping macOS settings." >&2 exit 0 fi +# --- Keyboard ------------------------------------------------------------- + +# Keyboard repeat rate as fast as possible and the initial repeat delay as +# short as possible — below the range the System Settings slider exposes. defaults write NSGlobalDomain KeyRepeat -int 1 defaults write NSGlobalDomain InitialKeyRepeat -int 10 + +# Turn off every "helpful" text substitution (System Settings > Keyboard > +# Input Sources > Edit). They fight with writing code and shell commands. +defaults write NSGlobalDomain NSAutomaticCapitalizationEnabled -bool false +defaults write NSGlobalDomain NSAutomaticDashSubstitutionEnabled -bool false +defaults write NSGlobalDomain NSAutomaticInlinePredictionEnabled -bool false +defaults write NSGlobalDomain NSAutomaticPeriodSubstitutionEnabled -bool false +defaults write NSGlobalDomain NSAutomaticQuoteSubstitutionEnabled -bool false +defaults write NSGlobalDomain NSAutomaticSpellingCorrectionEnabled -bool false +defaults write NSGlobalDomain WebAutomaticSpellingCorrectionEnabled -bool false + +# Text replacements (System Settings > Keyboard > Text Replacements). +# Note this replaces the whole list, including macOS's stock "omw" entry — +# which is why it's repeated here. +# +# These use XML plist literals rather than `defaults`' old-style `{ k = v; }` +# syntax, which types every scalar as a string — macOS wants a real integer +# in `on` and a real boolean in the hotkey dicts below. +defaults write NSGlobalDomain NSUserDictionaryReplacementItems ' + on1replaceomwwithOn my way! + on1replace/sternwithಠ_ಠ + on1replace/shrugwith¯\_(ツ)_/¯ +' + +# Free up Ctrl+Space / Ctrl+Opt+Space by disabling the "select previous / +# next input source" shortcuts (System Settings > Keyboard > Keyboard +# Shortcuts > Input Sources), and drop the Quick Note hotkey while we're +# here (Keyboard Shortcuts > Mission Control). +disable_hotkey() { + local id="$1" p1="$2" p2="$3" p3="$4" + defaults write com.apple.symbolichotkeys AppleSymbolicHotKeys -dict-add "$id" \ + " + enabled + value + parameters + $p1$p2$p3 + + typestandard + + " +} +disable_hotkey 60 32 49 262144 # select the previous input source +disable_hotkey 61 32 49 786432 # select the next input source +disable_hotkey 164 65535 65535 0 # show Quick Note + +# --- Appearance ----------------------------------------------------------- + +# Dark mode (System Settings > Appearance). +defaults write NSGlobalDomain AppleInterfaceStyle -string Dark + +# Trackpad tracking speed, faster than the 0.6875 default +# (System Settings > Trackpad > Point & Click). +defaults write NSGlobalDomain com.apple.trackpad.scaling -float 1 + +# Show seconds in the menu bar clock (System Settings > Control Centre > +# Menu Bar Only > Clock Options). +defaults write com.apple.menuextra.clock ShowSeconds -bool true + +# --- Dock ----------------------------------------------------------------- + +# No "Recent Applications" section in the Dock (System Settings > Desktop & +# Dock). +defaults write com.apple.dock show-recents -bool false + +# --- Window tiling -------------------------------------------------------- + +# Disable macOS's own drag-to-edge window tiling and its margins — window +# management is Rectangle's job (System Settings > Desktop & Dock > Windows). +defaults write com.apple.WindowManager EnableTiledWindowMargins -bool false +defaults write com.apple.WindowManager EnableTilingByEdgeDrag -bool false +defaults write com.apple.WindowManager EnableTopTilingByEdgeDrag -bool false +defaults write com.apple.WindowManager EnableTilingOptionAccelerator -bool false + +# --- Finder --------------------------------------------------------------- + +# List view by default, new windows open on ~, and keep the status bar. +defaults write com.apple.finder FXPreferredViewStyle -string Nlsv +defaults write com.apple.finder NewWindowTarget -string PfHm +defaults write com.apple.finder ShowStatusBar -bool true + +# --- Screenshots ---------------------------------------------------------- + +# Screenshots go to the clipboard rather than the Desktop. `target` is the +# older key, `target-screenshot` the one Sonoma and later actually read. +defaults write com.apple.screencapture target -string clipboard +defaults write com.apple.screencapture target-screenshot -string clipboard + +# --- Privacy -------------------------------------------------------------- + +# Opt out of Apple's personalised advertising (System Settings > Privacy & +# Security > Apple Advertising). +defaults write com.apple.AdLib allowApplePersonalizedAdvertising -bool false + +# --- Apps ----------------------------------------------------------------- + +# Rectangle (window management) and Maccy (clipboard history), plus the +# shortcuts and options they're configured with here. +# +# Both apps write their whole preference domain back out when they quit, so +# a `defaults write` against a running app gets silently clobbered. Each one +# is therefore quit first, configured, and only relaunched if it was already +# running. + +install_cask() { + local cask="$1" + if ! command -v brew >/dev/null 2>&1; then + echo "Warning: Homebrew not found; skipping '$cask'." >&2 + return 1 + fi + brew list --cask "$cask" >/dev/null 2>&1 || brew install --cask "$cask" +} + +app_is_running() { + pgrep -qx "$1" 2>/dev/null +} + +quit_app() { + app_is_running "$1" || return 0 + osascript -e "quit app \"$1\"" >/dev/null 2>&1 || killall "$1" >/dev/null 2>&1 || true + # Give the app a moment to flush its preferences before we overwrite them. + for _ in 1 2 3 4 5 6 7 8 9 10; do + app_is_running "$1" || break + sleep 0.5 + done +} + +# Rectangle shortcuts are stored as { keyCode, modifierFlags } dictionaries; +# XML plist literals keep both as real integers. +rectangle_shortcut() { + defaults write com.knollsoft.Rectangle "$1" \ + " + keyCode$2 + modifierFlags$3 + " +} + +if install_cask rectangle; then + rectangle_was_running=false + if app_is_running Rectangle; then + rectangle_was_running=true + fi + quit_app Rectangle + + # 1179648 = Command + Shift, 786432 = Control + Option. + rectangle_shortcut leftHalf 123 1179648 # Cmd+Shift+Left + rectangle_shortcut rightHalf 124 1179648 # Cmd+Shift+Right + rectangle_shortcut maximize 126 1179648 # Cmd+Shift+Up + rectangle_shortcut center 125 1179648 # Cmd+Shift+Down + rectangle_shortcut toggleTodo 11 786432 # Ctrl+Opt+B + rectangle_shortcut reflowTodo 45 786432 # Ctrl+Opt+N + + # Accept shortcuts macOS would otherwise reserve, keep the menu bar + # clear, and don't nag about updates (the cask handles those). + defaults write com.knollsoft.Rectangle allowAnyShortcut -bool true + defaults write com.knollsoft.Rectangle hideMenubarIcon -bool true + defaults write com.knollsoft.Rectangle subsequentExecutionMode -int 0 + defaults write com.knollsoft.Rectangle windowSnapping -int 1 + defaults write com.knollsoft.Rectangle SUEnableAutomaticChecks -bool false + + if [ "$rectangle_was_running" = true ]; then + open -a Rectangle || true + else + echo "Note: Rectangle needs Accessibility permission on first launch" >&2 + echo " (System Settings > Privacy & Security > Accessibility)." >&2 + fi +fi + +if install_cask maccy; then + maccy_was_running=false + if app_is_running Maccy; then + maccy_was_running=true + fi + quit_app Maccy + + # Maccy stores shortcuts as JSON strings of Carbon key/modifier codes. + # 768 = cmdKey + shiftKey, 2048 = optionKey. + defaults write org.p0deje.Maccy KeyboardShortcuts_popup \ + -string '{"carbonKeyCode":9,"carbonModifiers":768}' # Cmd+Shift+V + defaults write org.p0deje.Maccy KeyboardShortcuts_pin \ + -string '{"carbonKeyCode":35,"carbonModifiers":2048}' # Opt+P + defaults write org.p0deje.Maccy KeyboardShortcuts_delete \ + -string '{"carbonKeyCode":51,"carbonModifiers":2048}' # Opt+Delete + + # Open the history under the menu bar item rather than at the pointer. + defaults write org.p0deje.Maccy popupPosition -string statusItem + defaults write org.p0deje.Maccy windowSize -string '[450,800]' + defaults write org.p0deje.Maccy SUEnableAutomaticChecks -bool false + + if [ "$maccy_was_running" = true ]; then + open -a Maccy || true + fi +fi + +# --- Power ---------------------------------------------------------------- + +# Never blank the display when idle, on battery or mains — same choice as +# the kde set makes. Needs root, so it's skipped rather than prompting when +# passwordless sudo isn't available. +if command -v pmset >/dev/null 2>&1 && sudo -n true 2>/dev/null; then + sudo -n pmset -b displaysleep 0 + sudo -n pmset -c displaysleep 0 +else + echo "Note: skipping 'pmset displaysleep 0' (needs sudo). Run manually:" >&2 + echo " sudo pmset -b displaysleep 0 && sudo pmset -c displaysleep 0" >&2 +fi + +# --- Apply ---------------------------------------------------------------- + +# Restart the affected agents so the settings above show up now instead of +# at next login. Finder windows are reopened by Finder itself. +for app in Dock Finder SystemUIServer; do + killall "$app" >/dev/null 2>&1 || true +done diff --git a/config/sets/macos/description b/config/sets/macos/description index ebac978..8e0de4c 100644 --- a/config/sets/macos/description +++ b/config/sets/macos/description @@ -1 +1,6 @@ -macOS: fastest keyboard repeat rate, shortest delay +macOS: fastest keyboard repeat rate + shortest delay, text substitutions +off, text replacements, input-source and Quick Note hotkeys disabled, dark +mode, faster trackpad, seconds in the menu bar clock, no Dock recents, +built-in window tiling off, Finder in list view opening on ~ with a status +bar, screenshots to the clipboard, personalised ads off, display never +sleeps, plus Rectangle and Maccy installed and configured diff --git a/install-scripts/lib/sets.sh b/install-scripts/lib/sets.sh index 9a9bfda..04afa27 100644 --- a/install-scripts/lib/sets.sh +++ b/install-scripts/lib/sets.sh @@ -46,6 +46,11 @@ enabled_sets() { sets=("basic") fi + # Expanding an empty array trips `set -u` on bash 3.2 (which is what + # macOS ships), and an empty sets.conf -- every set deselected -- is a + # legitimate state. + [ "${#sets[@]}" -eq 0 ] && return 0 + for set in "${sets[@]}"; do if [ ! -d "$SETS_DIR/$set" ]; then echo "Warning: enabled set '$set' no longer exists in the repo, skipping." >&2 diff --git a/install-scripts/pick-sets.sh b/install-scripts/pick-sets.sh index 7dd3884..67a427d 100755 --- a/install-scripts/pick-sets.sh +++ b/install-scripts/pick-sets.sh @@ -1,19 +1,47 @@ #!/bin/bash set -euo pipefail -# `make picky`: an interactive dialog checklist for choosing which config -# sets (config/sets//) get deployed to this machine. The selection is -# remembered in $SETS_STATE_FILE so future `make refresh` / `make -# full-install` runs keep using it without asking again — re-run `make -# picky` any time to change your mind. +# Chooses which config sets (config/sets//) get deployed to this +# machine, and remembers the choice in $SETS_STATE_FILE so future `make +# refresh` / `make full-install` runs keep using it without asking again. # -# On confirm, the Makefile's `picky` target runs the full install right -# after this script exits successfully. Exiting non-zero (e.g. on cancel) -# stops `make` before that happens. +# With no arguments (a bare `make`, or `make picky`) it shows an interactive +# dialog checklist. With set names as arguments it enables exactly those, +# non-interactively — that's how `make basic` skips the dialog. +# +# Either way the Makefile runs the full install right after this script +# exits successfully. Exiting non-zero (e.g. on cancel) stops `make` before +# that happens. REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" # shellcheck source=install-scripts/lib/sets.sh source "$REPO/install-scripts/lib/sets.sh" +# Persist the given set names as this machine's selection. +save_sets() { + mkdir -p "$(dirname "$SETS_STATE_FILE")" + printf '%s\n' "$@" | sed '/^$/d' > "$SETS_STATE_FILE" + + if [ -s "$SETS_STATE_FILE" ]; then + echo "Enabled sets: $(paste -sd, "$SETS_STATE_FILE")" + else + echo "Enabled sets: (none)" + fi +} + +# Non-interactive path: enable exactly the named sets and skip the dialog +# (and skip installing `dialog` in the first place). +if [ "$#" -gt 0 ]; then + for set in "$@"; do + if [ ! -d "$SETS_DIR/$set" ]; then + echo "No config set named '$set' under config/sets/." >&2 + echo "Available: $(list_available_sets | paste -sd, -)" >&2 + exit 1 + fi + done + save_sets "$@" + exit 0 +fi + ensure_dialog() { command -v dialog >/dev/null 2>&1 && return @@ -40,19 +68,32 @@ ensure_dialog() { ensure_dialog -mapfile -t available < <(list_pickable_sets) +# Collected with read loops rather than `mapfile -t`: macOS still ships bash +# 3.2, which predates that builtin. +available=() +while IFS= read -r line; do + available+=("$line") +done < <(list_pickable_sets) + if [ "${#available[@]}" -eq 0 ]; then echo "No config sets found under config/sets/ that apply to this OS." >&2 exit 1 fi -mapfile -t currently_enabled < <(enabled_sets) +currently_enabled=() +while IFS= read -r line; do + currently_enabled+=("$line") +done < <(enabled_sets) is_enabled() { - local set="$1" - for enabled in "${currently_enabled[@]}"; do - [ "$set" == "$enabled" ] && return 0 - done + local set="$1" enabled + # Expanding an empty array trips `set -u` on bash 3.2, so check first — + # deselecting every set is a legitimate state. + if [ "${#currently_enabled[@]}" -gt 0 ]; then + for enabled in "${currently_enabled[@]}"; do + [ "$set" == "$enabled" ] && return 0 + done + fi return 1 } @@ -84,11 +125,4 @@ if [ "$status" -ne 0 ]; then exit 1 fi -mkdir -p "$(dirname "$SETS_STATE_FILE")" -printf '%s\n' "$selection" | sed '/^$/d' > "$SETS_STATE_FILE" - -if [ -s "$SETS_STATE_FILE" ]; then - echo "Enabled sets: $(paste -sd, "$SETS_STATE_FILE")" -else - echo "Enabled sets: (none)" -fi +save_sets "$selection" diff --git a/tests/Dockerfile-alpine b/tests/Dockerfile-alpine index f9b3960..c62239b 100644 --- a/tests/Dockerfile-alpine +++ b/tests/Dockerfile-alpine @@ -6,9 +6,9 @@ WORKDIR /workspace RUN apk add make shadow bash sudo -RUN make +RUN make basic -# Verify the install actually landed, not just that `make` exited 0. +# Verify the install actually landed, not just that `make basic` exited 0. RUN test -L /root/.config/nvim/init.lua \ && test -L /root/.config/fish/conf.d/aliases.fish \ && fish -c 'functions -q gitissue' \ diff --git a/tests/Dockerfile-archlinux b/tests/Dockerfile-archlinux index d8bff61..252383a 100644 --- a/tests/Dockerfile-archlinux +++ b/tests/Dockerfile-archlinux @@ -7,9 +7,9 @@ WORKDIR /workspace RUN pacman -Syy RUN pacman -S --noconfirm make sudo git -RUN make +RUN make basic -# Verify the install actually landed, not just that `make` exited 0. +# Verify the install actually landed, not just that `make basic` exited 0. RUN test -L /root/.config/nvim/init.lua \ && test -L /root/.config/fish/conf.d/aliases.fish \ && fish -c 'functions -q gitissue' \ diff --git a/tests/Dockerfile-fedora b/tests/Dockerfile-fedora index 03a9574..722fec1 100644 --- a/tests/Dockerfile-fedora +++ b/tests/Dockerfile-fedora @@ -7,9 +7,9 @@ WORKDIR /workspace RUN dnf -y update RUN dnf -y install make sudo -RUN make +RUN make basic -# Verify the install actually landed, not just that `make` exited 0. +# Verify the install actually landed, not just that `make basic` exited 0. RUN test -L /root/.config/nvim/init.lua \ && test -L /root/.config/fish/conf.d/aliases.fish \ && fish -c 'functions -q gitissue' \ diff --git a/tests/Dockerfile-ubuntu b/tests/Dockerfile-ubuntu index 630325a..b9e0d05 100644 --- a/tests/Dockerfile-ubuntu +++ b/tests/Dockerfile-ubuntu @@ -6,9 +6,9 @@ WORKDIR /workspace RUN apt-get update && apt-get install -y make sudo -RUN make +RUN make basic -# Verify the install actually landed, not just that `make` exited 0. +# Verify the install actually landed, not just that `make basic` exited 0. RUN test -L /root/.config/nvim/init.lua \ && test -L /root/.config/fish/conf.d/aliases.fish \ && fish -c 'functions -q gitissue' \