diff --git a/.config/fish/config.fish b/.config/fish/config.fish index 1238ea5..8287482 100644 --- a/.config/fish/config.fish +++ b/.config/fish/config.fish @@ -38,7 +38,7 @@ # DOTFILES_DIR holds the template files; DOTFILES_BACKUP_DIR holds backups # and the last-update-check timestamp. -set -g DOTFILES_VERSION "1.0.1" +set -g DOTFILES_VERSION "1.0.2" # --- Remote repository (set this to enable auto-updates) -------------------- # Example: diff --git a/.config/fish/functions/dotfiles.fish b/.config/fish/functions/dotfiles.fish index c661b10..7ceb88c 100644 --- a/.config/fish/functions/dotfiles.fish +++ b/.config/fish/functions/dotfiles.fish @@ -514,15 +514,19 @@ function dotfiles_publish -d "Push local changes to the remote dotfiles repo" # ── Step 3: Copy local state into the clone ─────────────────────────── # These are the files and directories that make up the dotfiles repo. - set -l local_config "$__fish_config_dir/config.fish" - set -l local_funcs "$__fish_config_dir/functions/" - set -l local_tmpl "$DOTFILES_DIR/" - set -l local_confd "$__fish_config_dir/conf.d/" + set -l local_config "$__fish_config_dir/config.fish" + set -l local_funcs "$__fish_config_dir/functions/" + set -l local_tmpl "$DOTFILES_DIR/" + set -l local_confd "$__fish_config_dir/conf.d/" + set -l local_install "$__fish_config_dir/install.fish" + set -l local_readme "$__fish_config_dir/README.md" - set -l repo_config "$tmpdir/.config/fish/config.fish" - set -l repo_funcs "$tmpdir/.config/fish/functions/" - set -l repo_tmpl "$tmpdir/.config/fish/dotfiles/" - set -l repo_confd "$tmpdir/.config/fish/conf.d/" + set -l repo_config "$tmpdir/.config/fish/config.fish" + set -l repo_funcs "$tmpdir/.config/fish/functions/" + set -l repo_tmpl "$tmpdir/.config/fish/dotfiles/" + set -l repo_confd "$tmpdir/.config/fish/conf.d/" + set -l repo_install "$tmpdir/install.fish" + set -l repo_readme "$tmpdir/README.md" # Ensure target directories exist for d in (dirname "$repo_config") "$repo_funcs" "$repo_tmpl" "$repo_confd" @@ -539,6 +543,18 @@ function dotfiles_publish -d "Push local changes to the remote dotfiles repo" echo " dotfiles/" command cp -r "$local_confd." "$repo_confd" echo " conf.d/" + if test -f "$local_install" + command cp "$local_install" "$repo_install" + echo " install.fish" + else + echo " (no install.fish)" + end + if test -f "$local_readme" + command cp "$local_readme" "$repo_readme" + echo " README.md" + else + echo " (no README.md)" + end # ── Step 4: Prompt for version ───────────────────────────────────────── echo "" diff --git a/README.md b/README.md new file mode 100644 index 0000000..7db9e2c --- /dev/null +++ b/README.md @@ -0,0 +1,113 @@ +# Dotfiles Manager + +A self-managing Fish shell configuration that tracks its own version, can +automatically update itself from a Git repository, and manages standard +dotfiles (`~/.tmux.conf`, `~/.gitconfig`, etc.) from version-controlled +templates. + +## Quick Install + +```fish +curl -fsSL https://git.toomuchtaco.net/taco/dots/raw/branch/main/install.fish | fish +``` + +This one-liner clones the repository, installs everything to +`~/.config/fish/`, creates `~/.tmux.conf` and other dotfiles from templates, +and prints next steps. + +**Prerequisites:** Fish shell 3.2+, Git. + +## Commands + +| Command | Description | +|---|---| +| `dotfiles help` | Show all subcommands | +| `dotfiles version` | Display current version, repo URL, shell info | +| `dotfiles status [name ...]` | Checksum-compare installed files against templates | +| `dotfiles init [name ...]` | Create dotfiles from templates (backs up existing) | +| `dotfiles update [name ...]` | Re-apply template contents (backs up existing) | +| `dotfiles edit ` | Open `~/.` in `$EDITOR` | +| `dotfiles repo [url]` | Show or set the remote Git repository URL | +| `dotfiles sync` | Pull latest templates from remote and re-apply | +| `dotfiles publish` | Bump version, commit local changes, push to remote | + +### Examples + +```fish +dotfiles status # Check every managed file +dotfiles init gitconfig # Create only ~/.gitconfig +dotfiles edit tmux # Open ~/.tmux.conf in your editor +dotfiles repo # Show current remote URL +dotfiles sync # Pull updates and re-apply +dotfiles publish # Bump version, commit, push +``` + +## File Structure + +``` +~/.config/fish/ +├── config.fish Main entry point — version, PATH, interactive setup +├── install.fish Bootstrap/install script (also at repo root) +├── README.md This file +├── conf.d/ +│ └── aliases.fish Example: Git abbreviations, helper functions +├── functions/ +│ ├── dotfiles.fish The `dotfiles` command and all subcommands +│ └── gitea_key.fish Helper: generate SSH key for Gitea +└── dotfiles/ + ├── tmux.conf Template → ~/.tmux.conf + ├── gitconfig Template → ~/.gitconfig + └── config/ + └── starship.toml Template → ~/.config/starship.toml +``` + +## How It Works + +### Versioning + +A single `DOTFILES_VERSION` variable at the top of `config.fish` is the +source of truth. When you run `dotfiles publish`, you are prompted for a +new version (auto-suggested minor bump, or custom), and both the local +`config.fish` and the remote repository are updated. + +### Templates + +Each file in `~/.config/fish/dotfiles/` is a template. When you run +`dotfiles init`, templates are copied to `~/` with a leading dot prepended +(e.g. `tmux.conf` → `~/.tmux.conf`). Templates in subdirectories are +placed under `~/` preserving the path (e.g. `config/starship.toml` → +`~/.config/starship.toml`). + +To add a new dotfile, simply drop a template file into `dotfiles/` and run +`dotfiles init `. No registration needed. + +### Sync & Publish + +- **`dotfiles sync`** — clones the remote repo (shallow) and copies templates + into `dotfiles/`, then re-applies them. Pulls changes *down*. +- **`dotfiles publish`** — clones the remote repo (full), copies your local + `config.fish`, `functions/`, `dotfiles/`, and `conf.d/` into the clone, + prompts for a version bump, commits, and pushes. Sends changes *up*. + +## Extending + +- **Add a template:** create a file in `~/.config/fish/dotfiles/`. +- **Add shell config:** create a file in `~/.config/fish/conf.d/` (auto-sourced + by Fish in alphabetical order). +- **Add a function:** create a file in `~/.config/fish/functions/` (auto-loaded + by Fish when first called). + +## Custom Install + +Pass a different repository URL to the install script: + +```fish +curl -fsSL https://git.toomuchtaco.net/taco/dots/raw/branch/main/install.fish | fish -s -- https://github.com/you/your-dotfiles +``` + +Or clone manually and run the install script locally: + +```fish +git clone git@gitssh.toomuchtaco.net:taco/dots.git /tmp/dots +fish /tmp/dots/install.fish +``` diff --git a/install.fish b/install.fish new file mode 100644 index 0000000..b60e225 --- /dev/null +++ b/install.fish @@ -0,0 +1,220 @@ +#!/usr/bin/env fish +# +# install.fish — Dotfiles bootstrap script +# ============================================================================= +# +# Quick install (copy-paste this one-liner): +# +# curl -fsSL https://git.toomuchtaco.net/taco/dots/raw/branch/main/install.fish | fish +# +# What this script does: +# 1. Checks prerequisites: Fish shell, Git, and a few common tools +# 2. Clones the dotfiles repository (default: git@gitssh.toomuchtaco.net:taco/dots.git) +# 3. Replaces or updates ~/.config/fish/ with the contents of the repo +# 4. Sources the new config and runs `dotfiles init` to install templates +# (~/.tmux.conf, ~/.gitconfig, ~/.config/starship.toml, etc.) +# 5. Prints next steps +# +# You can pass a custom repo URL as the first argument: +# +# curl -fsSL https://git.toomuchtaco.net/taco/dots/raw/branch/main/install.fish | fish -s -- https://github.com/you/dotfiles +# +# ============================================================================= + +# ────────────────────────────────────────────────────────────────────────────── +# Configuration +# ────────────────────────────────────────────────────────────────────────────── + +# Default repository URL (overridable via first argument). +set -l REPO_URL "git@gitssh.toomuchtaco.net:taco/dots.git" +if set -q argv[1] + set REPO_URL "$argv[1]" +end + +set -l REPO_BRANCH "main" +set -l FISH_CONFIG_DIR "$HOME/.config/fish" + +# ANSI colour codes for pretty output. +set -l RST (set_color normal) +set -l GRN (set_color green) +set -l YLW (set_color yellow) +set -l RED (set_color red) +set -l BOLD (set_color --bold) + + +# ────────────────────────────────────────────────────────────────────────────── +# Helper: print a formatted step header +# ────────────────────────────────────────────────────────────────────────────── + +function _step -d "Print a step header" + echo "" + echo (set_color --bold)"[$argv[1]]$RST $argv[2..]" +end + + +# ────────────────────────────────────────────────────────────────────────────── +# 1. Prerequisites check +# ────────────────────────────────────────────────────────────────────────────── + +echo "" +echo "$BOLD━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━$RST" +echo "$BOLD Dotfiles Installer$RST" +echo "$BOLD━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━$RST" + +_step "1/5" "Checking prerequisites ..." + +# Fish itself — we're running in it, but just in case. +if not set -q FISH_VERSION + echo "$RED ERROR: Fish shell is required but not running.$RST" + echo " Install fish and re-run this script." + exit 1 +end +echo "$GRN ✓$RST Fish $FISH_VERSION" + +# Git +if not command -v git &>/dev/null + echo "$RED ERROR: Git is required but not installed.$RST" + echo " Install it with your package manager, then re-run." + exit 1 +end +echo "$GRN ✓$RST Git "(git --version | cut -d' ' -f3) + +# Optional but nice-to-have tools +for tool in tmux starship + if command -v $tool &>/dev/null + echo " ⬡ $tool detected (templates available)" + else + echo " ⬡ $tool not found (optional — templates exist but are skipped)" + end +end + + +# ────────────────────────────────────────────────────────────────────────────── +# 2. Fetch the repository +# ────────────────────────────────────────────────────────────────────────────── + +_step "2/5" "Fetching dotfiles from $REPO_URL ..." + +set -l tmpdir (mktemp -d) + +if command git clone --branch "$REPO_BRANCH" "$REPO_URL" "$tmpdir" 2>/dev/null + echo "$GRN ✓$RST Repository cloned." +else + # Clone failed — the repo might not exist yet, or this is a first install. + # Print the error and let the user decide. + echo "$YLW ⚠ Clone failed.$RST" + echo " This might mean:" + echo " - The repository doesn't exist yet" + echo " - SSH key is not loaded (ssh-add -l)" + echo " - No network access" + echo "" + echo " Attempting alternative clone (HTTPS fallback) ..." + + # Try HTTPS fallback. + set -l https_url (string replace "git@gitssh.toomuchtaco.net:" "https://git.toomuchtaco.net/" "$REPO_URL" | string replace ".git" "") + if command git clone --branch "$REPO_BRANCH" "$https_url" "$tmpdir" 2>/dev/null + echo "$GRN ✓$RST Repository cloned via HTTPS." + else + echo "$RED ERROR: All clone methods failed.$RST" + echo " Manually clone the repo and re-run this script:" + echo " git clone $REPO_URL $FISH_CONFIG_DIR" + command rm -rf "$tmpdir" + exit 1 + end +end + + +# ────────────────────────────────────────────────────────────────────────────── +# 3. Install files into ~/.config/fish/ +# ────────────────────────────────────────────────────────────────────────────── + +_step "3/5" "Installing files to $FISH_CONFIG_DIR ..." + +# Ensure the config directory exists. +mkdir -p "$FISH_CONFIG_DIR" + +# Back up any existing config before overwriting. +set -l backup_dir "$HOME/.config/fish.backup."(date +%s) +if test -d "$FISH_CONFIG_DIR" + and not test -L "$FISH_CONFIG_DIR" + and count "$FISH_CONFIG_DIR"/* &>/dev/null + command cp -r "$FISH_CONFIG_DIR" "$backup_dir" + echo " Existing config backed up to $backup_dir" +end + +# Copy the repo contents into place. +# The repo is expected to have this structure: +# install.fish (this script — at repo root) +# .config/fish/ (the fish config directory) +command cp -r "$tmpdir/.config/fish/." "$FISH_CONFIG_DIR/" + +# Also copy the install script itself and README to the config dir for +# future reference. +command cp "$tmpdir/install.fish" "$FISH_CONFIG_DIR/install.fish" 2>/dev/null +if test -f "$tmpdir/README.md" + command cp "$tmpdir/README.md" "$FISH_CONFIG_DIR/README.md" +end + +echo "$GRN ✓$RST Files installed." + + +# ────────────────────────────────────────────────────────────────────────────── +# 4. Initialise managed dotfiles +# ────────────────────────────────────────────────────────────────────────────── + +_step "4/5" "Initialising managed dotfiles (~/.tmux.conf, ~/.gitconfig, ...) ..." + +# Source the newly installed config so the `dotfiles` function is available. +source "$FISH_CONFIG_DIR/config.fish" + +# Load the dotfiles function file explicitly (auto-load won't fire until +# the function is first called in a non-sourced context). +if test -f "$FISH_CONFIG_DIR/functions/dotfiles.fish" + source "$FISH_CONFIG_DIR/functions/dotfiles.fish" +end + +# Persist the repo URL so `dotfiles publish` and `dotfiles sync` work. +if test -n "$REPO_URL" + dotfiles repo "$REPO_URL" 2>/dev/null +end + +# Run init for every managed template. +dotfiles init + +echo "$GRN ✓$RST Dotfiles initialised." +echo "" +echo " Installed templates:" +for f in (find "$DOTFILES_DIR" -type f | sort) + set -l name (string replace "$DOTFILES_DIR/" "" "$f") + echo " $(set_color green)•$(set_color normal) ~/.$name" +end + + +# ────────────────────────────────────────────────────────────────────────────── +# 5. Done — print next steps +# ────────────────────────────────────────────────────────────────────────────── + +_step "5/5" "Cleaning up ..." +command rm -rf "$tmpdir" +echo "$GRN ✓$RST Done." + +echo "" +echo "$BOLD━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━$RST" +echo "$GRN Installation complete!$RST" +echo "$BOLD━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━$RST" +echo "" +echo " $YLW""Next steps:$RST" +echo "" +echo " 1. Start a new fish shell or run: exec fish" +echo " 2. Check status of managed files: dotfiles status" +echo " 3. Edit a dotfile: dotfiles edit tmux" +echo " 4. Pull remote updates: dotfiles sync" +echo " 5. Push your changes: dotfiles publish" +echo "" +echo " $YLW""New to the dotfiles system?$RST" +echo " dotfiles help" +echo "" +echo " $YLW""Need to set your Git identity?$RST" +echo " dotfiles edit gitconfig" +echo " # Change the [user] section, then: dotfiles update gitconfig" +echo ""