-
Notifications
You must be signed in to change notification settings - Fork 0
Add portable package bootstrap and application dotfiles #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
fac2b6b
feat: add portable package bootstrap
mark-groves ee01e8d
fix: address package bootstrap reviews
mark-groves 555b83d
fix: harden portable bootstrap paths
mark-groves defadf3
fix: complete bootstrap prerequisites
mark-groves 2a8f977
fix: align portable bootstrap boundaries
mark-groves 2bacfb2
fix: complete editor runtime profile
mark-groves 79345cc
fix: complete rust editor support
mark-groves File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,8 @@ | ||
| # Project Instructions | ||
|
|
||
| - When repairing symlinks for dotfiles in this project use @scripts/stow.sh | ||
|
|
||
| ## Git Workflow | ||
|
|
||
| - Never commit directly to main. Always create a branch, commit there, push, and open a PR if one doesn't already exist for that branch. | ||
| - Use conventional commit messages. Body should be in list format and not overly verbose. | ||
| - Use `scripts/stow.sh` for dotfile deployment and symlink repair. | ||
| - Run `scripts/check.sh` before committing implementation changes. | ||
| - Keep application configuration portable. Put OS package names and system | ||
| provisioning only under `ansible/`. | ||
| - Never commit secrets, resolved 1Password references, access tokens, or private | ||
| keys. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,67 +1,118 @@ | ||
| # dotfiles | ||
|
|
||
| Simple GNU Stow-based dotfiles with idempotent setup. | ||
| Portable application packages and configuration, deployed with GNU Stow. | ||
|
|
||
| ## Layout | ||
| This repository has a deliberately narrow job: | ||
|
|
||
| - Top-level directories (excluding `hosts`, `scripts`, `.git*`, and `.claude`) are base stow packages. | ||
| - Package contents mirror `$HOME` exactly. | ||
| - Per-host overrides live under `hosts/<hostname>/<package>` and are stowed after base packages. | ||
| 1. Track a small profile of applications and command-line tools. | ||
| 2. Map that profile to the current operating system's package names. | ||
| 3. Link portable application configuration into the user's home directory. | ||
|
|
||
| Current base packages: | ||
| Full Fedora workstation provisioning—including repositories, drivers, desktop | ||
| policy, and system tuning—belongs in a separate repository. | ||
|
|
||
| - `git` -> `~/.config/git/config` | ||
| - `starship` -> `~/.config/starship.toml` | ||
| ## Managed configuration | ||
|
|
||
| Example: | ||
| The default Stow deployment includes Git, Ghostty, Neovim/LazyVim, Bash, tmux, | ||
| and Starship. Package contents mirror paths relative to `$HOME`. The managed | ||
| Bash startup file loads additive fragments from `~/.bashrc.d`. If an existing | ||
| `~/.bashrc` already loads that directory, deployment preserves it; otherwise | ||
| Stow stops instead of replacing personal shell startup commands. | ||
|
|
||
| ```text | ||
| hosts/<hostname>/hypr/.config/hypr/... | ||
| The Fedora package profile installs the core command-line applications and | ||
| utilities available from the configured DNF repositories on mutable Fedora | ||
| installations. Ghostty and Starship configuration is tracked here, but their | ||
| third-party repository or binary setup is intentionally left to workstation | ||
| provisioning. | ||
|
|
||
| ## Bootstrap | ||
|
|
||
| Preview the package transaction and dotfile deployment: | ||
|
|
||
| ```bash | ||
| ./bootstrap.sh --dry-run | ||
| ``` | ||
|
|
||
| ## Usage | ||
| On a fresh machine without GNU Stow, this lists the dotfile packages that will | ||
| be deployed; after Stow is installed, the same command also checks exact link | ||
| changes and conflicts. | ||
|
|
||
| Check that GNU Stow is installed: | ||
| Install missing packages and deploy all configured dotfiles: | ||
|
|
||
| ```sh | ||
| stow --version | ||
| ```bash | ||
| ./bootstrap.sh | ||
| ``` | ||
|
|
||
| Dry-run first: | ||
| The package step is idempotent: the Fedora adapter checks installed RPMs and | ||
| invokes DNF only for missing packages. The Stow step always uses `--restow` so | ||
| it also repairs managed symlinks. | ||
|
|
||
| ```sh | ||
| ./scripts/stow.sh base -n | ||
| ./scripts/stow.sh host -n | ||
| Run either half independently: | ||
|
|
||
| ```bash | ||
| ./bootstrap.sh --packages-only | ||
| ./bootstrap.sh --dotfiles-only | ||
| ``` | ||
|
|
||
| Stow all base packages: | ||
| Never run these scripts with `sudo`; the Fedora adapter requests elevation only | ||
| for the DNF transaction. | ||
|
|
||
| ```sh | ||
| ./scripts/stow.sh base | ||
| ## Package tracking | ||
|
|
||
| List the portable identifiers and their Fedora package mapping: | ||
|
|
||
| ```bash | ||
| ./scripts/install-packages.sh --provider fedora --list | ||
| ``` | ||
|
|
||
| Stow host-specific packages for this machine: | ||
| The package data is split into: | ||
|
|
||
| ```sh | ||
| ./scripts/stow.sh host | ||
| - `ansible/packages/profile.txt`: provider-neutral application identifiers. | ||
| - `ansible/packages/providers/fedora.txt`: Fedora package names. | ||
| - `ansible/package-providers/fedora.sh`: Fedora detection, planning, and | ||
| installation behavior. | ||
|
|
||
| To add another tested provider, add its mapping and an executable adapter with | ||
| the same `detect`, `plan`, and `install` interface. The shared profile and | ||
| Stow packages do not change. | ||
|
|
||
| ## Dotfiles | ||
|
|
||
| Preview or deploy the explicit default package list: | ||
|
|
||
| ```bash | ||
| ./scripts/stow.sh base --dry-run | ||
| ./scripts/stow.sh base | ||
| ``` | ||
|
|
||
| Stow a specific host's packages: | ||
| Deploy selected portable packages: | ||
|
|
||
| ```sh | ||
| ./scripts/stow.sh host <hostname> | ||
| ```bash | ||
| ./scripts/stow.sh base git nvim | ||
| ``` | ||
|
|
||
| For low-level troubleshooting, this is the equivalent shape for one host | ||
| package: | ||
| Host-specific configuration remains available when needed: | ||
|
|
||
| ```sh | ||
| stow -t "$HOME" --restow --no-folding -d hosts/<hostname> <package> | ||
| ```bash | ||
| ./scripts/stow.sh host | ||
| ./scripts/stow.sh host nexus-unbound | ||
| ``` | ||
|
|
||
| ## Notes | ||
| `stow-packages.txt` is the source of truth for the default deployment. Keeping | ||
| that allowlist separate prevents infrastructure and documentation directories | ||
| from being mistaken for dotfile packages. | ||
|
|
||
| ## Git authentication and signing | ||
|
|
||
| - Keep only source-of-truth files here; avoid generated artifacts. | ||
| - Host packages should only contain overrides, so the base packages stay portable. | ||
| - Use `scripts/stow.sh` for normal refreshes and symlink repairs so base and | ||
| host package discovery stays consistent. | ||
| Ordinary commits are unsigned so unattended tools do not block on 1Password. | ||
| Use `git cis` for an explicitly signed personal commit. GitHub HTTPS credentials | ||
| are delegated to `gh auth git-credential`; credentials and private keys are | ||
| never stored in this repository. | ||
|
|
||
| ## Validation | ||
|
|
||
| Run the repository checks before committing implementation changes: | ||
|
|
||
| ```bash | ||
| ./scripts/check.sh | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| # Package manifests | ||
|
|
||
| This directory is intentionally limited to operating-system-specific package | ||
| data. `packages/profile.txt` contains stable application identifiers, while | ||
| `packages/providers/<provider>.txt` maps those identifiers to concrete package | ||
| names. | ||
|
|
||
| The package-provider adapters also live here because they perform the elevated | ||
| package transaction. The unprivileged dispatcher lives under `scripts/`. Full | ||
| system provisioning, repository configuration, drivers, and workstation policy | ||
| belong in the separate Fedora setup repository. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| #!/usr/bin/env bash | ||
| set -euo pipefail | ||
|
|
||
| die() { | ||
| printf 'Error: %s\n' "$*" >&2 | ||
| exit 1 | ||
| } | ||
|
|
||
| is_supported() { | ||
| [[ "$(uname -s)" == Linux ]] || return 1 | ||
| [[ -r /etc/os-release ]] || return 1 | ||
|
|
||
| ( | ||
| # shellcheck source=/dev/null | ||
| source /etc/os-release | ||
| [[ "${ID:-}" == fedora ]] | ||
| ) || return 1 | ||
|
|
||
| [[ ! -e /run/ostree-booted ]] || return 1 | ||
| command -v dnf > /dev/null 2>&1 && command -v rpm > /dev/null 2>&1 | ||
| } | ||
|
|
||
| collect_missing() { | ||
| missing_packages=() | ||
| local package | ||
| for package in "$@"; do | ||
| [[ "$package" =~ ^[A-Za-z0-9][A-Za-z0-9+_.:-]*$ ]] || | ||
| die "invalid Fedora package name: $package" | ||
| rpm -q -- "$package" > /dev/null 2>&1 || missing_packages+=("$package") | ||
| done | ||
| } | ||
|
|
||
| print_plan() { | ||
| collect_missing "$@" | ||
| if [[ ${#missing_packages[@]} -eq 0 ]]; then | ||
| printf 'All requested Fedora packages are already installed.\n' | ||
| return | ||
| fi | ||
|
|
||
| printf 'Would install %d missing Fedora package(s):\n' "${#missing_packages[@]}" | ||
| printf ' sudo dnf install --assumeyes' | ||
| printf ' %q' "${missing_packages[@]}" | ||
| printf '\n' | ||
| } | ||
|
|
||
| install_packages() { | ||
| collect_missing "$@" | ||
| if [[ ${#missing_packages[@]} -eq 0 ]]; then | ||
| printf 'All requested Fedora packages are already installed.\n' | ||
| return | ||
| fi | ||
|
|
||
| command -v sudo > /dev/null 2>&1 || die "sudo is required to install Fedora packages" | ||
| sudo dnf install --assumeyes "${missing_packages[@]}" | ||
| } | ||
|
|
||
| case "${1:-}" in | ||
| detect) | ||
| is_supported | ||
| ;; | ||
| plan) | ||
| shift | ||
| is_supported || die "this provider requires a mutable Fedora installation with dnf" | ||
| print_plan "$@" | ||
| ;; | ||
| install) | ||
| shift | ||
| is_supported || die "this provider requires a mutable Fedora installation with dnf" | ||
| install_packages "$@" | ||
| ;; | ||
| *) | ||
| die "provider usage: $(basename "$0") <detect|plan|install> [package ...]" | ||
| ;; | ||
| esac | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| # Stable application identifiers for the default portable command-line profile. | ||
| git | ||
| git-lfs | ||
| github-cli | ||
| c-compiler | ||
| openssh-client | ||
| curl | ||
| tree-sitter-cli | ||
| nodejs | ||
| npm | ||
| go | ||
| rust | ||
| cargo | ||
| rust-analyzer | ||
| neovim | ||
|
mark-groves marked this conversation as resolved.
|
||
| stow | ||
| tmux | ||
| direnv | ||
| ripgrep | ||
| fd | ||
| fzf | ||
| bat | ||
| jq | ||
| yq | ||
| unzip | ||
| shellcheck | ||
| shfmt | ||
| clipboard | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| # logical-id Fedora package name | ||
| git git | ||
| git-lfs git-lfs | ||
| github-cli gh | ||
| c-compiler gcc | ||
| openssh-client openssh-clients | ||
| curl curl | ||
| tree-sitter-cli tree-sitter-cli | ||
| nodejs nodejs22-bin | ||
| npm nodejs22-npm-bin | ||
| go golang | ||
| rust rust | ||
| cargo cargo | ||
| rust-analyzer rust-analyzer | ||
| neovim neovim | ||
| stow stow | ||
| tmux tmux | ||
| direnv direnv | ||
| ripgrep ripgrep | ||
| fd fd-find | ||
| fzf fzf | ||
| bat bat | ||
| jq jq | ||
| yq yq | ||
| unzip unzip | ||
| shellcheck ShellCheck | ||
| shfmt shfmt | ||
| clipboard wl-clipboard |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| #!/usr/bin/env bash | ||
| set -euo pipefail | ||
|
|
||
| usage() { | ||
| cat << 'EOF' | ||
| Usage: bootstrap.sh [options] | ||
|
|
||
| Install the portable package profile, then deploy its dotfiles. | ||
|
|
||
| Options: | ||
| -n, --dry-run Preview package and Stow changes | ||
| --provider NAME Override automatic package-provider detection | ||
| --packages-only Install packages without deploying dotfiles | ||
| --dotfiles-only Deploy dotfiles without installing packages | ||
| -h, --help Show this help message | ||
| EOF | ||
| } | ||
|
|
||
| die() { | ||
| printf 'Error: %s\n' "$*" >&2 | ||
| exit 1 | ||
| } | ||
|
|
||
| root="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | ||
| dry_run=false | ||
| packages_only=false | ||
| dotfiles_only=false | ||
| provider="" | ||
|
|
||
| while [[ $# -gt 0 ]]; do | ||
| case "$1" in | ||
| -n | --dry-run) dry_run=true ;; | ||
| --provider) | ||
| shift | ||
| [[ $# -gt 0 ]] || die "--provider requires a name" | ||
| provider="$1" | ||
| ;; | ||
| --provider=*) provider="${1#*=}" ;; | ||
| --packages-only) packages_only=true ;; | ||
| --dotfiles-only) dotfiles_only=true ;; | ||
| -h | --help) | ||
| usage | ||
| exit 0 | ||
| ;; | ||
| *) die "unknown option: $1" ;; | ||
| esac | ||
| shift | ||
| done | ||
|
|
||
| "$packages_only" && "$dotfiles_only" && | ||
| die "--packages-only and --dotfiles-only cannot be used together" | ||
| [[ ${EUID:-$(id -u)} -ne 0 ]] || die "run bootstrap as your normal user, not root" | ||
|
|
||
| if ! "$dotfiles_only"; then | ||
| package_args=() | ||
| "$dry_run" && package_args+=(--dry-run) | ||
| [[ -z "$provider" ]] || package_args+=(--provider "$provider") | ||
| "$root/scripts/install-packages.sh" "${package_args[@]}" | ||
| fi | ||
|
|
||
| if ! "$packages_only"; then | ||
| if "$dry_run" && ! command -v stow > /dev/null 2>&1; then | ||
| printf 'GNU Stow is not installed; the package plan above includes it.\n' | ||
| printf 'Would deploy these dotfile packages after installation:\n' | ||
| "$root/scripts/stow.sh" list | sed 's/^/ /' | ||
| else | ||
| stow_args=(base) | ||
| "$dry_run" && stow_args+=(--dry-run) | ||
| "$root/scripts/stow.sh" "${stow_args[@]}" | ||
| fi | ||
| fi |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| # Follow the desktop's light/dark appearance automatically. | ||
| theme = dark:Catppuccin Frappe,light:Catppuccin Latte | ||
|
|
||
| font-size = 12 | ||
| window-padding-x = 8 | ||
| window-padding-y = 6 | ||
| window-padding-balance = true | ||
|
|
||
| # Protect terminals that still have a running process. | ||
| confirm-close-surface = true |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.