dotfiles vCurrent version: v1.0.0 1.1.0
This commit is contained in:
63
.config/fish/conf.d/aliases.fish
Normal file
63
.config/fish/conf.d/aliases.fish
Normal file
@@ -0,0 +1,63 @@
|
||||
# =============================================================================
|
||||
# conf.d/aliases.fish — User aliases and abbreviations
|
||||
# =============================================================================
|
||||
#
|
||||
# Files in ~/.config/fish/conf.d/ are auto-sourced by Fish in alphabetical
|
||||
# order on every shell start. This is the recommended way to add modular
|
||||
# configuration snippets — add a new .fish file for each concern:
|
||||
#
|
||||
# conf.d/
|
||||
# ├── aliases.fish # Shortcuts
|
||||
# ├── env.fish # Environment variables
|
||||
# ├── prompt.fish # Custom prompt overrides
|
||||
# └── ... # Anything else
|
||||
#
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
# --- Abbreviations (expand after space/enter) --------------------------------
|
||||
# Fish abbreviations are like aliases but they expand inline as you type,
|
||||
# giving you a chance to edit before running the command.
|
||||
|
||||
abbr -a -- g git
|
||||
abbr -a -- ga 'git add'
|
||||
abbr -a -- gc 'git commit'
|
||||
abbr -a -- gp 'git push'
|
||||
abbr -a -- gl 'git log --oneline --graph --all'
|
||||
abbr -a -- gs 'git status -sb'
|
||||
abbr -a -- gd 'git diff'
|
||||
abbr -a -- gdc 'git diff --cached'
|
||||
abbr -a -- gco 'git checkout'
|
||||
abbr -a -- gcb 'git checkout -b'
|
||||
abbr -a -- gm 'git merge'
|
||||
abbr -a -- gr 'git rebase'
|
||||
abbr -a -- gcl 'git clone'
|
||||
|
||||
abbr -a -- ls 'ls -F --color=auto'
|
||||
abbr -a -- ll 'ls -l --color=auto'
|
||||
abbr -a -- la 'ls -la --color=auto'
|
||||
abbr -a -- tree 'tree -C'
|
||||
|
||||
abbr -a -- .. 'cd ..'
|
||||
abbr -a -- ... 'cd ../..'
|
||||
|
||||
abbr -a -- rmf 'rm -rf'
|
||||
|
||||
# --- Functions ----------------------------------------------------------------
|
||||
# For anything beyond a simple abbreviation, define a function.
|
||||
|
||||
function mkcd -d "Create a directory and cd into it"
|
||||
mkdir -p $argv && cd $argv
|
||||
end
|
||||
|
||||
function dotenv -d "Load a .env file into the environment"
|
||||
if test -f ".env"
|
||||
for line in (cat .env | string trim)
|
||||
set -l key (echo $line | cut -d= -f1)
|
||||
set -l val (echo $line | cut -d= -f2-)
|
||||
set -gx "$key" "$val"
|
||||
end
|
||||
echo "Loaded .env ($(wc -l < .env) variables)"
|
||||
else
|
||||
echo "No .env file found"
|
||||
end
|
||||
end
|
||||
143
.config/fish/config.fish
Normal file
143
.config/fish/config.fish
Normal file
@@ -0,0 +1,143 @@
|
||||
# =============================================================================
|
||||
# config.fish — Fish Shell Configuration & Dotfiles Manager
|
||||
# Version: 1.0.0
|
||||
# =============================================================================
|
||||
#
|
||||
# This file is the entry point for every Fish shell session. It handles:
|
||||
#
|
||||
# 1. BASIC SETUP — PATH, environment variables, editor
|
||||
# 2. VERSIONING — a single source-of-truth version number
|
||||
# 3. DOTFILES MANAGEMENT — create, update, and track ~/.tmux.conf and
|
||||
# other common config files through the `dotfiles` command
|
||||
# 4. SELF-UPDATE — pull the latest version of this very config from a
|
||||
# remote Git repository and re-apply all managed dotfiles
|
||||
# 5. EXTENSIBILITY — modular structure via conf.d/ and functions/ that
|
||||
# Fish auto-loads; no manual sourcing required
|
||||
#
|
||||
# The heavy lifting lives in ~/.config/fish/functions/dotfiles.fish, which
|
||||
# Fish loads automatically when you first run `dotfiles`. Templates for
|
||||
# managed dotfiles live in ~/.config/fish/dotfiles/.
|
||||
#
|
||||
# ── Quick start ────────────────────────────────────────────────────────────
|
||||
#
|
||||
# dotfiles help # Show all subcommands
|
||||
# dotfiles version # Show the current version of this framework
|
||||
# dotfiles status # Compare managed dotfiles against their templates
|
||||
# dotfiles init # Create ~/.tmux.conf, ~/.gitconfig, etc.
|
||||
# dotfiles sync # Git-pull updates and re-apply everything
|
||||
# dotfiles publish # Bump version, commit local changes, push to repo
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# SECTION 1 — Version & Core Constants
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
#
|
||||
# These values form the single source of truth for the entire framework.
|
||||
# DOTFILES_DIR holds the template files; DOTFILES_BACKUP_DIR holds backups
|
||||
# and the last-update-check timestamp.
|
||||
|
||||
set -g DOTFILES_VERSION "Current version: v1.0.0 1.1.0"
|
||||
|
||||
# --- Remote repository (set this to enable auto-updates) --------------------
|
||||
# Example:
|
||||
# dotfiles repo https://github.com/yourname/dotfiles
|
||||
#
|
||||
# Until you set this, `dotfiles sync` will show a reminder but won't fail.
|
||||
set -gx DOTFILES_REPO_URL ""
|
||||
set -gx DOTFILES_REPO_BRANCH "main"
|
||||
|
||||
# --- Paths ------------------------------------------------------------------
|
||||
# __fish_config_dir is a built-in Fish variable that points to
|
||||
# ~/.config/fish — we use it instead of hard-coding the path.
|
||||
set -gx DOTFILES_DIR "$__fish_config_dir/dotfiles"
|
||||
set -gx DOTFILES_BACKUP_DIR "$__fish_config_dir/backups"
|
||||
|
||||
# --- Auto-update interval ---------------------------------------------------
|
||||
# How often (in days) to remind you that updates are available when a new
|
||||
# interactive shell starts. Set to 0 to disable the nag.
|
||||
set -g DOTFILES_AUTO_UPDATE_INTERVAL_DAYS 7
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
# Version stamp embedded in the file itself for quick verification.
|
||||
# When you run `dotfiles version`, it reads this constant — no external file
|
||||
# to keep in sync.
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# SECTION 2 — PATH & Environment
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
# fish_add_path is idempotent — it won't add a directory that's already on PATH.
|
||||
fish_add_path "$HOME/.local/bin"
|
||||
fish_add_path "$HOME/bin"
|
||||
|
||||
# Preferred editor (used by `dotfiles edit` among other things).
|
||||
set -q EDITOR; or set -gx EDITOR "nano"
|
||||
|
||||
# Preferred file pager.
|
||||
set -q PAGER; or set -gx PAGER "less"
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# SECTION 3 — Dotfiles Infrastructure
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
# Create the required directories on every login so they are always present
|
||||
# even when the repo is cloned fresh.
|
||||
for dir in "$DOTFILES_DIR" "$DOTFILES_BACKUP_DIR"
|
||||
if not test -d "$dir"
|
||||
mkdir -p "$dir"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# SECTION 4 — Interactive Session: Prompt, Welcome, Auto-Update Nag
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
#
|
||||
# Everything below this guard runs only in interactive (i.e. human-facing)
|
||||
# shells, never in scripts.
|
||||
|
||||
if status is-interactive
|
||||
|
||||
# ── 4a. Welcome message (shown once per login) ────────────────────────
|
||||
# Set DOTFILES_SUPPRESS_WELCOME=1 in your environment to silence this.
|
||||
if not set -q DOTFILES_SUPPRESS_WELCOME
|
||||
echo "fish: dotfiles v$DOTFILES_VERSION — run 'dotfiles help' for commands"
|
||||
end
|
||||
|
||||
# ── 4b. Periodic auto-update reminder ─────────────────────────────────
|
||||
# Once every DOTFILES_AUTO_UPDATE_INTERVAL_DAYS, print a one-line
|
||||
# reminder that updates may be available. The actual update is
|
||||
# performed via `dotfiles sync` (manual, so you control the timing).
|
||||
if test "$DOTFILES_AUTO_UPDATE_INTERVAL_DAYS" -gt 0
|
||||
set -l last_check_file "$DOTFILES_BACKUP_DIR/.last_update_check"
|
||||
|
||||
# Determine days since last check
|
||||
set -l days_ago 999
|
||||
if test -f "$last_check_file"
|
||||
set -l last_ts (command cat "$last_check_file")
|
||||
set -l now_ts (date +%s)
|
||||
# Use integer arithmetic: 86400 seconds in a day
|
||||
set -l elapsed (math "$now_ts - $last_ts")
|
||||
if test "$elapsed" -ge 0
|
||||
set days_ago (math "$elapsed / 86400")
|
||||
end
|
||||
end
|
||||
|
||||
if test "$days_ago" -ge "$DOTFILES_AUTO_UPDATE_INTERVAL_DAYS"
|
||||
echo "tip: run 'dotfiles sync' to check for dotfiles updates"
|
||||
command date +%s >"$last_check_file"
|
||||
end
|
||||
end
|
||||
|
||||
# ── 4c. Prompt ─────────────────────────────────────────────────────────
|
||||
# No custom fish_prompt defined here — Fish uses its built-in default
|
||||
# which shows user@host in color, the working directory, git status,
|
||||
# and pipestatus. To customise, define your own fish_prompt function
|
||||
# in ~/.config/fish/functions/fish_prompt.fish.
|
||||
|
||||
end # status is-interactive
|
||||
80
.config/fish/dotfiles/config/starship.toml
Normal file
80
.config/fish/dotfiles/config/starship.toml
Normal file
@@ -0,0 +1,80 @@
|
||||
# =============================================================================
|
||||
# ~/.config/starship.toml — Starship prompt configuration
|
||||
# Managed by dotfiles ( ~/.config/fish/config.fish )
|
||||
# Manual changes will be overwritten when `dotfiles update` is run.
|
||||
# To customise, edit this template at:
|
||||
# ~/.config/fish/dotfiles/starship.toml
|
||||
# then run `dotfiles update starship`.
|
||||
# =============================================================================
|
||||
|
||||
# ── Appearance ───────────────────────────────────────────────────────────────
|
||||
format = """
|
||||
[░▒▓](#a6adc8)\
|
||||
$os\
|
||||
$username\
|
||||
$hostname\
|
||||
$directory\
|
||||
$git_branch\
|
||||
$git_status\
|
||||
$git_commit\
|
||||
$git_state\
|
||||
$nodejs\
|
||||
$python\
|
||||
$rust\
|
||||
$c\
|
||||
$docker_context\
|
||||
$cmd_duration\
|
||||
$line_break\
|
||||
$character"""
|
||||
|
||||
right_format = """$time"""
|
||||
|
||||
# ── Modules ──────────────────────────────────────────────────────────────────
|
||||
[character]
|
||||
success_symbol = "[▶](bold green)"
|
||||
error_symbol = "[▶](bold red)"
|
||||
|
||||
[directory]
|
||||
truncation_length = 3
|
||||
truncate_to_repo = true
|
||||
|
||||
[git_branch]
|
||||
format = " on [$branch](bold purple)"
|
||||
truncation_length = 20
|
||||
|
||||
[git_status]
|
||||
conflicted = "🏳"
|
||||
ahead = "⇡\${count}"
|
||||
behind = "⇣\${count}"
|
||||
diverged = "⇕\${ahead_count}⇣\${behind_count}"
|
||||
stashed = "📦"
|
||||
renamed = "📝"
|
||||
|
||||
[nodejs]
|
||||
format = "via [⬢ $version](bold green)"
|
||||
|
||||
[python]
|
||||
format = "via [🐍 $version](bold yellow)"
|
||||
|
||||
[rust]
|
||||
format = "via [🦀 $version](bold red)"
|
||||
|
||||
[cmd_duration]
|
||||
show_milliseconds = true
|
||||
min_time = 500
|
||||
|
||||
[time]
|
||||
disabled = false
|
||||
format = "🕙 $time"
|
||||
time_format = "%H:%M"
|
||||
style = "bold bright-white"
|
||||
|
||||
[os]
|
||||
disabled = false
|
||||
style = "bold white"
|
||||
format = "[$symbol]"
|
||||
|
||||
[os.symbols]
|
||||
windows = "🪟"
|
||||
macos = "🍎"
|
||||
linux = "🐧"
|
||||
62
.config/fish/dotfiles/gitconfig
Normal file
62
.config/fish/dotfiles/gitconfig
Normal file
@@ -0,0 +1,62 @@
|
||||
; =============================================================================
|
||||
; ~/.gitconfig — Git configuration
|
||||
; Managed by dotfiles ( ~/.config/fish/config.fish )
|
||||
; Manual changes will be overwritten when `dotfiles update` is run.
|
||||
; To customise, edit this template at:
|
||||
; ~/.config/fish/dotfiles/gitconfig
|
||||
; then run `dotfiles update gitconfig`.
|
||||
; =============================================================================
|
||||
|
||||
[user]
|
||||
; ── IMPORTANT: set your real name and email ──
|
||||
name = Your Name
|
||||
email = you@example.com
|
||||
|
||||
[init]
|
||||
defaultBranch = main
|
||||
|
||||
[core]
|
||||
editor = nano
|
||||
pager = less
|
||||
autocrlf = input
|
||||
whitespace = trailing-space,space-before-tab
|
||||
excludesfile = ~/.gitignore
|
||||
|
||||
[color]
|
||||
ui = auto
|
||||
|
||||
[color "branch"]
|
||||
current = yellow bold
|
||||
local = green
|
||||
remote = cyan
|
||||
|
||||
[color "diff"]
|
||||
meta = yellow bold
|
||||
frag = magenta bold
|
||||
old = red bold
|
||||
new = green bold
|
||||
|
||||
[color "status"]
|
||||
added = green
|
||||
changed = yellow
|
||||
untracked = red
|
||||
|
||||
[push]
|
||||
default = simple
|
||||
autoSetupRemote = true
|
||||
|
||||
[fetch]
|
||||
prune = true
|
||||
|
||||
[merge]
|
||||
conflictstyle = zdiff3
|
||||
|
||||
[rebase]
|
||||
autosquash = true
|
||||
autoStash = true
|
||||
|
||||
[diff]
|
||||
tool = vimdiff
|
||||
|
||||
[help]
|
||||
autocorrect = 10
|
||||
66
.config/fish/dotfiles/tmux.conf
Normal file
66
.config/fish/dotfiles/tmux.conf
Normal file
@@ -0,0 +1,66 @@
|
||||
# =============================================================================
|
||||
# ~/.tmux.conf — tmux configuration
|
||||
# Managed by dotfiles ( ~/.config/fish/config.fish )
|
||||
# Manual changes will be overwritten when `dotfiles update` is run.
|
||||
# To customise, edit this template at:
|
||||
# ~/.config/fish/dotfiles/tmux.conf
|
||||
# then run `dotfiles update tmux`.
|
||||
# =============================================================================
|
||||
|
||||
# ── Base settings ────────────────────────────────────────────────────────────
|
||||
set -g default-terminal "tmux-256color"
|
||||
set -ga terminal-overrides ",*256col*:Tc"
|
||||
set -g escape-time 0
|
||||
set -g history-limit 50000
|
||||
set -g mouse on
|
||||
set -g focus-events on
|
||||
|
||||
# ── Prefix key (unbind C-b, rebind to C-a for easier reach) ─────────────────
|
||||
unbind C-b
|
||||
set -g prefix C-a
|
||||
bind C-a send-prefix
|
||||
|
||||
# ── Easy reload ──────────────────────────────────────────────────────────────
|
||||
bind r source-file ~/.tmux.conf \; display " tmux.conf reloaded"
|
||||
|
||||
# ── Splitting ────────────────────────────────────────────────────────────────
|
||||
bind | split-window -h
|
||||
bind - split-window -v
|
||||
|
||||
# ── Vim-style pane navigation ────────────────────────────────────────────────
|
||||
bind h select-pane -L
|
||||
bind j select-pane -D
|
||||
bind k select-pane -U
|
||||
bind l select-pane -R
|
||||
|
||||
# ── Resize panes with arrow keys ─────────────────────────────────────────────
|
||||
bind -r Left resize-pane -L 2
|
||||
bind -r Right resize-pane -R 2
|
||||
bind -r Down resize-pane -D 2
|
||||
bind -r Up resize-pane -U 2
|
||||
|
||||
# ── Window management ────────────────────────────────────────────────────────
|
||||
bind c new-window -c "#{pane_current_path}"
|
||||
bind , command-prompt -I "#W" { rename-window -- "%%" }
|
||||
bind w choose-tree -w
|
||||
|
||||
# ── Status bar ───────────────────────────────────────────────────────────────
|
||||
set -g status-interval 5
|
||||
set -g status-position top
|
||||
set -g status-style "bg=#1e1e2e,fg=#cdd6f4"
|
||||
|
||||
set -g status-left " #[fg=#89b4fa]#S #[fg=#585b70]│"
|
||||
set -g status-left-length 40
|
||||
set -g status-right "#[fg=#585b70]│ #[fg=#a6e3a1]%Y-%m-%d #[fg=#fab387]%H:%M "
|
||||
set -g status-right-length 60
|
||||
|
||||
setw -g window-status-current-style "fg=#89b4fa,bold"
|
||||
setw -g window-status-style "fg=#6c7086"
|
||||
setw -g window-status-format " #I:#W "
|
||||
setw -g window-status-current-format " #I:#W "
|
||||
setw -g window-status-separator " "
|
||||
|
||||
# ── Notifications ────────────────────────────────────────────────────────────
|
||||
set -g bell-action any
|
||||
setw -g monitor-activity on
|
||||
set -g visual-activity off
|
||||
752
.config/fish/functions/dotfiles.fish
Normal file
752
.config/fish/functions/dotfiles.fish
Normal file
@@ -0,0 +1,752 @@
|
||||
# =============================================================================
|
||||
# functions/dotfiles.fish — Dotfiles Manager
|
||||
# =============================================================================
|
||||
#
|
||||
# This file is auto-loaded by Fish when you first run `dotfiles`. It defines
|
||||
# the `dotfiles` command and all its subcommands. The design is deliberately
|
||||
# monolithic (one file) so that the whole system is easy to fork, audit, and
|
||||
# copy around — yet each subcommand is a separate named function for clarity.
|
||||
#
|
||||
# ── How it works ────────────────────────────────────────────────────────────
|
||||
#
|
||||
# 1. Template files live in $DOTFILES_DIR (default: ~/.config/fish/dotfiles/).
|
||||
# Each template is named after the dotfile it manages, e.g.:
|
||||
# dotfiles/tmux.conf → installed to ~/.tmux.conf
|
||||
# dotfiles/gitconfig → installed to ~/.gitconfig
|
||||
#
|
||||
# 2. When you run `dotfiles init`, templates are copied to $HOME with a
|
||||
# leading dot prepended. Existing files are backed up.
|
||||
#
|
||||
# 3. When you run `dotfiles update`, the template is re-copied (with backup).
|
||||
#
|
||||
# 4. `dotfiles sync` also pulls the latest templates from the remote repo
|
||||
# before re-applying them.
|
||||
#
|
||||
# 5. `dotfiles status` compares the installed file against its template using
|
||||
# a checksum so you can see at a glance what's drifted.
|
||||
#
|
||||
# ── Convention for template filenames ──────────────────────────────────────
|
||||
#
|
||||
# Template name Target file Typical use
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
# tmux.conf ~/.tmux.conf tmux terminal multiplexer
|
||||
# gitconfig ~/.gitconfig Git version control
|
||||
# gitignore ~/.gitignore Global Git ignore rules
|
||||
# starship.toml ~/.config/starship.toml Starship prompt
|
||||
# bashrc ~/.bashrc Bash (when Fish is absent)
|
||||
#
|
||||
# To add support for a new file, simply drop a template in $DOTFILES_DIR
|
||||
# and run `dotfiles init <name>`. That's it — no registration needed.
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# dotfiles — main dispatcher
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
#
|
||||
# Usage: dotfiles <command> [arguments ...]
|
||||
#
|
||||
# Run `dotfiles help` (or just `dotfiles`) for a full list of subcommands.
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
function dotfiles -d "Manage dotfiles and self-update system"
|
||||
# ── No argument (or explicit help) → show help ─────────────────────────
|
||||
set -l cmd $argv[1]
|
||||
switch "$cmd"
|
||||
case "" help --help -h
|
||||
dotfiles_help
|
||||
|
||||
case version --version -v
|
||||
dotfiles_version
|
||||
|
||||
case status
|
||||
dotfiles_status
|
||||
|
||||
case init
|
||||
# `dotfiles init` → init all
|
||||
# `dotfiles init foo bar` → init only foo and bar
|
||||
dotfiles_init $argv[2..]
|
||||
|
||||
case update
|
||||
# Same argument pattern as init.
|
||||
dotfiles_update $argv[2..]
|
||||
|
||||
case edit
|
||||
if set -q argv[2]
|
||||
dotfiles_edit $argv[2]
|
||||
else
|
||||
echo "Usage: dotfiles edit <name>"
|
||||
echo "Example: dotfiles edit tmux"
|
||||
return 1
|
||||
end
|
||||
|
||||
case repo
|
||||
# `dotfiles repo` → show current URL
|
||||
# `dotfiles repo <url>` → set URL
|
||||
dotfiles_repo $argv[2]
|
||||
|
||||
case sync
|
||||
dotfiles_sync
|
||||
|
||||
case publish
|
||||
dotfiles_publish
|
||||
|
||||
case '*'
|
||||
echo "dotfiles: unknown command '$cmd'"
|
||||
echo "Run 'dotfiles help' for usage."
|
||||
return 1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# dotfiles_help
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
function dotfiles_help -d "Show the dotfiles help text"
|
||||
echo "Usage: dotfiles <command> [arguments]"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " help Show this help message"
|
||||
echo " version Display version and system info"
|
||||
echo " status [name ...] Show checksum diff for dotfiles"
|
||||
echo " init [name ...] Create dotfiles from templates"
|
||||
echo " update [name ...] Re-apply templates (with backup)"
|
||||
echo " edit <name> Open ~/.<name> in \$EDITOR"
|
||||
echo " repo [url] Show or set the remote repo URL"
|
||||
echo " sync Git-pull and re-apply everything"
|
||||
echo " publish Push local changes to remote repo"
|
||||
echo " with a version bump"
|
||||
echo ""
|
||||
echo "Available managed files:"
|
||||
_dotfiles_list_managed | string collect
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " dotfiles init Create ~/.tmux.conf, ~/.gitconfig, …"
|
||||
echo " dotfiles init gitconfig Create only ~/.gitconfig"
|
||||
echo " dotfiles status Check every managed file for drift"
|
||||
echo " dotfiles sync Pull + re-apply everything"
|
||||
echo " dotfiles publish Bump version, commit, and push"
|
||||
echo ""
|
||||
echo "The DOTFILES_REPO_URL environment variable controls where sync"
|
||||
echo "and publish operate. Set it with: dotfiles repo <url>"
|
||||
end
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# dotfiles_version
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
function dotfiles_version -d "Show version information"
|
||||
echo "dotfiles v$DOTFILES_VERSION"
|
||||
|
||||
if test -n "$DOTFILES_REPO_URL"
|
||||
echo "Repository : $DOTFILES_REPO_URL ($DOTFILES_REPO_BRANCH)"
|
||||
else
|
||||
echo "Repository : (none set — run 'dotfiles repo <url>' to enable sync)"
|
||||
end
|
||||
|
||||
echo "Shell : Fish $FISH_VERSION"
|
||||
echo "Config dir : $__fish_config_dir"
|
||||
echo "Templates : $DOTFILES_DIR"
|
||||
echo "Backups : $DOTFILES_BACKUP_DIR"
|
||||
end
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# dotfiles_status
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
#
|
||||
# Compares each installed dotfile against its template using MD5 checksums.
|
||||
# Shows three states:
|
||||
# ✓ up to date — template and installed file are identical
|
||||
# ✗ drifted — installed file differs from the template
|
||||
# - not installed — template exists but target has not been created
|
||||
#
|
||||
# If you give one or more names (e.g. `dotfiles status tmux`) only those
|
||||
# are checked.
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
function dotfiles_status -d "Show checksum diff for managed dotfiles"
|
||||
set -l targets (_dotfiles_resolve_targets $argv)
|
||||
|
||||
echo "Checking managed dotfiles ..."
|
||||
echo ""
|
||||
|
||||
set -l any_drift 0
|
||||
|
||||
for name in $targets
|
||||
set -l tmpl (_dotfiles_template_path "$name")
|
||||
set -l dest (_dotfiles_target_path "$name")
|
||||
|
||||
if not test -f "$tmpl"
|
||||
echo " - $name (no template at $tmpl)"
|
||||
continue
|
||||
end
|
||||
|
||||
if not test -f "$dest"
|
||||
echo " - $name (not installed — run 'dotfiles init $name')"
|
||||
continue
|
||||
end
|
||||
|
||||
# Compare checksums
|
||||
set -l tmpl_md5 (md5sum "$tmpl" | cut -d' ' -f1)
|
||||
set -l dest_md5 (md5sum "$dest" | cut -d' ' -f1)
|
||||
|
||||
if test "$tmpl_md5" = "$dest_md5"
|
||||
echo " ✓ $name (up to date)"
|
||||
else
|
||||
echo " ✗ $name (drifted — run 'dotfiles update $name')"
|
||||
set any_drift 1
|
||||
end
|
||||
end
|
||||
|
||||
echo ""
|
||||
if test "$any_drift" -eq 0
|
||||
echo "All managed files are up to date."
|
||||
else
|
||||
echo "Some files have drifted. Run 'dotfiles update' to re-apply templates."
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# dotfiles_init — create dotfiles from templates
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
#
|
||||
# Creates ~/.<name> from the template at $DOTFILES_DIR/<name>.
|
||||
# Existing files are backed up before being overwritten.
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
function dotfiles_init -d "Create dotfiles from templates"
|
||||
set -l targets (_dotfiles_resolve_targets $argv)
|
||||
|
||||
if set -q targets[1]
|
||||
echo "Initialising dotfiles ..."
|
||||
else
|
||||
echo "No managed files found in $DOTFILES_DIR"
|
||||
echo "Add a template file there and run 'dotfiles init <name>'."
|
||||
return 0
|
||||
end
|
||||
|
||||
set -l did_anything 0
|
||||
|
||||
for name in $targets
|
||||
set -l tmpl (_dotfiles_template_path "$name")
|
||||
set -l dest (_dotfiles_target_path "$name")
|
||||
|
||||
if not test -f "$tmpl"
|
||||
echo " skipping $name — no template at $tmpl"
|
||||
continue
|
||||
end
|
||||
|
||||
# Backup any existing file
|
||||
if test -f "$dest"
|
||||
set -l backup "$DOTFILES_BACKUP_DIR/$name"
|
||||
set -l backup_parent (dirname "$backup")
|
||||
if not test -d "$backup_parent"
|
||||
mkdir -p "$backup_parent"
|
||||
end
|
||||
command cp "$dest" "$backup"
|
||||
echo " backed up $dest → $backup"
|
||||
end
|
||||
|
||||
# Create parent directory if needed (e.g. ~/.config/starship.toml)
|
||||
set -l parent (dirname "$dest")
|
||||
if not test -d "$parent"
|
||||
mkdir -p "$parent"
|
||||
end
|
||||
|
||||
command cp "$tmpl" "$dest"
|
||||
echo " created $dest"
|
||||
set did_anything 1
|
||||
end
|
||||
|
||||
if test "$did_anything" -eq 0
|
||||
echo "Nothing was created."
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# dotfiles_update — re-apply templates (with backup)
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
#
|
||||
# Identical to `dotfiles init` except it prints "updated" rather than
|
||||
# "created". This distinction is useful when scripting or checking logs.
|
||||
# (Technically the behaviour is the same — backup + copy — but the user
|
||||
# intent is different.)
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
function dotfiles_update -d "Re-apply template to existing dotfile"
|
||||
set -l targets (_dotfiles_resolve_targets $argv)
|
||||
|
||||
if not set -q targets[1]
|
||||
echo "No managed files found in $DOTFILES_DIR"
|
||||
return 0
|
||||
end
|
||||
|
||||
for name in $targets
|
||||
set -l tmpl (_dotfiles_template_path "$name")
|
||||
set -l dest (_dotfiles_target_path "$name")
|
||||
|
||||
if not test -f "$tmpl"
|
||||
echo " skipping $name — no template at $tmpl"
|
||||
continue
|
||||
end
|
||||
|
||||
# Backup existing file
|
||||
if test -f "$dest"
|
||||
set -l backup "$DOTFILES_BACKUP_DIR/$name"
|
||||
set -l backup_parent (dirname "$backup")
|
||||
if not test -d "$backup_parent"
|
||||
mkdir -p "$backup_parent"
|
||||
end
|
||||
command cp "$dest" "$backup"
|
||||
echo " backed up $dest → $backup"
|
||||
end
|
||||
|
||||
set -l parent (dirname "$dest")
|
||||
if not test -d "$parent"
|
||||
mkdir -p "$parent"
|
||||
end
|
||||
|
||||
command cp "$tmpl" "$dest"
|
||||
echo " updated $dest"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# dotfiles_edit — open a dotfile in $EDITOR
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
#
|
||||
# Opens ~/.<name> for editing. If the file doesn't exist yet, you can
|
||||
# initialise it from the template first with `dotfiles init <name>`.
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
function dotfiles_edit -d "Open a dotfile for editing"
|
||||
set -l name $argv[1]
|
||||
set -l dest (_dotfiles_target_path "$name")
|
||||
|
||||
if not test -f "$dest"
|
||||
echo "File $dest does not exist."
|
||||
echo "Create it first: dotfiles init $name"
|
||||
return 1
|
||||
end
|
||||
|
||||
# $EDITOR is set in config.fish; fall back to nano if unset
|
||||
$EDITOR "$dest"
|
||||
end
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# dotfiles_repo — show or set the remote repository URL
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
#
|
||||
# The URL is stored persistently in a small text file under the backup dir
|
||||
# so it survives shell restarts. It is also loaded into the environment
|
||||
# variable DOTFILES_REPO_URL by config.fish if the file exists.
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
function dotfiles_repo -d "Show or set the remote repository URL"
|
||||
set -l repo_file "$DOTFILES_BACKUP_DIR/.repo_url"
|
||||
set -l url $argv[1]
|
||||
|
||||
if set -q url[1]
|
||||
# ── Set the URL ────────────────────────────────────────────────────
|
||||
echo "$url" >"$repo_file"
|
||||
set -gx DOTFILES_REPO_URL "$url"
|
||||
echo "Repository URL set to: $url"
|
||||
else
|
||||
# ── Show the current URL ──────────────────────────────────────────
|
||||
if test -f "$repo_file"
|
||||
set -l stored_url (command cat "$repo_file")
|
||||
echo "$stored_url"
|
||||
else if test -n "$DOTFILES_REPO_URL"
|
||||
echo "$DOTFILES_REPO_URL"
|
||||
else
|
||||
echo "(no repository configured)"
|
||||
echo ""
|
||||
echo "Run: dotfiles repo https://github.com/yourname/dotfiles"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Load persisted repo URL into the environment on shell start.
|
||||
# This runs when the function file is sourced, so it's evaluated once per
|
||||
# session (before the first `dotfiles` invocation).
|
||||
set -l repo_file "$DOTFILES_BACKUP_DIR/.repo_url"
|
||||
if test -f "$repo_file"
|
||||
set -gx DOTFILES_REPO_URL (command cat "$repo_file")
|
||||
end
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# dotfiles_sync — full sync: git pull + update all dotfiles
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
#
|
||||
# This is the "self-update" mechanism. It:
|
||||
#
|
||||
# 1. Checks that DOTFILES_REPO_URL is set
|
||||
# 2. Clones the repo into a temporary directory (or pulls if already cloned)
|
||||
# 3. Copies the templates from the cloned repo into DOTFILES_DIR
|
||||
# 4. Re-applies every template to its target location
|
||||
#
|
||||
# The config.fish itself is NOT overwritten — that would require a shell
|
||||
# restart to take effect. Instead, sync copies the *templates* from the
|
||||
# upstream repo, then re-applies them. To update the framework logic
|
||||
# itself (the *.fish files), run the update command shown after sync.
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
function dotfiles_sync -d "Pull remote updates and re-apply all dotfiles"
|
||||
# ── Step 1: Ensure we have a remote URL ────────────────────────────────
|
||||
if test -z "$DOTFILES_REPO_URL"
|
||||
echo "No repository URL configured."
|
||||
echo "Set one with: dotfiles repo https://github.com/yourname/dotfiles"
|
||||
return 1
|
||||
end
|
||||
|
||||
echo "Syncing from $DOTFILES_REPO_URL ($DOTFILES_REPO_BRANCH) ..."
|
||||
echo ""
|
||||
|
||||
# ── Step 2: Clone/pull into a temporary directory ──────────────────────
|
||||
set -l tmpdir (mktemp -d)
|
||||
echo "Cloning into $tmpdir ..."
|
||||
|
||||
if command git clone --branch "$DOTFILES_REPO_BRANCH" --depth 1 "$DOTFILES_REPO_URL" "$tmpdir"
|
||||
echo "Clone successful."
|
||||
else
|
||||
echo "Error: failed to clone repository."
|
||||
command rm -rf "$tmpdir"
|
||||
return 1
|
||||
end
|
||||
|
||||
# ── Step 3: Copy upstream templates into our local DOTFILES_DIR ────────
|
||||
set -l upstream_templates "$tmpdir/.config/fish/dotfiles"
|
||||
if test -d "$upstream_templates"
|
||||
echo ""
|
||||
echo "Updating templates from upstream ..."
|
||||
# Copy recursively to handle subdirectory templates (e.g. config/).
|
||||
# We use cp -r and then clean up the destination to only keep files.
|
||||
command cp -r "$upstream_templates/." "$DOTFILES_DIR/"
|
||||
echo " templates synced from upstream"
|
||||
else
|
||||
echo "Warning: no dotfiles/ directory found in the upstream repo."
|
||||
echo "Expected at: $upstream_templates"
|
||||
end
|
||||
|
||||
# ── Step 4: Re-apply all templates ────────────────────────────────────
|
||||
echo ""
|
||||
echo "Re-applying dotfiles ..."
|
||||
dotfiles_update
|
||||
|
||||
# ── Step 5: Clean up ───────────────────────────────────────────────────
|
||||
command rm -rf "$tmpdir"
|
||||
echo ""
|
||||
echo "Sync complete."
|
||||
echo ""
|
||||
echo "NOTE: framework updates (config.fish, functions/) require a manual"
|
||||
echo "copy and shell restart. To apply them:"
|
||||
echo ""
|
||||
echo " git clone $DOTFILES_REPO_URL /tmp/dotfiles-update"
|
||||
echo " cp -r /tmp/dotfiles-update/.config/fish/config.fish ~/.config/fish/"
|
||||
echo " cp -r /tmp/dotfiles-update/.config/fish/functions/ ~/.config/fish/"
|
||||
echo " exec fish"
|
||||
end
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# dotfiles_publish — push local changes to the remote repo
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
#
|
||||
# This is the counterpart to `dotfiles sync`. Instead of pulling remote
|
||||
# templates down, it pushes your local config, functions, templates, and
|
||||
# conf.d/ files back up to the remote repository.
|
||||
#
|
||||
# Workflow:
|
||||
# 1. Clones the remote repo into a temporary directory
|
||||
# 2. Copies the current local state on top of it:
|
||||
# config.fish, functions/, dotfiles/, conf.d/
|
||||
# 3. Prompts for a version number (auto-suggested minor bump, or custom)
|
||||
# 4. Updates DOTFILES_VERSION in both the local and cloned config.fish
|
||||
# 5. Commits (with a message like "dotfiles v1.1.0")
|
||||
# 6. Pushes to the remote
|
||||
# 7. Cleans up the temporary clone
|
||||
#
|
||||
# After publish, run `dotfiles sync` on any other machine to pick up the
|
||||
# changes.
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
function dotfiles_publish -d "Push local changes to the remote dotfiles repo"
|
||||
# ── Step 1: Ensure we have a remote URL ────────────────────────────────
|
||||
if test -z "$DOTFILES_REPO_URL"
|
||||
echo "No repository URL configured."
|
||||
echo "Set one with: dotfiles repo https://github.com/yourname/dotfiles"
|
||||
return 1
|
||||
end
|
||||
|
||||
echo "Publishing to $DOTFILES_REPO_URL ($DOTFILES_REPO_BRANCH) ..."
|
||||
|
||||
# ── Step 2: Clone (or init) the repo ─────────────────────────────────────
|
||||
set -l tmpdir (mktemp -d)
|
||||
echo "Preparing $tmpdir ..."
|
||||
|
||||
if command git clone --branch "$DOTFILES_REPO_BRANCH" "$DOTFILES_REPO_URL" "$tmpdir" 2>/dev/null
|
||||
echo "Clone successful."
|
||||
else
|
||||
# Remote may not exist yet, or it exists but has no commits.
|
||||
# Initialise a fresh repo locally and wire up the remote.
|
||||
echo "Remote branch not found — initialising new repository."
|
||||
command git init "$tmpdir"
|
||||
command git -C "$tmpdir" remote add origin "$DOTFILES_REPO_URL"
|
||||
echo "Local repo ready (origin → $DOTFILES_REPO_URL)."
|
||||
end
|
||||
|
||||
# Make sure the expected branch exists (create it if it doesn't).
|
||||
if not command git -C "$tmpdir" rev-parse --verify "$DOTFILES_REPO_BRANCH" 2>/dev/null
|
||||
command git -C "$tmpdir" checkout -b "$DOTFILES_REPO_BRANCH"
|
||||
echo "Created branch '$DOTFILES_REPO_BRANCH'."
|
||||
else
|
||||
command git -C "$tmpdir" checkout "$DOTFILES_REPO_BRANCH"
|
||||
end
|
||||
|
||||
# ── 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 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/"
|
||||
|
||||
# Ensure target directories exist
|
||||
for d in (dirname "$repo_config") "$repo_funcs" "$repo_tmpl" "$repo_confd"
|
||||
mkdir -p "$d"
|
||||
end
|
||||
|
||||
echo ""
|
||||
echo "Copying local state into clone ..."
|
||||
command cp "$local_config" "$repo_config"
|
||||
echo " config.fish"
|
||||
command cp -r "$local_funcs." "$repo_funcs"
|
||||
echo " functions/"
|
||||
command cp -r "$local_tmpl." "$repo_tmpl"
|
||||
echo " dotfiles/"
|
||||
command cp -r "$local_confd." "$repo_confd"
|
||||
echo " conf.d/"
|
||||
|
||||
# ── Step 4: Prompt for version ─────────────────────────────────────────
|
||||
echo ""
|
||||
set -l new_version (_dotfiles_prompt_version "$DOTFILES_VERSION")
|
||||
|
||||
# ── Step 5: Update version in both local and cloned config.fish ───────
|
||||
# The version line looks like: set -g DOTFILES_VERSION "X.Y.Z"
|
||||
set -l version_line 'set -g DOTFILES_VERSION "'"$new_version"'"'
|
||||
|
||||
# Update local config.fish
|
||||
sed -i 's/^set -g DOTFILES_VERSION ".*"$/'"$version_line"'/' "$local_config"
|
||||
echo "Updated local $local_config → v$new_version"
|
||||
|
||||
# Update cloned config.fish
|
||||
sed -i 's/^set -g DOTFILES_VERSION ".*"$/'"$version_line"'/' "$repo_config"
|
||||
|
||||
# Set the session variable so subsequent commands in this function
|
||||
# and future shell sessions see the new version.
|
||||
set -g DOTFILES_VERSION "$new_version"
|
||||
|
||||
# ── Step 6: Commit and push from the clone ─────────────────────────────
|
||||
echo ""
|
||||
command git -C "$tmpdir" add -A
|
||||
|
||||
# Check if there's anything to commit
|
||||
if not command git -C "$tmpdir" status --porcelain | grep -q .
|
||||
echo "Nothing changed — skipping commit and push."
|
||||
command rm -rf "$tmpdir"
|
||||
return 0
|
||||
end
|
||||
|
||||
echo "Committing and pushing ..."
|
||||
command git -C "$tmpdir" commit -m "dotfiles v$new_version"
|
||||
|
||||
if command git -C "$tmpdir" push origin "$DOTFILES_REPO_BRANCH"
|
||||
echo ""
|
||||
echo "Published v$new_version successfully."
|
||||
else
|
||||
echo ""
|
||||
echo "Warning: push failed. The commit exists locally in $tmpdir."
|
||||
echo "Check your credentials and remote URL, then push manually:"
|
||||
echo " cd $tmpdir && git push origin $DOTFILES_REPO_BRANCH"
|
||||
# Don't clean up on failure so the user can fix and push manually.
|
||||
return 1
|
||||
end
|
||||
|
||||
# ── Step 7: Clean up ───────────────────────────────────────────────────
|
||||
command rm -rf "$tmpdir"
|
||||
echo ""
|
||||
echo "Done. Run 'dotfiles sync' on other machines to pull v$new_version."
|
||||
end
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# _dotfiles_prompt_version — ask user for version, auto-suggest minor bump
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
#
|
||||
# Parses the current version (X.Y.Z), suggests X.(Y+1).0, and prompts.
|
||||
# If the user presses Enter without typing, the suggested bump is used.
|
||||
# If the user types a custom string (e.g. "2.0.0" or "1.5.0-beta"), that
|
||||
# is used instead.
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
function _dotfiles_prompt_version -d "Prompt for version, defaulting to minor bump"
|
||||
set -l current $argv[1]
|
||||
set -l suggested (_dotfiles_bump_minor "$current")
|
||||
|
||||
echo "Current version: v$current"
|
||||
# Use double quotes so $suggested expands in the prompt string.
|
||||
read -p "echo \"New version (press Enter for v$suggested): \"" -l custom
|
||||
|
||||
if test -z "$custom"
|
||||
echo "$suggested"
|
||||
else
|
||||
echo "$custom"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# _dotfiles_bump_minor — given X.Y.Z, return X.(Y+1).0
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
#
|
||||
# Works with versions like "1.0.0", "2.15.3", etc. Non-numeric parts
|
||||
# (e.g. "1.0.0-beta") will cause the math to fail, so we fall back to
|
||||
# appending ".1" as a best-effort.
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
function _dotfiles_bump_minor -d "Increment the minor version component"
|
||||
set -l v $argv[1]
|
||||
|
||||
# Split on dots: "1.2.3" → major=1, minor=2, patch=3
|
||||
set -l parts (string split "." "$v")
|
||||
set -l major $parts[1]
|
||||
set -l minor $parts[2]
|
||||
set -l patch $parts[3]
|
||||
|
||||
# Default to 0 for missing components
|
||||
set -q minor[1]; or set minor 0
|
||||
set -q patch[1]; or set patch 0
|
||||
|
||||
# Only attempt arithmetic if all three components are plain integers.
|
||||
# Use string match to check they are digits only.
|
||||
if string match -rq '^\d+$' "$major"
|
||||
and string match -rq '^\d+$' "$minor"
|
||||
and string match -rq '^\d+$' "$patch"
|
||||
|
||||
set -l new_minor (math "$minor + 1")
|
||||
echo "$major.$new_minor.0"
|
||||
else
|
||||
# Non-numeric version — just append a bump suffix.
|
||||
echo "$v.1"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# Private helper functions (prefixed with _dotfiles_)
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
#
|
||||
# These are not intended to be called directly from the command line.
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# _dotfiles_list_managed
|
||||
#
|
||||
# Returns a sorted list of template names in $DOTFILES_DIR.
|
||||
# Used by the help text and by _dotfiles_resolve_targets.
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
function _dotfiles_list_managed -d "List available dotfile templates"
|
||||
if not test -d "$DOTFILES_DIR"
|
||||
return 1
|
||||
end
|
||||
|
||||
# Recursively list all regular files relative to DOTFILES_DIR.
|
||||
# This supports subdirectory-based templates, e.g.:
|
||||
# dotfiles/tmux.conf → tmux.conf
|
||||
# dotfiles/config/starship.toml → config/starship.toml
|
||||
command find "$DOTFILES_DIR" -type f -printf '%P\n' | sort
|
||||
end
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# _dotfiles_resolve_targets
|
||||
#
|
||||
# Converts a list of user-supplied names (e.g. "tmux gitconfig") into the
|
||||
# full list of targets. If no names are given, falls back to ALL managed
|
||||
# templates. Unknown names are silently skipped with a warning.
|
||||
#
|
||||
# This function is used by init, update, and status.
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
function _dotfiles_resolve_targets -d "Resolve user-supplied names to template names"
|
||||
if set -q argv[1]
|
||||
# User specified specific names — return them as-is.
|
||||
# Validation happens downstream (_dotfiles_template_path).
|
||||
printf "%s\n" $argv
|
||||
else
|
||||
# No names given — return everything we have templates for.
|
||||
_dotfiles_list_managed
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# _dotfiles_template_path
|
||||
#
|
||||
# Returns the full filesystem path to the template for a given name.
|
||||
# Example: _dotfiles_template_path "tmux" → $DOTFILES_DIR/tmux.conf
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
function _dotfiles_template_path -d "Get the template path for a dotfile name"
|
||||
echo "$DOTFILES_DIR/$argv[1]"
|
||||
end
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# _dotfiles_target_path
|
||||
#
|
||||
# Returns the full filesystem path where the dotfile should be installed.
|
||||
#
|
||||
# Convention:
|
||||
# - If the template name contains a "/", it's treated as a relative path
|
||||
# under $HOME (e.g. "config/starship.toml" → ~/.config/starship.toml).
|
||||
# - Otherwise, the template name is prefixed with a dot and placed directly
|
||||
# under $HOME (e.g. "tmux" → ~/.tmux). Note: tmux.conf is the *filename*,
|
||||
# so "tmux.conf" → ~/.tmux.conf.
|
||||
#
|
||||
# This means:
|
||||
# Template name Target
|
||||
# tmux.conf ~/.tmux.conf
|
||||
# gitconfig ~/.gitconfig
|
||||
# config/starship.toml ~/.config/starship.toml
|
||||
#
|
||||
# To add a file in a subdirectory of $HOME, simply create a template with
|
||||
# a "/" in its name: "vim/vimrc" → ~/.vim/vimrc
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
function _dotfiles_target_path -d "Get the target installation path for a dotfile"
|
||||
set -l name $argv[1]
|
||||
|
||||
if string match -q "*/*" "$name"
|
||||
# Name contains a "/" → treat as relative path under $HOME
|
||||
echo "$HOME/.$name"
|
||||
else
|
||||
# Name without slash → place directly in $HOME, prefixed with dot
|
||||
echo "$HOME/.$name"
|
||||
end
|
||||
end
|
||||
75
.config/fish/functions/gitea_key.fish
Normal file
75
.config/fish/functions/gitea_key.fish
Normal file
@@ -0,0 +1,75 @@
|
||||
function gitea_key -d "Generate (or regenerate) an ed25519 SSH key for Gitea and configure ~/.ssh/config"
|
||||
set -l key_path "$HOME/.ssh/id_ed25519_gitea"
|
||||
set -l pub_path "$key_path.pub"
|
||||
set -l config_path "$HOME/.ssh/config"
|
||||
set -l host "gitssh.toomuchtaco.net"
|
||||
|
||||
# ── Step 1: Handle existing key ─────────────────────────────────────────
|
||||
if test -f "$key_path"
|
||||
echo "An SSH key already exists at $key_path"
|
||||
read -p 'echo "Overwrite it? [y/N] "' -l confirm
|
||||
if test "$confirm" != "y" -a "$confirm" != "Y"
|
||||
echo "Aborted."
|
||||
return 1
|
||||
end
|
||||
echo ""
|
||||
end
|
||||
|
||||
# ── Step 2: Generate the key ────────────────────────────────────────────
|
||||
# -t ed25519: modern, secure, fast elliptic-curve key
|
||||
# -f: output file
|
||||
# -N "": empty passphrase (prompts to set one if desired)
|
||||
ssh-keygen -t ed25519 -f "$key_path" -N ""
|
||||
echo ""
|
||||
|
||||
# ── Step 3: Update ~/.ssh/config ────────────────────────────────────────
|
||||
# If the host block already exists, update the IdentityFile line in place.
|
||||
# Otherwise, append a new block at the end of the file.
|
||||
|
||||
if grep -q "^Host $host\$" "$config_path" 2>/dev/null
|
||||
# Host block exists — update the IdentityFile line.
|
||||
# If an IdentityFile line already exists under this Host, replace it;
|
||||
# otherwise add one after the HostName line.
|
||||
if grep -A5 "^Host $host\$" "$config_path" | grep -q "IdentityFile"
|
||||
sed -i "/^Host $host\$/,/^Host /s|IdentityFile .*|IdentityFile $key_path|" "$config_path"
|
||||
echo "Updated IdentityFile for Host $host in $config_path"
|
||||
else
|
||||
sed -i "/^Host $host\$/,/^Host /s|HostName .*|&\n\tIdentityFile $key_path|" "$config_path"
|
||||
echo "Added IdentityFile for Host $host in $config_path"
|
||||
end
|
||||
else
|
||||
# No Host block yet — append one.
|
||||
# printf interprets \t as a literal tab (unlike echo in Fish).
|
||||
printf "\n# Gitea\nHost %s\n\tHostName %s\n\tUser git\n\tIdentityFile %s\n" \
|
||||
"$host" "$host" "$key_path" >>"$config_path"
|
||||
echo "Appended Host block for $host to $config_path"
|
||||
end
|
||||
|
||||
# ── Step 4: Set correct permissions ────────────────────────────────────
|
||||
chmod 600 "$key_path"
|
||||
chmod 644 "$pub_path"
|
||||
chmod 600 "$config_path"
|
||||
|
||||
echo ""
|
||||
|
||||
# ── Step 5: Print the public key ────────────────────────────────────────
|
||||
echo "=============================="
|
||||
echo " Public key (add to Gitea): "
|
||||
echo "=============================="
|
||||
echo ""
|
||||
cat "$pub_path"
|
||||
echo ""
|
||||
echo "=============================="
|
||||
echo "Key copied to clipboard if pbcopy/xclip is available."
|
||||
if command -v pbcopy &>/dev/null
|
||||
cat "$pub_path" | pbcopy
|
||||
echo "(copied to clipboard via pbcopy)"
|
||||
else if command -v xclip &>/dev/null
|
||||
cat "$pub_path" | xclip -selection clipboard
|
||||
echo "(copied to clipboard via xclip)"
|
||||
else if command -v wl-copy &>/dev/null
|
||||
cat "$pub_path" | wl-copy
|
||||
echo "(copied to clipboard via wl-copy)"
|
||||
end
|
||||
echo "=============================="
|
||||
end
|
||||
Reference in New Issue
Block a user