diff --git a/README.md b/README.md index 37a65d1..bd7fa27 100644 --- a/README.md +++ b/README.md @@ -1,366 +1,296 @@ # dotfiles -My personal dotfiles collection, designed for consistency across macOS and Debian/Ubuntu systems using Bash or Zsh, with a separate Windows PowerShell preview path. +[![Shell Quality](https://github.com/alsd4git/dotfiles/actions/workflows/shell-quality.yml/badge.svg)](https://github.com/alsd4git/dotfiles/actions/workflows/shell-quality.yml) +[![License: MIT](https://img.shields.io/badge/license-MIT-yellow.svg)](LICENSE) -The Unix installer requires Bash 3.2 or newer. CI exercises the macOS-compatible Bash 3.2 baseline as well as current Bash and Zsh on Ubuntu 24.04. +Personal, opinionated dotfiles for macOS, Debian/Ubuntu, and Windows. The Unix path configures Bash or Zsh through `install.sh`; the Windows path configures PowerShell through `install.ps1` and uses WinGet for packages. -## License +Review the dry-run and the relevant platform files before applying the configuration to a new machine. The installers can update shell profiles, global Git defaults, package manifests, and platform-specific settings. -MIT. See [LICENSE](LICENSE). +## Contents ---- - -## โœจ Features - -* ๐Ÿงผ **Clean Structure:** Configuration logically separated into `general/`, `git/`, and `nano/` directories. -* โœ… **Shell Compatibility:** Works seamlessly with both Bash and Zsh. -* ๐Ÿš€ **Intelligent Installer (`install.sh`):** - * Symlinks configurations into your `$HOME` directory (default). - * Automatically backs up existing conflicting files (`.bak.`). - * Supports copy mode (`--copy`) instead of symlinking. - * Offers minimal setup (`--minimal`) for core files only. - * Provides dry-run (`--dry-run`) to preview changes. - * Separates component selection (`--all`) from non-interactive confirmation (`--yes`). - * Optional backup cleanup (`--clean-backups`). - -* ๐Ÿ› ๏ธ **Optional Tool Installation:** Installs useful tools via a macOS Brewfile or apt (Debian/Ubuntu): - * `fzf` (Fuzzy finder) + keybindings/completions - * `eza` (Modern `ls` replacement) - * `zoxide` (Smarter `cd`) with shell init - * `bat` (Syntax-highlighting `cat`) with `batcat` shim on Ubuntu - * `fd` (Fast `find`) with `fdfind` shim on Ubuntu - * `ripgrep` (`rg`, fast grep) - * `jq` (Lightweight JSON processor) - * `delta` (Enhanced Git pager/diff viewer) - * `gh` (GitHub CLI) - * `oh-my-posh` (Customizable prompt) - * `exiv2` (Needed for `ren_pics` function) - * `fastfetch` (System info display - preferred) - * `nano` (Ensures a consistent editor is available) - * `shellcheck` (Shell script static analysis) - * `shfmt` (Shell script formatter) - * `uv` (Python tooling manager; can optionally install CPython 3.13 under `~/.local`) - * `swiftly` (Swift toolchain manager; installed but does not install a Swift toolchain) - * macOS also applies a small recommended `defaults` baseline for typing, Finder, Dock, and screenshots, plus an optional saved Dock layout -* ๐Ÿช„ **Enhanced Shell Experience:** - * Sensible command history settings with cross-session sharing. - * `oh-my-posh` integration for an informative prompt (interactive shells only). - * Helpful aliases and functions for common tasks. - * Discover aliases quickly: run `aa` to print a readable alias list (`nice_print_aliases`). -* โš™๏ธ **Git Enhancements:** Useful Git aliases, functions (like `fzf` branch switching), and recommended global settings for branch/tag sorting, safer rebases, richer diffs, push/fetch hygiene, and `core.excludesfile`. - * Examples: `gl` (pull current branch with rebase/autostash), `gp` (push current branch), `gsu` (set upstream), `gla`/`glaf` (last commit summary/full), `lg`/`lgr` (commits missing on origin/release). -* ๐Ÿ”’ **Private Aliases:** Supports loading personal, untracked aliases from `~/.private_aliases`. -* ๐ŸชŸ **Windows Preview:** `install.ps1` bootstraps a small PowerShell profile plus `winget`-based Windows manifests, separately from the Bash/Zsh path. - ---- - -## ๐Ÿ“ Directory Structure - -```sh -. -โ”œโ”€โ”€ archive/ # Historical installers, not used by the supported workflow -โ”œโ”€โ”€ general/ # Shared shell config (aliases, functions, history, prompt) -โ”œโ”€โ”€ git/ # Git-specific aliases, functions, and global ignore -โ”œโ”€โ”€ lib/ # Sourced installer modules (CLI, bootstrap policy, reporting) -โ”œโ”€โ”€ macos/ # macOS Brewfile and system defaults -โ”‚ โ”œโ”€โ”€ Brewfile -โ”‚ โ”œโ”€โ”€ dock.sh -โ”‚ โ””โ”€โ”€ defaults.sh -โ”œโ”€โ”€ nano/ # Nano text editor configuration -โ”œโ”€โ”€ scripts/ # Installer health checks and tool manifest -โ”‚ โ”œโ”€โ”€ health-check.sh -โ”‚ โ”œโ”€โ”€ tool-health-check.sh -โ”‚ โ””โ”€โ”€ tool-health.json -โ”œโ”€โ”€ tests/ # Isolated installer behavior tests with command stubs -โ”‚ โ”œโ”€โ”€ test-installer-functions.sh -โ”‚ โ”œโ”€โ”€ docker-ubuntu-smoke.sh -โ”‚ โ””โ”€โ”€ windows-smoke.ps1 -โ”œโ”€โ”€ windows/ # Minimal PowerShell profile for Windows -โ”‚ โ”œโ”€โ”€ Dotfiles.WindowsPackages.psm1 -โ”‚ โ”œโ”€โ”€ packages.optional.psd1 -โ”‚ โ”œโ”€โ”€ packages.private.example.psd1 -โ”‚ โ”œโ”€โ”€ packages.psd1 -โ”‚ โ”œโ”€โ”€ terminal/ -โ”‚ โ”‚ โ””โ”€โ”€ settings.json -โ”‚ โ”œโ”€โ”€ profile.local.example.ps1 -โ”‚ โ””โ”€โ”€ profile.ps1 -โ”œโ”€โ”€ install.sh # Recommended installation script -โ”œโ”€โ”€ install.ps1 # Windows/PowerShell installer preview -โ””โ”€โ”€ README.md # This file -``` +- [Platform support](#platform-support) +- [Quick start](#quick-start) + - [Windows](#windows) + - [macOS](#macos) + - [Debian and Ubuntu](#debian-and-ubuntu) +- [What this repository manages](#what-this-repository-manages) +- [Installer modes](#installer-modes) +- [Local and private configuration](#local-and-private-configuration) +- [Safety and recovery](#safety-and-recovery) +- [Common commands](#common-commands) +- [Verification and tests](#verification-and-tests) +- [Repository layout](#repository-layout) +- [Detailed guides](#detailed-guides) +- [License](#license) + +## Platform support + +| Platform | Status | Entry point | Package manager | Validation | +| --- | --- | --- | --- | --- | +| macOS | Supported | `install.sh` | Homebrew Bundle | Bash 3.2 compatibility, shell checks, and macOS defaults/Dock dry-runs | +| Debian/Ubuntu | Supported | `install.sh` | APT | Bash and Zsh lifecycle tests on Ubuntu 24.04 | +| Windows | Preview | `install.ps1` | WinGet | PowerShell parsing plus dry-run, sync, and Git restore smoke tests | ---- +Other Linux distributions are not covered by the installer. The shell files can still be adapted manually, but package installation and platform setup assume Debian or Ubuntu. -## ๐Ÿš€ Setup Instructions +The Windows path is marked as preview because it does not yet provide a complete uninstall workflow and CI does not perform a real installation of every WinGet package. -1. **Clone the repository** (Recommended location: `~/.dotfiles`): +## Quick start - ```bash - git clone https://github.com/alsd4git/dotfiles ~/.dotfiles - ``` +### Windows -2. **Navigate into the directory:** +Open a regular PowerShell window. WinGet may request elevation for individual packages when required. - ```bash - cd ~/.dotfiles - ``` +1. Install Git, which is not included in a standard Windows installation: -3. **Make the installer executable:** + ```powershell + winget install --id Git.Git --exact --source winget --accept-package-agreements --accept-source-agreements + ``` - ```bash - chmod +x install.sh - ``` + Close and reopen PowerShell after the installation, then verify the command is available: -4. **Run the installer:** + ```powershell + git --version + ``` - ```bash - ./install.sh - ``` +2. Clone the repository and enter it: - * The script guides you through optional tools and platform settings. Use `--all --yes` for the full non-interactive selection. + ```powershell + git clone https://github.com/alsd4git/dotfiles.git "$HOME\.dotfiles" + Set-Location "$HOME\.dotfiles" + ``` -**Installer Options:** +3. Allow locally stored PowerShell scripts and profiles for the current user: -* `./install.sh --help` or `-h`: Show help message. -* `./install.sh --dry-run` or `-dr`: Show what would be done without making changes (no file writes, no deletions, no global Git config changes). -* `./install.sh --copy` or `-c`: Copy files instead of creating symlinks (backs up existing files). -* `./install.sh --yes` or `-y`: Answer yes to prompts for the operations selected by the invocation. -* `./install.sh --all` or `-a`: Select all optional components. -* `./install.sh --force` or `-f`: Deprecated compatibility alias for `--all --yes`. Backup cleanup remains opt-in. -* `./install.sh --minimal` or `-m`: Install only core dotfiles, skip optional tools and Git config. -* `./install.sh --skip-tools`: Skip optional package managers and tool installation while keeping the normal dotfile and Git setup. -* `./install.sh --sync`: Reconcile dotfiles, shell setup, and Git defaults without installing tools or adding optional startup commands. -* `./install.sh --trust-brew-taps`: On macOS, explicitly trust every third-party tap declared in `macos/Brewfile` before installing packages. -* `./install.sh --brew-upgrade`: On macOS, upgrade the packages tracked by `macos/Brewfile`; the default bootstrap only installs missing packages. -* `./install.sh --uninstall`: Remove symlinks, restore untouched Git defaults captured on first install, and revert shell rc additions this installer made, including Homebrew bootstrap entries on macOS (runs uninstall flow only, then exits). -* `./install.sh --clean-backups` or `-cb`: Offer to remove `.bak.*` files recorded as created by this installer (or preview removals in dry-run mode). Backups from other programs are never selected. + ```powershell + Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser + ``` ---- +4. Preview the changes, then run the interactive installer: -## ๐ŸชŸ Windows Preview + ```powershell + .\install.ps1 -DryRun + .\install.ps1 + ``` -The Windows path is intentionally smaller and currently focuses on PowerShell profile setup plus package manager bootstrap. +5. Open a new PowerShell or Windows Terminal session to load the installed profile. -### Refreshing the macOS inventory +See the [Windows guide](docs/windows.md) for WinGet recovery, package selection, changed files, local overlays, backup behavior, and known limitations. -The companion `list-macOS-apps` tool can produce a current snapshot without changing this repository: +### macOS + +Check that Git is available: ```bash -mkdir -p /tmp/macos-inventory -../list-macOS-apps/list-installed-apps.sh \ - --with-formulae \ - --export-json \ - --output-dir /tmp/macos-inventory +git --version ``` -Use the snapshot to review `macos/Brewfile`, but promote only stable, cross-machine tools. Manual applications, beta builds, browsers, personal services, and machine-specific SDKs should remain outside the public manifest unless they have an explicit baseline role. +On a new Mac, install the Xcode Command Line Tools first if that command is unavailable: -```powershell -Set-ExecutionPolicy RemoteSigned -Scope CurrentUser -.\install.ps1 +```bash +xcode-select --install ``` -Use `.\install.ps1 -Sync` to reconcile profiles and Git defaults without installing packages. Use `.\install.ps1 -RestoreGitDefaults` to restore the Git values saved before the first install; settings changed after installation are preserved. +After the tools finish installing: -The Windows installer backs up any conflicting profile or Git ignore file as `.bak.` before copying the shared version into place, and it also creates the optional local overlay directory used by the public profile loader. +```bash +git clone https://github.com/alsd4git/dotfiles.git ~/.dotfiles +cd ~/.dotfiles +./install.sh --dry-run +./install.sh +``` -It also applies the same recommended global Git defaults as the Bash/Zsh installer path, so Git behavior stays consistent across machines. +The executable bit for `install.sh` is tracked by Git, so a normal clone does not require `chmod +x`. -There is also a tracked example at `windows/profile.local.example.ps1` you can copy or adapt for local-only tweaks. +The Command Line Tools are enough for Git and the initial clone. The current optional macOS package/defaults path also checks `xcodebuild -version`, so install full Xcode before approving that phase. A configuration-only run with `--skip-tools` does not enter the Xcode/Homebrew path. -The installer copies the curated Windows package baseline into `~\.config\dotfiles\windows\packages.psd1` and the optional extras into `~\.config\dotfiles\windows\packages.optional.psd1`, so the shared manifests stay available even after the repo is moved or not mounted. +See the [macOS guide](docs/macos.md) before applying the Homebrew manifest, system defaults, or saved Dock layout. -Winget manifest normalization, manifest discovery, and installed-package detection live in `windows/Dotfiles.WindowsPackages.psm1`; the installer and copied profile both use that module to keep package state interpretation consistent. +### Debian and Ubuntu -There is also a tracked template at `windows/packages.private.example.psd1` that can be copied to `~\.config\dotfiles\windows\packages.private.psd1` for local-only package entries. +Install Git before cloning the repository: -If you want to remove old Windows backup files later, run `.\install.ps1 -CleanBackups` and confirm the prompt, or add `-Force` to skip the confirmation. +```bash +sudo apt update +sudo apt install -y git -The Windows bootstrap assumes `winget` is already available through App Installer. +git clone https://github.com/alsd4git/dotfiles.git ~/.dotfiles +cd ~/.dotfiles +./install.sh --dry-run +./install.sh +``` -The Windows profile also exposes `a` to inspect commands, `aa` to print aliases, plus update helpers like `npmupg` and `wingup`. +Run the initial APT commands as `root` instead when `sudo` is not installed. The package phase inside the current installer invokes `sudo`; use `./install.sh --skip-tools` and manage packages separately when `sudo` is intentionally unavailable. -The installer does not reload the active PowerShell session in place, which keeps the current prompt stable. Open a new PowerShell window after installation, or run `rld` manually if you want to re-source the profile. +See the [Debian/Ubuntu guide](docs/linux.md) for package groups, shell setup, optional toolchains, update behavior, and troubleshooting. -There are curated public manifests in `windows/packages.psd1` and `windows/packages.optional.psd1` that track the starter baseline by package manager: +## What this repository manages -- `winget` for core shell/runtime apps and Store-backed desktop apps -- `Bitwarden`, `Chrome`, `Quick Share`, `Telegram`, `Android Studio`, `Keyguard`, `RustDesk`, `Tailscale`, `Zen Browser`, `UniGetUI`, and the rest of the desktop apps you asked for live in the optional extras manifest -- Cross-platform CLI tools that are equally useful on Windows now include `shellcheck`, `shfmt`, `yq`, `ast-grep`, `actionlint`, `pandoc`, `ffmpeg`, and `ExifTool` -- `NpmGlobal` remains intentionally empty so we do not encode machine-specific or personal globals into the repo -- If a future app only exists through Microsoft Store, the Windows manifest already supports `Source = 'msstore'` on a Winget entry; for now we only use that when it is genuinely needed. +- **Shell configuration:** shared aliases, functions, history settings, prompt initialization, Nano configuration, and Bash/Zsh startup entries. +- **PowerShell configuration:** profiles for Windows PowerShell and PowerShell 7, aliases and helper functions, local profile overlays, Windows Terminal settings, and selected application configuration. +- **Git configuration:** a shared global ignore file and a conservative set of global defaults without replacing the complete user `.gitconfig`. +- **Packages:** `macos/Brewfile`, Debian/Ubuntu package groups, and separate Windows core, optional, and private manifests. +- **Platform settings:** an optional macOS defaults baseline, an optional saved Dock layout, Windows Terminal settings, and TrafficMonitor configuration. +- **Recovery state:** timestamped backups and snapshots of the Git settings managed by the installers. -The Windows prompt uses the upstream `tokyo.omp.json` shipped with the `JanDeDobbeleer.OhMyPosh` package, the minimal Windows Terminal settings live in `windows/terminal/settings.json`, and `JetBrainsMono Nerd Font` is part of the core `winget` baseline. The live prompt resolves the installed `oh-my-posh` theme folder once, preferring the AppX install location when available, and caches it under `AppData\Local` so the profile stays simple while still adapting to the installed path. +Package manifests are the source of truth for the current inventory. The README intentionally does not duplicate every package name. -The installer prints a summary of the manifests, shows a short alias cheat sheet, and can install only the missing items after an explicit confirmation, so you can rerun the bootstrap as many times as needed without duplicating work. Use `-y` if you want to answer yes to all installer prompts without typing each confirmation. +## Installer modes -Winget does not guarantee a Start Menu shortcut for every desktop package. To create the curated shortcuts for GUI packages that are installed without one, run `.\windows\start-menu-shortcuts.ps1`; use `-Repair` after a Winget upgrade if a package moved its executable. +### macOS and Debian/Ubuntu -For machine-specific PowerShell tweaks, keep them outside the repo in one of these optional local overlays: +| Command | Behavior | +| --- | --- | +| `./install.sh` | Interactive setup with managed dotfiles, Git defaults, and optional components | +| `./install.sh --dry-run` | Preview managed file and Git actions without modifying the machine; the package/tool phase is skipped | +| `./install.sh --minimal` | Install only the core dotfiles; skip Git defaults, package installation, and remote bootstrap operations | +| `./install.sh --skip-tools` | Apply dotfiles and Git defaults without installing packages or optional tools | +| `./install.sh --sync` | Reconcile managed dotfiles and Git defaults without installing tools or adding optional startup commands | +| `./install.sh --all --yes` | Select all optional components and answer yes to their prompts | +| `./install.sh --copy` | Copy managed files instead of symlinking them, with backups for conflicts | +| `./install.sh --clean-backups` | Offer to remove only backups recorded by this installer | +| `./install.sh --uninstall` | Remove managed links/startup entries and restore unchanged Git settings captured before installation | -- `~\.private_profile.ps1` for one-off overrides -- `~\.config\dotfiles\windows\profile.d\*.ps1` for ordered local fragments +Run `./install.sh --help` for the complete option list. macOS-specific options such as `--trust-brew-taps` and `--brew-upgrade` are documented in the [macOS guide](docs/macos.md). -The public profile loads those overlays last, so they can override the shared defaults without forcing personal details into the repo. +### Windows ---- +| Command | Behavior | +| --- | --- | +| `.\install.ps1` | Apply managed configuration and ask separately about core, optional, and private packages | +| `.\install.ps1 -DryRun` | Preview file, Git, and package operations without changing the machine | +| `.\install.ps1 -Minimal` | Apply managed configuration but skip package installation | +| `.\install.ps1 -Sync` | Reconcile profiles, copied configuration, and Git defaults without installing packages | +| `.\install.ps1 -Force` or `-y` | Answer yes to all package prompts; this includes optional and non-empty private manifests | +| `.\install.ps1 -CleanBackups` | Inspect the currently supported Windows backup-cleanup scope and ask before deletion | +| `.\install.ps1 -RestoreGitDefaults` | Restore managed Git values when they have not changed since installation | -## ๐Ÿง  What `install.sh` Does +For a non-destructive preview of the complete Windows package selection, use: -* **Detects OS and Shell:** Determines if you're on macOS or Debian/Ubuntu Linux, and using Bash or Zsh. -* **Creates Symlinks (Default):** For each configuration file (e.g., `general/.aliases`), it creates a symlink in your home directory (e.g., `~/.shell_aliases`) pointing back to the file in the `~/.dotfiles` repository. - * If a file or symlink already exists at the destination, it's backed up as `~/.bak.`. -* **Updates RC Files:** Adds lines to your `~/.bashrc` or `~/.zshrc` (creating them if they don't exist) to source the new alias, function, history, and prompt files. It checks if lines already exist to avoid duplicates. -* **Configures Global Git Ignore:** - * Symlinks `git/global.gitignore` to `$HOME/.global.gitignore`. - * Runs `git config --global core.excludesfile "$HOME/.global.gitignore"` to tell Git to use this file. -* **Sets Git Defaults:** Merges the shared [`git/defaults.conf`](git/defaults.conf) baseline into the existing user config without replacing the machine's `.gitconfig`. It covers branch/tag sorting, rebase ergonomics, verbose commits, smarter diffs, push/fetch hygiene, `core.editor = nano`, and `init.defaultBranch = main`. - * The first install saves the previous values for these keys under `~/.config/dotfiles/installer-state`. `--uninstall` restores them unless a setting was changed after installation, in which case it leaves the newer value untouched. - * When optional tools are approved and `delta` is available, the installer offers an explicit opt-in for `core.pager`, interactive diff filtering, navigation, dark theme, side-by-side output, and line numbers. Automatic, minimal, sync, and dry-run modes never enable it implicitly. -* **Installs Optional Tools (if confirmed or selected with `--all`):** Uses `brew` (macOS) or `apt` (Debian/Ubuntu) to install tools listed in the Features section. - * If Homebrew is missing on macOS, the installer bootstraps it and sets up shell env automatically (adds `eval "$(/opt/homebrew/bin/brew shellenv)"` or `eval "$(/usr/local/bin/brew shellenv)"` depending on install path). - * On macOS, the tool manifest lives in `macos/Brewfile`, the baseline defaults live in `macos/defaults.sh`, and the saved Dock layout lives in `macos/dock.sh`. - * On Ubuntu/Debian, the `bat` binary may be named `batcat`, and `fd` as `fdfind`. The installer creates shims (`/usr/local/bin/bat` and `/usr/local/bin/fd`) for a consistent experience. - * For Python/Swift tooling, the managers are installed (`uv`, `swiftly`), and the script can optionally install CPython 3.13 via `uv` or the latest stable Swift toolchain via `swiftly`. - * Ensures `~/.local/bin` is on `PATH` (if the directory exists) so user-installed tools like `uv` and `swiftly` are available. - * On Linux, `swiftly` is installed from the official Swift.org tarball flow and initialized with `--skip-install` to avoid installing a Swift toolchain by default. - * On Linux, `swiftly` requires `gpg` for signature verification; the installer ensures `gnupg` is installed. - * On macOS, `brew bundle install` runs with `--no-upgrade` by default. Use `--brew-upgrade` to update managed dependencies, or Homebrew's `HOMEBREW_BUNDLE_BREW_SKIP`, `HOMEBREW_BUNDLE_CASK_SKIP`, `HOMEBREW_BUNDLE_MAS_SKIP`, and `HOMEBREW_BUNDLE_TAP_SKIP` environment variables for per-machine exclusions. - * Fresh Homebrew installations that enforce tap trust need `--trust-brew-taps` when the Brewfile contains third-party taps. The flag is deliberately explicit because it grants those taps permission to run their formulae and casks. - * Linux installs follow [eza's official Debian/Ubuntu instructions](https://github.com/eza-community/eza/blob/main/INSTALL.md): the upstream `deb.asc` key is stored in a dedicated `signed-by` keyring and the documented `http://deb.gierens.de` repository URL is preserved. Package integrity is enforced by APT signatures; the installer does not download an unverified `latest` release `.deb`. - * Bootstrap policy is canonical in `lib/bootstrap-policy.sh`. Run `DOTFILES_TEST_FUNCTION=bootstrap-policy ./install.sh` to print the current inventory. `nvm` is pinned by default to `v0.40.4`; moving official channels are explicitly labeled `trusted-upstream-dynamic`. - * Optional bootstrap verification: `run_remote_script` accepts `--sha256`; provide `DOTFILES_HOMEBREW_INSTALL_SHA256`, `DOTFILES_OHMYPOSH_INSTALL_SHA256`, `DOTFILES_UV_INSTALL_SHA256`, or `DOTFILES_NVM_INSTALL_SHA256` to verify the downloaded installer before execution. `DOTFILES_SWIFTLY_INSTALL_SHA256` verifies the Swiftly archive, and `DOTFILES_EZA_KEY_SHA256` verifies the eza repository key, before use. These values are intentionally opt-in because the corresponding upstream channels are dynamic and do not publish one stable digest for the moving bootstrap URL. - * Supply-chain exceptions: eza follows the upstream signed APT repository flow (the repository key is fetched from the official eza source), while Homebrew, Oh My Posh, uv, Swiftly, and the pinned nvm installer remain upstream-controlled channels unless an operator supplies a digest. Prefer reviewing those upstream release/install pages before a new-machine bootstrap. -* **macOS Defaults:** On macOS, the installer can apply a small `defaults` baseline for typing, Finder, Dock, and screenshots. -* **macOS Dock Layout:** The installer can also restore the saved Dock apps/folders from `macos/dock.sh` using `dockutil`. -* **Checks for Dependencies:** Verifies if essential commands used by aliases/functions (like `docker`, `swift`, `git`, `nano`) are present and warns if not. -* **Configures Startup Commands (Optional):** Asks if you want `nice_print_aliases` and `fastfetch` (or `screenfetch` as a fallback) to run when a new shell starts. These run only in interactive shells. -* **fzf & zoxide Initialization:** If installed, `zoxide` is initialized for your shell; `fzf` keybindings/completions are sourced when available. -* **Swiftly Env:** On Linux, the installer adds a line to your shell rc to source `~/.local/share/swiftly/env.sh` (if present) so `swiftly` and installed toolchains are on `PATH`. -* **PATH Cleanup:** The installer appends a snippet to remove duplicate entries from `PATH` while preserving order. During upgrades it also removes the legacy de-duplication lines from older installer versions before adding the current implementation. -* **Optional Node Tooling:** Offers to install or update `nvm` (Node Version Manager) to the pinned `v0.40.4` release. Set `DOTFILES_NVM_VERSION` explicitly when you want a different reviewed release. If installed, your shell will source `~/.nvm/nvm.sh` automatically. - * nvm commands run through a narrow compatibility wrapper that temporarily disables Bash `nounset` while nvm executes, because nvm's internal functions can read an unset local variable under `set -u`. The installer restores strict mode immediately after each call. - * If no Node is active via `nvm`, you can install the latest LTS and set it as default. - * If a Node version is already active via `nvm`, the installer offers to switch to the latest LTS and set it as default. After a successful switch it asks whether to migrate global npm packages with `nvm reinstall-packages ` ([official nvm guidance](https://github.com/nvm-sh/nvm#copying-global-packages-from-previously-installed-version)); declining leaves the old environment untouched. Automatic modes never migrate packages implicitly and print the command for later use. - * If `corepack` is available, it is enabled after installing/switching to LTS to provide Yarn/PNPM shims. -* **Optional Python Tooling:** Installs `uv` (Python tool and package manager). Optionally offers to install CPython 3.13 managed by `uv` with `python`/`python3` defaults (does not change your system `python`). -* **Optional Swift Tooling:** Installs `swiftly` (Swift toolchain manager). Optionally offers to install the latest stable Swift toolchain via `swiftly`. -* **GitHub CLI Authentication:** After optional tools are approved, if `gh` is installed and not authenticated, the installer offers an interactive [`gh auth login`](https://cli.github.com/manual/gh_auth_login). It never starts authentication in automatic, minimal, sync, dry-run, or non-interactive runs. After a successful login it prints optional commands for [`gh auth setup-git`](https://cli.github.com/manual/gh_auth_setup-git) and SSH/GPG signing-key setup. +```powershell +.\install.ps1 -DryRun -Force +``` -Run `./scripts/health-check.sh --strict` after a standard installation to verify the managed shell files and global Git ignore configuration. +Do not use `-Force` for a conservative first installation: it selects all available package manifests rather than merely overwriting files. -For an isolated Linux lifecycle check, run `./tests/docker-ubuntu-smoke.sh`. It uses `ubuntu:24.04`, mounts the repository read-only, and validates install, shell loading, health checks, and uninstall for Bash and Zsh in disposable homes. +## Local and private configuration -The main workflow is organized into named platform-tools, Git, and NVM phases. Required, optional, and advisory operations use the shared reporting policy so failures remain visible in the final summary. +Keep machine-specific or sensitive configuration outside the tracked public files. -Run `./scripts/tool-health-check.sh` to inspect the availability and reported versions of the curated Git, JSON, editor, GitHub, Python, and Swift tools. The check reports Swiftly separately when its binary is present but its user configuration is not initialized. Add `--strict` when missing optional tools or failed version commands should fail the check. +### macOS and Debian/Ubuntu ---- +Create `~/.private_aliases`. The shared aliases file loads it automatically when present. -## ๐Ÿ’ฌ Notes +### Windows -* **Legacy Installer:** `archive/old_setup.sh` is retained only for historical reference. It is not part of the supported installation workflow. -* **Zsh Default:** If you use Zsh, ensure it's set as your default login shell: `chsh -s $(which zsh)` -* **Private Aliases:** You can create a `~/.private_aliases` file to store personal aliases you don't want to commit to Git. The main alias file (`general/.aliases`) will automatically source it if it exists. -* **Backups:** Old configuration files backed up by the script will have names like `~/.bashrc.bak.1678886400`. The installer records these paths in its state directory; `./install.sh --clean-backups` only considers that manifest, so backups from other programs are left untouched. -* **System Info:** The script can run `fastfetch` on startup if available. If `fastfetch` is not found, it falls back to trying `screenfetch`. Note that the installer only attempts to install `fastfetch`, not `screenfetch`. +The PowerShell profile loads these locations last: ---- +1. `~\.config\dotfiles\windows\profile.d\*.ps1`, in filename order +2. `~\.private_profile.ps1` -## โš ๏ธ Supported Platforms +Use [`windows/profile.local.example.ps1`](windows/profile.local.example.ps1) as a starting point. The overlay directory can also be replaced with the `DOTFILES_WINDOWS_PROFILE_DIR` environment variable. -* macOS (via Homebrew Bundle) -* Debian/Ubuntu (via apt) +For local-only package entries, edit the scaffold created at: -Other Linux distributions are not covered by the installer. You can adapt the scripts or install tools manually on those platforms. +```text +~\.config\dotfiles\windows\packages.private.psd1 +``` ---- +The public template is [`windows/packages.private.example.psd1`](windows/packages.private.example.psd1). -## ๐Ÿงฉ Platform Matrix +## Safety and recovery -| Platform | Package manager | What the installer does | -| --- | --- | --- | -| macOS | Homebrew Bundle | Bootstraps Homebrew if missing, installs the manifest in `macos/Brewfile`, applies the recommended defaults in `macos/defaults.sh`, restores the Dock layout in `macos/dock.sh`, and updates shell startup files for `brew`, `fzf`, `zoxide`, `nvm`, and `swiftly` when relevant. | -| Debian/Ubuntu | apt | Installs core packages, configures `gh` from the official repository, creates `bat`/`fd` shims when needed, and installs `swiftly` from the official tarball flow. | -| Windows | winget | Installs the PowerShell profile and uses winget for the public baseline. | +- Run the appropriate dry-run before the first installation and after material configuration changes. Review package manifests separately because the Unix dry-run skips the package/tool phase. +- Existing conflicting files are moved to timestamped paths such as `.bak.` before replacement. +- The Unix installer records the backups it creates and limits `--clean-backups` to that manifest. +- The Windows installer currently creates backups but does not maintain an equivalent recursive backup manifest. Its cleanup command has a narrower scope; see the [Windows recovery notes](docs/windows.md#backup-and-recovery). +- Both installers snapshot the Git values they manage. Restore logic leaves a setting untouched when it was changed after installation. +- `install.sh --uninstall` provides a conservative Unix uninstall. Windows currently has `-RestoreGitDefaults`, not a complete uninstall. +- macOS defaults, the saved Dock layout, Windows Terminal settings, and TrafficMonitor configuration are opinionated. Review their source files before applying them. ---- +## Common commands -## ๐Ÿ”Ž Troubleshooting +The shared command names use shell-native implementations so they remain familiar across Bash, Zsh, and PowerShell. -* **Homebrew not on `PATH`:** Open a new shell or run `eval "$(/opt/homebrew/bin/brew shellenv)"` on Apple Silicon, or `eval "$(/usr/local/bin/brew shellenv)"` on Intel Macs. -* **No Rosetta bootstrap:** The macOS bootstrap is intended for native Apple Silicon. Intel-only software is left to manual installation or a separate, explicit bootstrap path. -* **`nvm` does not load:** Restart the shell or source `~/.bashrc` / `~/.zshrc`; if you need a one-off recovery, run `export NVM_DIR="$HOME/.nvm"; . "$NVM_DIR/nvm.sh"; nvm use --lts`. -* **`swiftly` is missing on Linux:** Make sure `~/.local/share/swiftly/env.sh` exists and that `gnupg` is installed, because signature verification depends on `gpg`; a manual recovery is `test -f "$HOME/.local/share/swiftly/env.sh" && . "$HOME/.local/share/swiftly/env.sh" && swiftly install stable`. -* **Xcode developer tools are missing on macOS:** The installer now checks `xcode-select -p` and `xcodebuild -version` before running the macOS package/bootstrap path. If either check fails, install Xcode from the App Store or run `xcode-select --install` and retry. -* **Three-finger drag is not taking effect:** Set it manually in `System Settings > Accessibility > Pointer Control > Trackpad Options` and enable `Use trackpad for dragging`. macOS may not persist that toggle reliably through `defaults`. -* **`fzf` bindings are missing:** Rerun the installer with `--all` or source the `fzf` keybindings and completion files manually from your shell rc. -* **`bat` and `fd` look unfamiliar on Ubuntu:** `batcat` and `fdfind` are the packaged binary names; the installer creates `bat` and `fd` shims when it can write to `/usr/local/bin`. -* **Prompt customization is not visible:** `oh-my-posh` only loads in interactive shells, so non-interactive sessions will not show the prompt theme. -* **`winget` is missing on Windows:** Install App Installer from Microsoft and retry the bootstrap. -* **Touch ID for `sudo`:** On macOS, the installer can only check whether Touch ID is already enabled for `sudo` and print a manual recovery hint if it is missing. The file to edit is `/etc/pam.d/sudo`, and the line to add is `auth sufficient pam_tid.so`. -* **Stats.app is blocked by Gatekeeper:** If Stats is installed via Homebrew but still refuses to open, run `sudo xattr -r -d com.apple.quarantine /Applications/Stats.app/`. -* **Inventory sync:** The companion `list-macOS-apps` repo can help snapshot installed Mac apps before you expand or prune `macos/Brewfile`. -* **Warp on macOS:** Keep Warp outside `macos/Brewfile` and install/update it manually, because its built-in updater is less conflict-prone than managing the app through Homebrew. -* **Homebrew ownership:** `brewup` updates formulae and standard casks. Use `brewupall` only when you intentionally want to include `:latest` and self-updating casks. Do not add `brew bundle cleanup` to the installer: it removes packages that are not in the Brewfile. -* **Windows package baseline:** The public starter inventory lives in `windows/packages.psd1` and `windows/packages.optional.psd1`; treat them as curated baselines, not a dump of every installed Windows app. -* **Windows package sources:** Use `winget` for GUI apps and for the CLI tools that are available there. Store-only apps that do not resolve reliably in `winget` should stay manual instead of making the bootstrap more fragile; `PC Manager` is one of those edge cases on some machines. -* **Windows prompt assets:** the prompt uses the upstream `tokyo.omp.json` shipped with the `JanDeDobbeleer.OhMyPosh` package, `windows/terminal/settings.json` captures the minimal Terminal defaults, and `JetBrainsMono Nerd Font` is bootstrapped through the core `winget` manifest. The live profile points to the installed theme path, prefers the AppX install location when present, and caches the resolved folder under `AppData\Local` so the prompt stays straightforward. -* **Windows Terminal cleanup:** The template intentionally leaves out machine-specific SSH and one-off profiles; keep those in a local overlay if you still want them. -* **Windows reruns are safe:** `install.ps1` only installs missing packages after you confirm the prompt. -* **Prompt refresh:** If the shell prompt looks stale after a run, use `rld` to re-source the profile and refresh `oh-my-posh`. +| Command | Platform | Purpose | +| --- | --- | --- | +| `a` / `aa` | All configured shells | Inspect or list configured aliases and command shortcuts | +| `l`, `la`, `ll`, `lt` | All configured shells | Directory listings, using `eza` when available | +| `rld` | Bash, Zsh, PowerShell | Reload the active shell configuration without starting a nested shell | +| `gl` / `gp` | All configured shells | Pull with rebase/autostash or push the current branch | +| `gsu` | All configured shells | Set the upstream to `origin/` | +| `gla` / `glaf` | All configured shells | Show the latest commit summary or full patch | +| `npmupg` | Bash, Zsh, PowerShell | Show outdated global npm packages and run `npm update -g` | +| `brewup` | macOS | Update, upgrade, and clean Homebrew packages | +| `wingup` | Windows | Show and apply WinGet upgrades | ---- +`rld` detects the active interpreter rather than relying on the login-shell value in `$SHELL`. It re-sources `~/.bashrc` in Bash, `${ZDOTDIR:-$HOME}/.zshrc` in Zsh, and the current-user all-hosts profile in PowerShell. It does not reload login-only files such as `.bash_profile` or `.zprofile`. The older Unix command `rldz` remains as a compatibility alias to `rld`. -## ๐Ÿ“š Sources and Inspiration +`npmupg` requires `npm`. On Unix it now reports a clear error when Node.js is unavailable instead of executing two failing commands. -* Git defaults: [How Core Git Developers Configure Git](https://blog.gitbutler.com/how-git-core-devs-configure-git) by Scott Chacon. The installer adopts the broadly useful settings from the article, while leaving personal conflict-resolution memory (`rerere`), repository-size-specific options such as `core.fsmonitor` and `core.untrackedCache`, plus version-sensitive conflict marker defaults such as `merge.conflictStyle = zdiff3`, out of the global baseline. +## Verification and tests ---- +After a Unix installation, verify the managed configuration: -## โœ… Verification +```bash +type rld npmupg +git config --global --get core.excludesfile +./scripts/health-check.sh --strict +./scripts/tool-health-check.sh +``` -After installation, a quick smoke check is: +When the optional tool phase was approved, also verify its commands: ```bash command -v git nano fzf zoxide uv swiftly gh +``` + +After a Windows installation, open a new PowerShell session and run: + +```powershell +Get-Command git, winget, aa, l, gl, rld, npmupg, wingup -ErrorAction SilentlyContinue git config --global --get core.excludesfile -./scripts/tool-health-check.sh ``` -If you use Zsh, open a new interactive shell and confirm that `aa`, `l`, `gl`, and `myip` are available. - ---- - -## ๐Ÿงญ Cheatsheet - -* Shell basics: - * `a` / `aa`: Inspect a command or print aliases. - * `l`/`lt`/`ll`: Directory listings (use `eza` if installed, otherwise `ls`). - * `mntlist`: Show mounted volumes (portable, does not shadow `mount`). - * `myip`: Show public IP. - * `rld`: Reload the current PowerShell profile. - * `brewup`: Update, upgrade and clean Homebrew (macOS). - -* Packages and repos: - * `npmupg`: Update all globally installed npm packages (respecting semver ranges). - * `rpx`: Run RepoMix and output `-repomix.md`, ignoring `*.html`. - -* Docker helpers: - * `up_dockers`: Pull latest tags for all local image repositories. - * `up_dockers_wt`: One-shot updates via Watchtower (`--run-once --cleanup`). - * `up_portainer_ce` / `up_portainer_be`: Recreate Portainer CE/BE containers with volumes/ports. - -* Git aliases (highlights): - * `gl` / `gp`: Pull (rebase+autostash) / push current branch. - * `gsu`: Set upstream to `origin/`. - * `gla` / `glaf`: Show last commit summary / full diff. - * `gd` / `gds`: Diff vs. HEAD / diff stats. - * `gcb` / `gca` / `gcd`: New branch / amend / amend with now timestamp. - * `lg`: Commits on local branch not on `origin/`. - * `lgr`: Commits on current branch not in `origin/release`. - -* Git fzf functions: - * `fuzzy_branch_selector`: Select a branch (includes remotes); uses `git switch` with tracking. - * `fuzzy_log_viewer`: Fuzzy-find commits with preview (`git show`). - * `git_see_authors`: Shortlog authors summary. +Repository checks include: + +```bash +./tests/test-installer-functions.sh +./tests/docker-ubuntu-smoke.sh +``` + +```powershell +pwsh -File .\tests\windows-smoke.ps1 +``` + +GitHub Actions runs ShellCheck, `shfmt`, Unix installer tests, Ubuntu lifecycle tests, macOS defaults/Dock dry-runs, and Windows PowerShell smoke tests. + +## Repository layout + +```text +. +โ”œโ”€โ”€ .github/ GitHub Actions workflow +โ”œโ”€โ”€ archive/ Historical installers, not part of the supported workflow +โ”œโ”€โ”€ docs/ Platform-specific setup and troubleshooting guides +โ”œโ”€โ”€ general/ Shared aliases, functions, history, and prompt setup +โ”œโ”€โ”€ git/ Git aliases, functions, defaults, and global ignore +โ”œโ”€โ”€ lib/ Unix installer modules and package policy +โ”œโ”€โ”€ macos/ Brewfile, defaults, and saved Dock layout +โ”œโ”€โ”€ nano/ Nano configuration +โ”œโ”€โ”€ scripts/ Health checks and tool inventory +โ”œโ”€โ”€ tests/ Installer, lifecycle, and PowerShell smoke tests +โ”œโ”€โ”€ windows/ PowerShell profile, manifests, Terminal, and app configuration +โ”œโ”€โ”€ install.sh macOS and Debian/Ubuntu installer +โ””โ”€โ”€ install.ps1 Windows installer +``` + +## Detailed guides + +- [Windows setup](docs/windows.md) +- [macOS setup](docs/macos.md) +- [Debian and Ubuntu setup](docs/linux.md) + +## License + +MIT. See [LICENSE](LICENSE). diff --git a/docs/linux.md b/docs/linux.md new file mode 100644 index 0000000..56b7a9c --- /dev/null +++ b/docs/linux.md @@ -0,0 +1,398 @@ +# Debian and Ubuntu setup + +[Back to the main README](../README.md) + +The Linux installer supports Debian and Ubuntu with Bash or Zsh. It manages shared dotfiles and Git defaults, installs a curated package baseline through APT, and can bootstrap selected cross-platform development tools. + +## Contents + +- [Supported systems and requirements](#supported-systems-and-requirements) +- [Fresh installation](#fresh-installation) +- [What the installer changes](#what-the-installer-changes) +- [Installer modes](#installer-modes) +- [Packages and optional toolchains](#packages-and-optional-toolchains) +- [Local configuration](#local-configuration) +- [Updating and uninstalling](#updating-and-uninstalling) +- [Bootstrap security](#bootstrap-security) +- [Troubleshooting](#troubleshooting) +- [Verification and lifecycle tests](#verification-and-lifecycle-tests) + +## Supported systems and requirements + +Supported package-manager path: + +- Debian +- Ubuntu + +The installer expects: + +- Bash 3.2 or newer for the installer itself +- Bash or Zsh as the configured interactive shell +- APT +- `sudo` for package installation performed by the current installer +- Internet access for packages and approved optional bootstrap operations + +Other Linux distributions are not supported by the package and platform logic. The shell files can be adapted manually, but do not run the APT path unchanged on another distribution. + +## Fresh installation + +### 1. Install Git + +Git is required before the repository can be cloned: + +```bash +sudo apt update +sudo apt install -y git +``` + +Run the commands as `root` instead when `sudo` is not installed. + +### 2. Clone the repository + +```bash +git clone https://github.com/alsd4git/dotfiles.git ~/.dotfiles +cd ~/.dotfiles +``` + +The executable bit for `install.sh` is tracked by Git, so a normal clone does not require `chmod +x`. + +### 3. Preview the installer + +```bash +./install.sh --dry-run +``` + +The dry-run previews managed file and Git operations. The package/tool phase is skipped rather than simulated, so review [`lib/tool-manifest.sh`](../lib/tool-manifest.sh) and the bootstrap policy before a full installation. + +### 4. Run the installer + +```bash +./install.sh +``` + +The default run configures the shared files and asks before optional package/tool operations. + +## What the installer changes + +### Managed dotfiles + +By default, the installer creates symlinks from the home directory to the repository: + +| Home path | Repository source | +| --- | --- | +| `~/.shell_aliases` | `general/.aliases` | +| `~/.shell_functions` | `general/.functions` | +| `~/.history_settings` | `general/.history_settings` | +| `~/.omp_init` | `general/.omp_init` | +| `~/.nanorc` | `nano/.nanorc` | +| `~/.git_aliases` | `git/.git_aliases` | +| `~/.git_functions` | `git/.git_functions` | +| `~/.global.gitignore` | `git/global.gitignore` | + +Use `--copy` when symlinks back to the repository are not desirable. + +### Shell startup files + +The installer updates the active Bash or Zsh startup file so it loads the managed aliases, functions, history, prompt, `~/.local/bin`, and Swiftly environment when available. Additions are marker-aware and designed to avoid duplicates. + +It also installs a PATH de-duplication snippet and removes the legacy implementation used by older versions of this repository. + +### Git defaults + +The keys in [`git/defaults.conf`](../git/defaults.conf) are merged into the user's global Git config. The installer does not replace the complete `.gitconfig`. + +Before the first change, previous values are recorded under: + +```text +~/.config/dotfiles/installer-state/ +``` + +The global ignore is configured with: + +```text +core.excludesfile = ~/.global.gitignore +``` + +When `delta` is available, its pager configuration remains an explicit opt-in; automatic, minimal, sync, and dry-run modes do not enable it implicitly. + +### Backups + +Conflicting files are moved to timestamped `.bak.` paths. Every backup created by the Unix installer is recorded, allowing cleanup to target only installer-owned backups. + +## Installer modes + +| Command | Behavior | +| --- | --- | +| `./install.sh` | Interactive managed dotfiles, Git defaults, packages, and optional tooling | +| `./install.sh --dry-run` | Preview managed file and Git operations; the package/tool phase is skipped | +| `./install.sh --minimal` | Install core dotfiles only; skip Git defaults, package installation, and remote fetches | +| `./install.sh --skip-tools` | Apply dotfiles and Git defaults without package-manager or optional-tool installation | +| `./install.sh --sync` | Reconcile dotfiles and Git defaults without tools or optional startup commands | +| `./install.sh --copy` | Copy files instead of symlinking them, backing up conflicts first | +| `./install.sh --all` | Select every optional component; review the package and bootstrap sources before using it | +| `./install.sh --yes` | Answer yes to prompts for operations already selected by the invocation | +| `./install.sh --all --yes` | Full non-interactive component selection | +| `./install.sh --clean-backups` | Offer to remove only paths recorded in the installer's backup manifest | +| `./install.sh --uninstall` | Remove managed links/startup entries and conservatively restore Git settings | + +`--force` is a deprecated compatibility alias for `--all --yes`. + +Mode restrictions are validated before installation. For example, `--sync` cannot be mixed with component-selection or cleanup switches, and `--uninstall` cannot be mixed with installation-only options. + +## Packages and optional toolchains + +The canonical Debian/Ubuntu package groups are in [`lib/tool-manifest.sh`](../lib/tool-manifest.sh). + +### Required APT baseline + +```text +curl exiv2 fzf gnupg jq nano ripgrep unzip +``` + +### Optional APT baseline + +```text +bat delta fd-find fastfetch zoxide +``` + +Additional platform logic installs or configures selected tools through their appropriate upstream path, including GitHub CLI, eza, Oh My Posh, uv, Swiftly, and NVM. + +### Consistent command names + +Debian/Ubuntu package names can differ from the upstream command names: + +- `bat` may be installed as `batcat` +- `fd` may be installed as `fdfind` + +The installer creates `/usr/local/bin/bat` and `/usr/local/bin/fd` shims when it can write there, so aliases and scripts can use the cross-platform names. + +### Python with uv + +The installer can install `uv` and optionally install CPython 3.13 under the user environment. It does not replace the distribution's system Python. + +Verify the managed runtime with: + +```bash +uv python list +``` + +### Swift with Swiftly + +On Linux, Swiftly is installed through the official archive/signature flow and initialized without installing a Swift toolchain by default. `gnupg` is required for verification. + +The shell startup file loads this environment when present: + +```text +~/.local/share/swiftly/env.sh +``` + +A stable Swift toolchain is a separate optional choice. + +### Node.js with NVM + +The installer can install or update NVM. The reviewed default version is: + +```text +v0.40.4 +``` + +Override it only with another reviewed semantic version: + +```bash +DOTFILES_NVM_VERSION=v0.40.4 ./install.sh +``` + +When no NVM-managed Node version is active, the installer can install the latest LTS and set it as default. When moving from an existing version, migration of global npm packages remains an explicit prompt and is never performed automatically. + +If available, Corepack is enabled after the selected Node LTS is active. + +### GitHub CLI authentication + +After optional tools are approved, an interactive run can offer `gh auth login` when GitHub CLI is installed but not authenticated. + +Authentication is not started in automatic, minimal, sync, dry-run, or non-interactive flows. + +## Local configuration + +### Private aliases + +Create an untracked `~/.private_aliases` file: + +```bash +touch ~/.private_aliases +``` + +The shared alias configuration loads it automatically. + +### Git identity + +The installer configures shared behavior, not personal identity: + +```bash +git config --global user.name "Your Name" +git config --global user.email "you@example.com" +``` + +### Zsh as the login shell + +When Zsh is installed but is not the login shell: + +```bash +chsh -s "$(command -v zsh)" +``` + +Log out and back in before expecting every terminal to use it. + +## Updating and uninstalling + +Update the repository: + +```bash +git -C ~/.dotfiles pull --ff-only +cd ~/.dotfiles +``` + +Preview and apply a configuration-only sync: + +```bash +./install.sh --dry-run --sync +./install.sh --sync +``` + +Run the normal installer again when package groups or optional tool choices changed: + +```bash +./install.sh +``` + +Preview and run the conservative uninstall: + +```bash +./install.sh --dry-run --uninstall +./install.sh --uninstall +``` + +Uninstall removes managed symlinks and shell-startup additions and restores Git values that still match the installer's managed values. It does not remove packages installed through APT or third-party tool managers. + +Clean only backups recorded by this installer: + +```bash +./install.sh --dry-run --clean-backups +./install.sh --clean-backups +``` + +Backups created by other applications are not selected. + +## Bootstrap security + +The canonical bootstrap classification is in [`lib/bootstrap-policy.sh`](../lib/bootstrap-policy.sh). Print it with: + +```bash +DOTFILES_TEST_FUNCTION=bootstrap-policy ./install.sh +``` + +The eza path follows its signed Debian/Ubuntu repository flow. Other moving upstream installers remain upstream-controlled unless an operator supplies a reviewed digest. + +Optional digest variables include: + +- `DOTFILES_OHMYPOSH_INSTALL_SHA256` +- `DOTFILES_UV_INSTALL_SHA256` +- `DOTFILES_NVM_INSTALL_SHA256` +- `DOTFILES_SWIFTLY_INSTALL_SHA256` +- `DOTFILES_EZA_KEY_SHA256` + +Use these when a reviewed artifact digest is available. Do not copy a digest from an unrelated version merely to satisfy the check. + +## Troubleshooting + +### `sudo` is not installed + +The initial `apt install git` bootstrap can be run from a root shell, but the current package phase inside `install.sh` invokes `sudo` explicitly. Install/configure `sudo`, or use `--skip-tools` and manage packages separately. The dotfile operations themselves target the current user's home directory. + +### `bat` or `fd` is missing + +Check the distribution binary names: + +```bash +command -v bat batcat fd fdfind +``` + +Rerun the installer after ensuring `/usr/local/bin` is writable for the shim creation, or define user-local aliases manually. + +### `fzf` keybindings or completion are missing + +Open a new interactive shell, then confirm the package is available: + +```bash +command -v fzf +``` + +Rerun the installer with optional tools selected if it is missing: + +```bash +./install.sh --all +``` + +### NVM does not load + +Open a new shell or recover the active session manually: + +```bash +export NVM_DIR="$HOME/.nvm" +[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" +nvm use --lts +``` + +### Swiftly is installed but unavailable + +Check and source its environment: + +```bash +test -f "$HOME/.local/share/swiftly/env.sh" && . "$HOME/.local/share/swiftly/env.sh" +command -v swiftly +``` + +`gnupg` must be installed because the Linux Swiftly flow verifies signatures with `gpg`. + +### The prompt is missing + +Oh My Posh initialization runs only in interactive shells. Verify from a new terminal rather than a non-interactive CI shell: + +```bash +command -v oh-my-posh +``` + +### Unsupported Linux distributions + +On a real run, before it links or copies any managed files, `install.sh` reads `/etc/os-release` and accepts only `ID=debian` or `ID=ubuntu`. Other Linux distributions exit with an error before the installer changes the home directory. A dry-run remains available for previewing managed file operations, but it does not simulate package installation. Adapt the shared shell files manually or add a dedicated, tested platform module before using package installation. + +## Verification and lifecycle tests + +Open a new interactive shell and verify the managed configuration: + +```bash +type rld npmupg +git config --global --get core.excludesfile +./scripts/health-check.sh --strict +./scripts/tool-health-check.sh +``` + +If you approved the optional tool phase, verify those commands separately: + +```bash +command -v git nano fzf zoxide uv swiftly gh +``` + +Run the isolated installer function tests: + +```bash +./tests/test-installer-functions.sh +``` + +Run the full disposable Ubuntu 24.04 Bash/Zsh lifecycle test when Docker is available: + +```bash +./tests/docker-ubuntu-smoke.sh +``` + +The container test mounts the repository read-only and verifies install, interactive shell loading, health checks, and uninstall in isolated home directories. diff --git a/docs/macos.md b/docs/macos.md new file mode 100644 index 0000000..2f5c306 --- /dev/null +++ b/docs/macos.md @@ -0,0 +1,384 @@ +# macOS setup + +[Back to the main README](../README.md) + +The macOS path manages Bash/Zsh dotfiles, Git defaults, a Homebrew Bundle manifest, an optional system-defaults baseline, and an optional saved Dock layout. The configuration is intentionally opinionated; preview it before applying it to an existing Mac. + +## Contents + +- [Requirements](#requirements) +- [Fresh installation](#fresh-installation) +- [Installer modes](#installer-modes) +- [Homebrew management](#homebrew-management) +- [macOS defaults](#macos-defaults) +- [Saved Dock layout](#saved-dock-layout) +- [Local configuration](#local-configuration) +- [Updating and uninstalling](#updating-and-uninstalling) +- [Bootstrap security](#bootstrap-security) +- [Refreshing the application inventory](#refreshing-the-application-inventory) +- [Troubleshooting](#troubleshooting) +- [Verification](#verification) + +## Requirements + +- macOS +- Bash 3.2 or newer, or Zsh +- Xcode Command Line Tools for Git and the initial clone +- Full Xcode when using the current optional Homebrew/defaults/Dock path, which verifies `xcodebuild -version` +- Internet access for Homebrew and optional tool installation + +The installer supports the Bash 3.2 baseline shipped by older macOS releases and current Zsh. Native Apple Silicon is the primary macOS bootstrap path; the installer does not automatically add Rosetta for Intel-only software. + +## Fresh installation + +### 1. Install the Command Line Tools + +Check whether Git is available: + +```bash +git --version +``` + +On a new Mac, request the Xcode Command Line Tools if that command is unavailable: + +```bash +xcode-select --install +``` + +Finish the graphical installation before continuing. Verify both the selected developer directory and Git: + +```bash +xcode-select -p +git --version +``` + +The Command Line Tools are sufficient for this bootstrap and clone step. + +### 2. Clone the repository + +```bash +git clone https://github.com/alsd4git/dotfiles.git ~/.dotfiles +cd ~/.dotfiles +``` + +The executable bit for `install.sh` is tracked by Git. A normal clone does not require a separate `chmod +x install.sh` step. + +### 3. Choose the installation scope + +For managed dotfiles and Git defaults without Homebrew, macOS defaults, or Dock changes: + +```bash +./install.sh --dry-run --skip-tools +./install.sh --skip-tools +``` + +For the optional macOS package/defaults/Dock phase, the current installer requires full Xcode because its preflight checks `xcodebuild -version`. Install Xcode from the App Store, select it, and verify it before approving that phase: + +```bash +sudo xcode-select -s /Applications/Xcode.app/Contents/Developer +xcodebuild -version +``` + +### 4. Preview and run the interactive installer + +```bash +./install.sh --dry-run +./install.sh +``` + +The default run configures the managed dotfiles and asks before optional tools and platform settings are applied. The Unix dry-run previews file and Git operations; it deliberately skips the package/tool phase. Review the Brewfile and use the standalone macOS script dry-runs before approving the full platform setup. + +## Installer modes + +| Command | Behavior | +| --- | --- | +| `./install.sh` | Interactive dotfile, Git, package, and macOS setup | +| `./install.sh --dry-run` | Preview managed file and Git operations; the package/tool phase is skipped | +| `./install.sh --minimal` | Install only core dotfiles; skip Git defaults, tools, and remote fetches | +| `./install.sh --skip-tools` | Apply dotfiles and Git defaults without package-manager or tool installation | +| `./install.sh --sync` | Reconcile dotfiles and Git defaults without tools or optional startup commands | +| `./install.sh --copy` | Copy managed files instead of symlinking them | +| `./install.sh --all` | Select all optional components; review the platform files before using it | +| `./install.sh --yes` | Answer yes to prompts for the operations selected by the invocation | +| `./install.sh --all --yes` | Full non-interactive component selection | +| `./install.sh --trust-brew-taps` | Explicitly trust third-party taps declared by the Brewfile | +| `./install.sh --brew-upgrade` | Upgrade dependencies tracked by the Brewfile instead of installing only missing items | +| `./install.sh --clean-backups` | Offer to remove only backups recorded by the installer | +| `./install.sh --uninstall` | Remove managed links/startup entries and conservatively restore Git settings | + +`--force` remains a deprecated compatibility alias for `--all --yes`. Prefer the two explicit switches in scripts and documentation. + +## Homebrew management + +The declarative inventory lives in [`macos/Brewfile`](../macos/Brewfile). It contains formulae, casks, Mac App Store entries, fonts, and the third-party taps required by selected packages. + +When optional tools are approved and Homebrew is missing, the installer can bootstrap Homebrew and add the appropriate shell environment line: + +- Apple Silicon: `eval "$(/opt/homebrew/bin/brew shellenv)"` +- Intel: `eval "$(/usr/local/bin/brew shellenv)"` + +### Default install behavior + +The default Homebrew Bundle operation installs missing dependencies without upgrading every already-installed package. Use this for a predictable new-machine bootstrap: + +```bash +./install.sh --all --yes --trust-brew-taps +``` + +The trust switch is explicit because the current Brewfile contains third-party taps. Review [`macos/Brewfile`](../macos/Brewfile) before approving them. + +### Upgrade managed dependencies + +```bash +./install.sh --all --yes --trust-brew-taps --brew-upgrade +``` + +This permits Homebrew Bundle to upgrade dependencies tracked by the manifest. + +### Per-machine exclusions + +Homebrew Bundle supports environment variables for skipping selected entries without editing the shared file: + +- `HOMEBREW_BUNDLE_BREW_SKIP` +- `HOMEBREW_BUNDLE_CASK_SKIP` +- `HOMEBREW_BUNDLE_MAS_SKIP` +- `HOMEBREW_BUNDLE_TAP_SKIP` + +Keep personal applications, beta builds, machine-specific SDKs, and software with a reliable built-in updater outside the public baseline unless they have a clear cross-machine role. + +Do not add an automatic `brew bundle cleanup` step to the installer. Cleanup removes packages that are absent from the Brewfile, including intentionally unmanaged software. + +## macOS defaults + +[`macos/defaults.sh`](../macos/defaults.sh) manages a compact shared baseline. It currently covers: + +- dark interface style +- key-repeat behavior instead of press-and-hold accents +- visible file extensions +- spring-loaded folders +- natural scrolling +- Finder path/status bars and list view +- external, mounted, and removable volumes on the desktop +- Dock size, magnification, recent apps, and autohide behavior +- tap-to-click and Force Click preferences +- PNG screenshots stored under `~/Pictures/Screenshots` + +Preview the exact commands independently: + +```bash +./macos/defaults.sh --dry-run --restart +``` + +Apply them and restart the affected UI processes: + +```bash +./macos/defaults.sh --restart +``` + +Review the source before applying it to an established Mac. macOS does not provide a universal automatic rollback for arbitrary `defaults write` operations. + +## Saved Dock layout + +[`macos/dock.sh`](../macos/dock.sh) uses `dockutil` to replace the existing Dock contents with the saved application/folder layout. Missing applications are skipped, but the current Dock is still cleared first. + +Preview the operation: + +```bash +./macos/dock.sh --dry-run --restart +``` + +Apply it only after reviewing the tracked app list: + +```bash +./macos/dock.sh --restart +``` + +This operation is intentionally separate and destructive with respect to the existing Dock arrangement. + +## Local configuration + +### Private aliases + +Create `~/.private_aliases` for aliases that should not be committed: + +```bash +touch ~/.private_aliases +``` + +The shared aliases file loads it automatically when present. + +### Homebrew exclusions + +Prefer the Homebrew Bundle skip variables for one-machine omissions rather than deleting shared manifest entries. + +### Git identity + +The installer manages behavior defaults but does not invent a personal identity. Configure it separately when needed: + +```bash +git config --global user.name "Your Name" +git config --global user.email "you@example.com" +``` + +## Updating and uninstalling + +Update the repository: + +```bash +git -C ~/.dotfiles pull --ff-only +cd ~/.dotfiles +``` + +Preview and apply configuration reconciliation without package installation: + +```bash +./install.sh --dry-run --sync +./install.sh --sync +``` + +Run the normal or full installer when the Brewfile or optional components changed: + +```bash +./install.sh +``` + +The conservative uninstall path is: + +```bash +./install.sh --dry-run --uninstall +./install.sh --uninstall +``` + +It removes managed symlinks and shell-startup additions, restores Git values that have not been changed since installation, and reverts Homebrew bootstrap entries added by the installer. It does not uninstall Homebrew packages, reverse every macOS default, or reconstruct a previous Dock layout. + +## Bootstrap security + +The canonical remote-bootstrap classification is in [`lib/bootstrap-policy.sh`](../lib/bootstrap-policy.sh). Print it with: + +```bash +DOTFILES_TEST_FUNCTION=bootstrap-policy ./install.sh +``` + +Moving upstream installers are not treated as immutable artifacts. Optional SHA-256 environment variables can pin reviewed downloads before execution, including: + +- `DOTFILES_HOMEBREW_INSTALL_SHA256` +- `DOTFILES_OHMYPOSH_INSTALL_SHA256` +- `DOTFILES_UV_INSTALL_SHA256` +- `DOTFILES_NVM_INSTALL_SHA256` +- `DOTFILES_SWIFTLY_INSTALL_SHA256` + +These values are opt-in because upstream moving install URLs do not expose one permanent digest. Review the corresponding upstream release/install page before bootstrapping a new machine. + +The NVM release defaults to the pinned `v0.40.4`. Override it only with another reviewed semantic version: + +```bash +DOTFILES_NVM_VERSION=v0.40.4 ./install.sh +``` + +## Refreshing the application inventory + +The companion [`alsd4git/list-macOS-apps`](https://github.com/alsd4git/list-macOS-apps) repository can create an inventory snapshot without changing this repository. + +With both repositories cloned as siblings: + +```bash +mkdir -p /tmp/macos-inventory +../list-macOS-apps/list-installed-apps.sh \ + --with-formulae \ + --export-json \ + --output-dir /tmp/macos-inventory +``` + +Use the result as review input for [`macos/Brewfile`](../macos/Brewfile), not as a file to copy wholesale. Promote only stable items with a clear shared purpose. + +## Troubleshooting + +### Homebrew is not on `PATH` + +Open a new shell, or initialize it manually for the active session: + +```bash +# Apple Silicon +eval "$(/opt/homebrew/bin/brew shellenv)" + +# Intel +eval "$(/usr/local/bin/brew shellenv)" +``` + +Then verify: + +```bash +brew --version +``` + +### Xcode developer tools are missing + +The Command Line Tools provide Git for the bootstrap, but the current optional macOS package path checks both `xcode-select -p` and `xcodebuild -version`. Install full Xcode, select its developer directory, and retry: + +```bash +sudo xcode-select -s /Applications/Xcode.app/Contents/Developer +xcodebuild -version +``` + +Use `./install.sh --skip-tools` when only the managed dotfiles and Git defaults are required. + +### A third-party tap is rejected + +Review the `tap` declarations in [`macos/Brewfile`](../macos/Brewfile), then rerun with explicit trust: + +```bash +./install.sh --all --yes --trust-brew-taps +``` + +Do not add the trust switch blindly to a machine-wide automation before reviewing new tap changes. + +### Three-finger drag did not apply + +Set it manually in: + +```text +System Settings > Accessibility > Pointer Control > Trackpad Options +``` + +Enable **Use trackpad for dragging**. macOS does not persist this preference reliably through the same `defaults` interfaces used for the rest of the baseline. + +### Advisory-only system checks + +The installer may report that Stats still has a quarantine attribute or that Touch ID for `sudo` is not enabled. It does not remove quarantine attributes or edit PAM configuration automatically. Verify the application source and current macOS guidance before making either system-level change manually. + +### The prompt or optional tools are missing + +Open a new interactive shell and verify the installed commands: + +```bash +command -v brew fzf zoxide oh-my-posh +``` + +Prompt initialization is intentionally skipped for non-interactive shells. + +## Verification + +Open a new Bash or Zsh session and verify the managed configuration: + +```bash +type rld npmupg +git config --global --get core.excludesfile +./scripts/health-check.sh --strict +./scripts/tool-health-check.sh +``` + +If you approved the Homebrew and optional tool phase, verify those commands +separately: + +```bash +command -v git nano fzf zoxide uv swiftly gh +``` + +Preview the standalone macOS scripts: + +```bash +./macos/defaults.sh --dry-run --restart +./macos/dock.sh --dry-run --restart +``` + +GitHub Actions checks the shared shell sources and runs these macOS dry-runs on a macOS runner. diff --git a/docs/windows.md b/docs/windows.md new file mode 100644 index 0000000..85aa7ce --- /dev/null +++ b/docs/windows.md @@ -0,0 +1,362 @@ +# Windows setup + +[Back to the main README](../README.md) + +The Windows path installs a shared PowerShell profile, applies the repository's Git defaults, copies the Windows package manifests, and manages selected Windows Terminal and application settings. It is usable but remains a preview because there is not yet a complete Windows uninstall workflow. + +## Contents + +- [Requirements](#requirements) +- [Fresh installation](#fresh-installation) +- [What the installer changes](#what-the-installer-changes) +- [Installer modes](#installer-modes) +- [Package manifests](#package-manifests) +- [Local configuration](#local-configuration) +- [Start Menu shortcuts](#start-menu-shortcuts) +- [Updating and rerunning](#updating-and-rerunning) +- [Backup and recovery](#backup-and-recovery) +- [Troubleshooting](#troubleshooting) +- [Verification](#verification) + +## Requirements + +- Windows with [WinGet](https://learn.microsoft.com/windows/package-manager/winget/) available through App Installer +- Windows PowerShell 5.1 or PowerShell 7 +- Git for cloning the repository and applying the managed Git configuration +- Internet access when installing packages + +Run the installer from a regular PowerShell session. WinGet can request elevation for individual packages that require it. + +## Fresh installation + +### 1. Install Git + +Git is not included in a standard Windows installation. Install it with WinGet before attempting to clone this repository: + +```powershell +winget install --id Git.Git --exact --source winget --accept-package-agreements --accept-source-agreements +``` + +Close and reopen PowerShell so the refreshed `PATH` is visible, then verify the installation: + +```powershell +git --version +``` + +### 2. Clone the repository + +```powershell +git clone https://github.com/alsd4git/dotfiles.git "$HOME\.dotfiles" +Set-Location "$HOME\.dotfiles" +``` + +### 3. Allow local scripts and profiles + +The shared profile must be allowed to run in future PowerShell sessions: + +```powershell +Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser +``` + +This is a persistent current-user setting. Review the effective policies with: + +```powershell +Get-ExecutionPolicy -List +``` + +A domain or device-management policy can override `CurrentUser`. See the official [`Set-ExecutionPolicy` documentation](https://learn.microsoft.com/powershell/module/microsoft.powershell.security/set-executionpolicy) for the precedence rules. + +### 4. Preview the changes + +```powershell +.\install.ps1 -DryRun +``` + +To include every package group in the preview without answering prompts: + +```powershell +.\install.ps1 -DryRun -Force +``` + +### 5. Run the installer + +```powershell +.\install.ps1 +``` + +The default run applies the managed configuration and then asks separately whether to install missing core, optional, and local private packages. + +### 6. Start a new shell + +Open a new PowerShell or Windows Terminal session after installation. The installer deliberately does not reload the active session in place. + +## What the installer changes + +The installer compares file contents before replacing them. When an existing target differs, it moves the old file to a timestamped `.bak.` path before writing the managed version. + +| Area | Target | Behavior | +| --- | --- | --- | +| Shared PowerShell profile | `\PowerShell\profile.ps1` | Copies [`windows/profile.ps1`](../windows/profile.ps1) for PowerShell 7 | +| PowerShell host profile | `\PowerShell\Microsoft.PowerShell_profile.ps1` | Writes a small managed shim so the shared profile is loaded only once | +| Shared Windows PowerShell profile | `\WindowsPowerShell\profile.ps1` | Copies the same shared profile for Windows PowerShell 5.1 | +| Windows PowerShell host profile | `\WindowsPowerShell\Microsoft.PowerShell_profile.ps1` | Writes the same managed shim | +| Global Git ignore | `~\.gitignore_global` | Copies [`git/global.gitignore`](../git/global.gitignore) and sets `core.excludesfile` | +| Global Git defaults | User Git config | Merges the keys in [`git/defaults.conf`](../git/defaults.conf) without replacing the whole `.gitconfig` | +| Windows package state | `~\.config\dotfiles\windows\` | Copies the core and optional manifests plus the shared PowerShell package module | +| Private package scaffold | `~\.config\dotfiles\windows\packages.private.psd1` | Creates it from the public example only when it does not already exist | +| Local profile fragments | `~\.config\dotfiles\windows\profile.d\` | Creates the directory when missing | +| Windows Terminal | The detected Terminal `LocalState\settings.json` | Copies [`windows/terminal/settings.json`](../windows/terminal/settings.json) with backup | +| TrafficMonitor | Supported user-local and WinGet package paths | Copies [`windows/trafficmonitor/config.ini`](../windows/trafficmonitor/config.ini) with backup | + +The Documents directory is resolved through Windows rather than assumed to be `C:\Users\\Documents`, so redirected or OneDrive-backed Documents folders are respected. + +Windows Terminal and TrafficMonitor configuration are currently copied during normal, `-Minimal`, and `-Sync` runs. Package selection only controls package installation; it does not disable these managed files. + +## Installer modes + +| Command | Behavior | +| --- | --- | +| `.\install.ps1` | Apply files and Git defaults, then ask about each package group | +| `.\install.ps1 -DryRun` | Preview files, Git changes, manifest selection, and missing packages without modifying the machine | +| `.\install.ps1 -Minimal` | Apply managed files and Git defaults but skip package installation | +| `.\install.ps1 -Sync` | Reconcile profiles, copied files, manifests, and Git defaults without installing packages | +| `.\install.ps1 -Force` | Answer yes to core, optional, and non-empty private package prompts | +| `.\install.ps1 -y` | Alias for `-Force` | +| `.\install.ps1 -CleanBackups` | List backups in the currently supported cleanup scope and ask before deletion | +| `.\install.ps1 -RestoreGitDefaults` | Restore installer-managed Git values when they still match the values applied by the installer | + +Important mode rules: + +- `-Sync` cannot be combined with `-Minimal`, `-Force`, or `-CleanBackups`. +- `-RestoreGitDefaults` is a dedicated operation and cannot be combined with installation options. +- `-DryRun` can be combined with `-Force` or `-Sync` for a non-destructive full preview. +- `-Force` is a yes-to-all switch, not simply an overwrite switch. It opts into optional and local private packages as well. + +## Package manifests + +The package inventory is split by intent: + +| Manifest | Purpose | +| --- | --- | +| [`windows/packages.psd1`](../windows/packages.psd1) | Conservative public core: Git, PowerShell, Windows Terminal, prompt/font support, and common CLI tools | +| [`windows/packages.optional.psd1`](../windows/packages.optional.psd1) | Public-safe desktop applications and additional development/media utilities | +| `~\.config\dotfiles\windows\packages.private.psd1` | Machine-specific or private packages that must not be committed | + +[`windows/Dotfiles.WindowsPackages.psm1`](../windows/Dotfiles.WindowsPackages.psm1) is shared by the installer and installed profile. It normalizes manifest entries, discovers core/optional/private manifests, and checks whether packages are already installed. + +Both `Winget` and `NpmGlobal` sections are supported. The public `NpmGlobal` sections are intentionally empty so machine-specific global npm tools are not encoded into the repository. + +A WinGet entry can be either a package ID string or a structured entry with an explicit source. The installer uses exact package IDs and installs only packages it does not detect as already present. + +## Local configuration + +### PowerShell overlays + +The shared profile loads local configuration at the end, so local files can override public defaults without modifying the repository. + +The default order is: + +1. `~\.config\dotfiles\windows\profile.d\*.ps1`, sorted by filename +2. `~\.private_profile.ps1` + +Copy [`windows/profile.local.example.ps1`](../windows/profile.local.example.ps1) as a starting point: + +```powershell +Copy-Item .\windows\profile.local.example.ps1 "$HOME\.private_profile.ps1" +``` + +For multiple ordered fragments: + +```powershell +Copy-Item .\windows\profile.local.example.ps1 "$HOME\.config\dotfiles\windows\profile.d\90-local.ps1" +``` + +Set `DOTFILES_WINDOWS_PROFILE_DIR` when the fragment directory must live somewhere else. + +### Private packages + +The first installer run creates this local-only scaffold when it is missing: + +```text +~\.config\dotfiles\windows\packages.private.psd1 +``` + +Keep credentials, internal package identifiers, and machine-specific choices out of the tracked public manifests. + +## Start Menu shortcuts + +Some portable or WinGet-installed applications do not create useful Start Menu shortcuts. Run the curated helper after package installation: + +```powershell +.\windows\start-menu-shortcuts.ps1 +``` + +After a package upgrade moves an executable, repair an existing shortcut with: + +```powershell +.\windows\start-menu-shortcuts.ps1 -Repair +``` + +The script currently knows selected package-specific executable locations; it is not a general shortcut generator for every manifest entry. + +## Updating and rerunning + +Update the repository first: + +```powershell +git -C "$HOME\.dotfiles" pull --ff-only +Set-Location "$HOME\.dotfiles" +``` + +Preview and apply configuration-only reconciliation: + +```powershell +.\install.ps1 -DryRun -Sync +.\install.ps1 -Sync +``` + +Run the normal installer again when newly added packages should be considered: + +```powershell +.\install.ps1 +``` + +Reruns are content-aware for managed files and check package presence before attempting installation. + +## Backup and recovery + +### File backups + +A replaced file is moved beside its original target with a name such as: + +```text +profile.ps1.bak. +settings.json.bak. +``` + +Restore a file by moving the managed version aside and renaming the desired backup to its original name. + +### Current cleanup limitation + +`-CleanBackups` currently scans only the top level of `$HOME` for `*.bak.*` files. It does not recurse into Documents, AppData, Windows Terminal, or TrafficMonitor directories, where several installer backups can exist. + +Treat the command as a limited helper rather than a complete backup manager. Inspect nested backups manually before deleting them. A future installer change should record every Windows backup in a manifest, matching the safer Unix behavior. + +### Git defaults + +Before the first Git configuration change, the installer stores previous values in: + +```text +~\.config\dotfiles\installer-state\git-config.before.windows.json +``` + +Restore them with: + +```powershell +.\install.ps1 -RestoreGitDefaults +``` + +For safety, a Git key is left unchanged when its current value no longer matches the value previously applied by the installer. + +### No complete uninstall yet + +The Windows path does not currently remove profiles, copied manifests, Terminal settings, TrafficMonitor configuration, or overlay directories automatically. Restore backed-up files and remove managed copies manually when a full rollback is required. + +## Troubleshooting + +### `winget` is not available + +WinGet is supplied through App Installer. Update or install App Installer, then open a new PowerShell session. + +On a newly provisioned Windows account where App Installer exists but WinGet has not registered yet, request registration with: + +```powershell +Add-AppxPackage -RegisterByFamilyName -MainPackage Microsoft.DesktopAppInstaller_8wekyb3d8bbwe +``` + +Then verify: + +```powershell +winget --version +``` + +See Microsoft's [WinGet installation and troubleshooting documentation](https://learn.microsoft.com/windows/package-manager/winget/) for current platform requirements. + +### `git` is not found after WinGet installation + +Close every open PowerShell/Terminal window and start a new session. Then run: + +```powershell +git --version +Get-Command git +``` + +If the command is still unavailable, inspect the Git installation with: + +```powershell +winget list --id Git.Git --exact +``` + +### Script execution is blocked + +Inspect the effective policy order: + +```powershell +Get-ExecutionPolicy -List +``` + +Apply the current-user policy again when it is not overridden by Group Policy: + +```powershell +Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser +``` + +Do not permanently switch the machine to `Bypass` merely to run this repository. + +### The new profile is not loaded + +Open a new PowerShell session. If an older version of the profile is already active and provides `rld`, run: + +```powershell +rld +``` + +Check the profile paths PowerShell is using: + +```powershell +$PROFILE | Format-List * -Force +``` + +### Windows Terminal settings changed unexpectedly + +The installer manages the detected Terminal `settings.json`. Look for the adjacent timestamped backup and restore it manually, or edit [`windows/terminal/settings.json`](../windows/terminal/settings.json) before the next sync. + +Keep machine-specific Terminal profiles outside the tracked template; the managed file is intentionally a minimal shared baseline. + +### A package installation fails + +Inspect the exact manifest ID and ask WinGet for details: + +```powershell +winget show --id Git.Git --exact --source winget +``` + +Run the installer again after correcting the package source or local WinGet state. Successful and already-installed packages are skipped on subsequent runs. + +## Verification + +Open a new PowerShell session and run: + +```powershell +Get-Command git, winget, aa, l, gl, npmupg, wingup -ErrorAction SilentlyContinue +git config --global --get core.excludesfile +git config --global --get init.defaultBranch +``` + +Run the repository smoke test with PowerShell 7: + +```powershell +pwsh -File .\tests\windows-smoke.ps1 +``` + +The smoke test parses the PowerShell sources, validates manifest normalization, performs a minimal dry-run, uses temporary HOME/Git state for the lifecycle checks, applies `-Sync`, and verifies `-RestoreGitDefaults`. diff --git a/general/.aliases b/general/.aliases index ce7f4f0..537dbb7 100644 --- a/general/.aliases +++ b/general/.aliases @@ -37,6 +37,9 @@ else fi fi +# Keep the common "show all" spelling available in Bash and Zsh. +alias la='l' + # Always available eza-based aliases (only if eza is installed) if [ "$USE_EZA" = true ]; then alias lx='eza --long --all --group-directories-first --header --links --sort=name --icons --group --git --total-size' @@ -76,8 +79,36 @@ function brewupall() { } alias noh='[[ ${SHELL##*/} == zsh ]] && : > ~/.zsh_history || : > ~/.bash_history' -alias rld="echo -e 'reloading .bashrc\n' && . ~/.bashrc" -alias rldz="echo -e 'reloading .zshrc\n' && source ~/.zshrc" + +# Reload the startup file for the shell that is running this function. Use the +# interpreter variables instead of $SHELL, which can still name a login shell +# different from the current process. +function rld() { + local shell_name rc_file + + if [ -n "${ZSH_VERSION:-}" ]; then + shell_name='zsh' + rc_file="${ZDOTDIR:-$HOME}/.zshrc" + elif [ -n "${BASH_VERSION:-}" ]; then + shell_name='bash' + rc_file="$HOME/.bashrc" + else + echo 'rld: unsupported active shell. Supported shells: Bash and Zsh.' >&2 + return 1 + fi + + if [ ! -r "$rc_file" ]; then + echo "rld: cannot read $rc_file" >&2 + return 1 + fi + + printf 'Reloading %s configuration: %s\n' "$shell_name" "$rc_file" + # shellcheck source=/dev/null + . "$rc_file" +} + +# Backward-compatible name; rld now detects Bash or Zsh itself. +alias rldz='rld' alias wget='wget -c' function update() { @@ -122,5 +153,14 @@ alias edt='[[ ${SHELL##*/} == zsh ]] && nano ~/.zshrc || nano ~/.bashrc' alias rpx='repomix -o "$(basename "$PWD")-repomix.md" --style markdown --ignore "*.html"' # ===== Package Management ===== -# npm: update all globally installed packages to the latest versions allowed by semver ranges -alias npmupg='npm outdated -g ; npm update -g' +# npm: inspect and update globally installed packages to versions allowed by semver ranges. +function npmupg() { + if ! command -v npm >/dev/null 2>&1; then + echo 'npmupg: npm not found. Install Node.js or activate an NVM-managed version.' >&2 + return 1 + fi + + echo 'Checking globally installed npm packages...' + npm outdated -g || true + npm update -g +} diff --git a/install.sh b/install.sh index 2b769cf..c806c84 100755 --- a/install.sh +++ b/install.sh @@ -133,6 +133,10 @@ SYMLINK_VALUES=( OS="$(uname -s)" SHELL_NAME=$(basename "$SHELL") +if [ "$OS" = "Linux" ] && ! $DRY_RUN; then + validate_linux_distribution +fi + if $TRUST_BREW_TAPS && [ "$OS" != "Darwin" ]; then echo "โŒ --trust-brew-taps is only available on macOS." >&2 exit 2 diff --git a/lib/installer-platforms.sh b/lib/installer-platforms.sh index 2fb3d03..eca8259 100644 --- a/lib/installer-platforms.sh +++ b/lib/installer-platforms.sh @@ -1,5 +1,36 @@ #!/usr/bin/env bash +validate_linux_distribution() { + local distribution_id='' + local key value + + if [ ! -r /etc/os-release ]; then + echo "โŒ Cannot identify the Linux distribution: /etc/os-release is unavailable." >&2 + return 1 + fi + + while IFS='=' read -r key value; do + if [ "$key" = ID ]; then + distribution_id=${value#\"} + distribution_id=${distribution_id%\"} + break + fi + done &2 + ;; + *) + echo "โŒ Unsupported Linux distribution: $distribution_id. This installer supports Debian and Ubuntu." >&2 + ;; + esac + return 1 +} + apt_package_installed() { dpkg -s "$1" >/dev/null 2>&1; } install_required_apt_package() { diff --git a/tests/docker-ubuntu-smoke.sh b/tests/docker-ubuntu-smoke.sh index 5506219..b083bc9 100755 --- a/tests/docker-ubuntu-smoke.sh +++ b/tests/docker-ubuntu-smoke.sh @@ -23,7 +23,29 @@ docker run --rm \ /workspace/scripts/health-check.sh --strict >/tmp/health.log grep -Fq "Required failures: 0" /tmp/install.log - "$shell_bin" -ic "type aa >/dev/null; type gl >/dev/null" + case "$shell_bin" in + /bin/bash) + rc_file="$test_home/.bashrc" + expected_shell=bash + ;; + /usr/bin/zsh) + rc_file="$test_home/.zshrc" + expected_shell=zsh + ;; + esac + + printf "\nexport DOTFILES_RLD_SMOKE=%s\n" "$expected_shell" >>"$rc_file" + "$shell_bin" -ic " + unset DOTFILES_RLD_SMOKE + type aa >/dev/null + type gl >/dev/null + type la >/dev/null + type rld >/dev/null + type npmupg >/dev/null + rld >/tmp/rld.log + test \"\$DOTFILES_RLD_SMOKE\" = \"$expected_shell\" + " + grep -Fq "Reloading $expected_shell configuration: $rc_file" /tmp/rld.log /workspace/install.sh --uninstall --force >/tmp/uninstall.log test ! -e "$test_home/.shell_aliases" diff --git a/tests/windows-smoke.ps1 b/tests/windows-smoke.ps1 index 9ffac90..904a751 100644 --- a/tests/windows-smoke.ps1 +++ b/tests/windows-smoke.ps1 @@ -25,6 +25,25 @@ foreach ($source in $sources) { } } +$profileTokens = $null +$profileErrors = $null +$profileAst = [System.Management.Automation.Language.Parser]::ParseFile( + (Join-Path $repoRoot 'windows/profile.ps1'), + [ref]$profileTokens, + [ref]$profileErrors +) +$profileFunctions = @( + $profileAst.FindAll( + { param($node) $node -is [System.Management.Automation.Language.FunctionDefinitionAst] }, + $true + ) | ForEach-Object Name +) +foreach ($commandName in @('rld', 'npmupg')) { + if ($profileFunctions -notcontains $commandName) { + throw "Missing shared PowerShell helper: $commandName" + } +} + Import-PowerShellDataFile -Path (Join-Path $repoRoot 'windows/packages.psd1') | Out-Null Import-Module (Join-Path $repoRoot 'windows/Dotfiles.WindowsPackages.psm1') -Force