chore: initial commit
This commit is contained in:
@@ -0,0 +1,12 @@
|
|||||||
|
{{- $hostname := .chezmoi.hostname -}}
|
||||||
|
{{- $email := promptStringOnce . "email" "Git email address" -}}
|
||||||
|
{{- $name := promptStringOnce . "name" "Git full name" -}}
|
||||||
|
|
||||||
|
[data]
|
||||||
|
hostname = {{ $hostname | quote }}
|
||||||
|
email = {{ $email | quote }}
|
||||||
|
name = {{ $name | quote }}
|
||||||
|
{{ if ne .chezmoi.os "windows" -}}
|
||||||
|
[diff]
|
||||||
|
pager = "less -FRX"
|
||||||
|
{{ end -}}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{{- if eq .chezmoi.os "windows" }}
|
||||||
|
["AppData/Local/nvim"]
|
||||||
|
{{- else }}
|
||||||
|
[".config/nvim"]
|
||||||
|
{{- end }}
|
||||||
|
type = "git-repo"
|
||||||
|
url = "https://github.com/StevanFreeborn/nvim-config.git"
|
||||||
|
refreshPeriod = "168h"
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# chezmoi ignore patterns
|
||||||
|
# These paths will not be managed by chezmoi
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Editor temp files
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# Nested git repos (e.g. nvim has its own .git)
|
||||||
|
**/.git/**
|
||||||
|
AppData/Local/nvim/.exe/**
|
||||||
|
|
||||||
|
# chezmoi internal
|
||||||
|
.chezmoiroot
|
||||||
|
|
||||||
|
# Repo-only files — don't apply these to the home directory
|
||||||
|
README.md
|
||||||
|
packages
|
||||||
|
scripts
|
||||||
|
.gitattributes
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# Windows-only paths — skip on Linux
|
||||||
|
{{ if ne .chezmoi.os "windows" }}
|
||||||
|
AppData/**
|
||||||
|
dot_wslconfig
|
||||||
|
Documents/**
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
# Linux-only paths — skip on Windows
|
||||||
|
{{ if ne .chezmoi.os "linux" }}
|
||||||
|
.zshrc
|
||||||
|
.zshenv
|
||||||
|
.oh-my-zsh/**
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
# Linux-only scripts — skip on Windows
|
||||||
|
{{ if ne .chezmoi.os "linux" }}
|
||||||
|
.chezmoiscripts/linux_*
|
||||||
|
.chezmoiscripts/setup-ssh-keys.sh
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
# Windows-only scripts — skip on Linux
|
||||||
|
{{ if ne .chezmoi.os "windows" }}
|
||||||
|
.chezmoiscripts/windows_*
|
||||||
|
{{ end }}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# run_once_linux_configure-shell.sh
|
||||||
|
# Configures zsh as the default shell and sets up the shell environment.
|
||||||
|
# This script runs once (unless deleted from ~/.local/share/chezmoi).
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "==> Configuring shell environment (Linux)..."
|
||||||
|
|
||||||
|
# --- Set zsh as default shell ---
|
||||||
|
ZSH_PATH=$(which zsh 2>/dev/null || echo "")
|
||||||
|
if [ -z "$ZSH_PATH" ]; then
|
||||||
|
echo " ERROR: zsh is not installed. Run the package install script first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
CURRENT_SHELL=$(getent passwd "$USER" | cut -d: -f7)
|
||||||
|
if [ "$CURRENT_SHELL" != "$ZSH_PATH" ]; then
|
||||||
|
echo " Setting zsh as default shell..."
|
||||||
|
chsh -s "$ZSH_PATH"
|
||||||
|
echo " Default shell set to: $ZSH_PATH"
|
||||||
|
else
|
||||||
|
echo " zsh is already the default shell"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Set up Rust (cargo) in environment ---
|
||||||
|
CARGO_ENV="$HOME/.cargo/env"
|
||||||
|
if [ -f "$CARGO_ENV" ]; then
|
||||||
|
RUST_ZSHENV_LINE='. "$HOME/.cargo/env"'
|
||||||
|
ZSHENV="$HOME/.zshenv"
|
||||||
|
if ! grep -qF "$RUST_ZSHENV_LINE" "$ZSHENV" 2>/dev/null; then
|
||||||
|
echo "$RUST_ZSHENV_LINE" >> "$ZSHENV"
|
||||||
|
echo " Added Rust cargo env to .zshenv"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Set up Go in environment ---
|
||||||
|
if [ -d "/usr/local/go" ]; then
|
||||||
|
GO_ZSHENV_LINE='export PATH=$PATH:/usr/local/go/bin'
|
||||||
|
ZSHENV="$HOME/.zshenv"
|
||||||
|
if ! grep -qF "go/bin" "$ZSHENV" 2>/dev/null; then
|
||||||
|
echo "$GO_ZSHENV_LINE" >> "$ZSHENV"
|
||||||
|
echo " Added Go to PATH in .zshenv"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "==> Shell configuration complete!"
|
||||||
|
echo " NOTE: Log out and back in (or restart your terminal) for shell changes to take effect."
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# run_once_setup-ssh-keys.sh.tmpl
|
||||||
|
# Pulls SSH PRIVATE keys from Bitwarden and writes them to ~/.ssh/.
|
||||||
|
#
|
||||||
|
# Public keys (.pub files) are committed directly to the repo in dot_ssh/
|
||||||
|
# and are applied automatically by chezmoi — no Bitwarden needed for those.
|
||||||
|
#
|
||||||
|
# Requires: bw (Bitwarden CLI) to be logged in and unlocked.
|
||||||
|
# To unlock Bitwarden before running chezmoi:
|
||||||
|
# export BW_SESSION=$(bw unlock --raw)
|
||||||
|
# chezmoi apply
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "==> Setting up SSH keys from Bitwarden..."
|
||||||
|
|
||||||
|
SSH_DIR="$HOME/.ssh"
|
||||||
|
mkdir -p "$SSH_DIR"
|
||||||
|
chmod 700 "$SSH_DIR"
|
||||||
|
|
||||||
|
write_key() {
|
||||||
|
local name="$1"
|
||||||
|
local bw_item_name="$2"
|
||||||
|
local key_path="$SSH_DIR/$name"
|
||||||
|
|
||||||
|
if [ -f "$key_path" ]; then
|
||||||
|
echo " Key already exists: $name (skipping)"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo " Fetching key: $bw_item_name -> $name"
|
||||||
|
# bw get notes "Item Name" returns the secure note content
|
||||||
|
bw get notes "$bw_item_name" 2>/dev/null > "$key_path" || {
|
||||||
|
echo " WARNING: Could not fetch '$bw_item_name' from Bitwarden. Skipping."
|
||||||
|
rm -f "$key_path"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ ! -s "$key_path" ]; then
|
||||||
|
echo " WARNING: Key content was empty for '$bw_item_name'. Removing."
|
||||||
|
rm -f "$key_path"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
chmod 600 "$key_path"
|
||||||
|
echo " Wrote: $key_path"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Add your SSH keys below ---
|
||||||
|
# Format: write_key "filename_in_~/.ssh" "Bitwarden secure note name"
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# write_key "stevan@freeborn.cloud" "SSH Key - stevan@freeborn.cloud"
|
||||||
|
# write_key "ftp_stevanfreeborn_com" "SSH Key - ftp_stevanfreeborn_com"
|
||||||
|
# write_key "tangled" "SSH Key - tangled"
|
||||||
|
# write_key "tinker" "SSH Key - tinker"
|
||||||
|
# write_key "gitea.freeborn.cloud" "SSH Key - gitea.freeborn.cloud"
|
||||||
|
|
||||||
|
write_key "bit_bastion.key" "SSH Key - bit_bastion.key"
|
||||||
|
write_key "stevan@freeborn.cloud" "SSH Key - stevan@freeborn.cloud"
|
||||||
|
write_key "ftp_stevanfreeborn_com" "SSH Key - ftp_stevanfreeborn_com"
|
||||||
|
write_key "tangled" "SSH Key - tangled"
|
||||||
|
write_key "zenbook_tinker" "SSH Key - zenbook_tinker"
|
||||||
|
write_key "gitea.freeborn.cloud" "SSH Key - gitea.freeborn.cloud"
|
||||||
|
write_key "id_ed25519" "SSH Key - id_ed25519"
|
||||||
|
write_key "macbookair" "SSH Key - macbookair"
|
||||||
|
write_key "macbookpro" "SSH Key - macbookpro"
|
||||||
|
write_key "truenas" "SSH Key - truenas"
|
||||||
|
write_key "blog.stevanfreeborn.com_github_actions" "SSH Key - blog.stevanfreeborn.com_github_actions"
|
||||||
|
write_key "commands_github_actions" "SSH Key - commands_github_actions"
|
||||||
|
write_key "onspring_qa_playwright_reports_render" "SSH Key - onspring_qa_playwright_reports_render"
|
||||||
|
write_key "onx_graph_github_actions" "SSH Key - onx_graph_github_actions"
|
||||||
|
write_key "restapiplayground.stevanfreeborn.com_github_actions" "SSH Key - restapiplayground.stevanfreeborn.com_github_actions"
|
||||||
|
write_key "steves_bot_github_actions" "SSH Key - steves_bot_github_actions"
|
||||||
|
|
||||||
|
echo "==> SSH key setup complete!"
|
||||||
|
echo " NOTE: Store your SSH private keys as Bitwarden Secure Notes with the names listed above."
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#!/usr/bin/env pwsh
|
||||||
|
# run_once_windows_configure-shell.ps1
|
||||||
|
# Installs PowerShell modules and configures the shell environment.
|
||||||
|
# This script runs once (unless deleted from ~/.local/share/chezmoi).
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Continue"
|
||||||
|
|
||||||
|
Write-Host "==> Configuring PowerShell shell environment..." -ForegroundColor Cyan
|
||||||
|
|
||||||
|
# --- Install PowerShell modules ---
|
||||||
|
$modules = @("PSReadLine", "Terminal-Icons", "posh-git")
|
||||||
|
|
||||||
|
foreach ($module in $modules) {
|
||||||
|
if (-not (Get-Module -ListAvailable -Name $module)) {
|
||||||
|
Write-Host " Installing module: $module..." -ForegroundColor Yellow
|
||||||
|
Install-Module -Name $module -Force -Scope CurrentUser -SkipPublisherCheck -ErrorAction SilentlyContinue
|
||||||
|
Write-Host " Installed: $module" -ForegroundColor Green
|
||||||
|
} else {
|
||||||
|
Write-Host " Module already installed: $module" -ForegroundColor DarkGray
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Set PowerShell execution policy ---
|
||||||
|
$policy = Get-ExecutionPolicy -Scope CurrentUser
|
||||||
|
if ($policy -ne "RemoteSigned" -and $policy -ne "Unrestricted") {
|
||||||
|
Write-Host " Setting execution policy to RemoteSigned for CurrentUser..." -ForegroundColor Yellow
|
||||||
|
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser -Force
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Ensure profile directory exists ---
|
||||||
|
$profileDir = Split-Path $PROFILE -Parent
|
||||||
|
if (-not (Test-Path $profileDir)) {
|
||||||
|
New-Item -ItemType Directory -Path $profileDir -Force | Out-Null
|
||||||
|
Write-Host " Created profile directory: $profileDir" -ForegroundColor Green
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "==> Shell configuration complete!" -ForegroundColor Cyan
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
#!/usr/bin/env pwsh
|
||||||
|
# run_once_windows_setup-ssh-keys.ps1.tmpl
|
||||||
|
# Pulls SSH private keys from Bitwarden and writes them to ~/.ssh/.
|
||||||
|
#
|
||||||
|
# Public keys (.pub files) are committed directly to the repo in dot_ssh/
|
||||||
|
# and are applied automatically by chezmoi — no Bitwarden needed for those.
|
||||||
|
#
|
||||||
|
# Requires: bw (Bitwarden CLI) to be logged in and unlocked.
|
||||||
|
# To unlock Bitwarden before running chezmoi:
|
||||||
|
# $env:BW_SESSION = bw unlock --raw
|
||||||
|
# chezmoi apply
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
Write-Host "==> Setting up SSH keys from Bitwarden..." -ForegroundColor Cyan
|
||||||
|
|
||||||
|
$SshDir = Join-Path $env:USERPROFILE ".ssh"
|
||||||
|
New-Item -ItemType Directory -Force $SshDir | Out-Null
|
||||||
|
|
||||||
|
function Write-Key($name, $bwItemName) {
|
||||||
|
$keyPath = Join-Path $SshDir $name
|
||||||
|
|
||||||
|
if (Test-Path $keyPath) {
|
||||||
|
Write-Host " Key already exists: $name (skipping)" -ForegroundColor DarkGray
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host " Fetching key: $bwItemName -> $name" -NoNewline
|
||||||
|
|
||||||
|
try {
|
||||||
|
$content = bw get notes "$bwItemName" 2>&1
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw $content }
|
||||||
|
[System.IO.File]::WriteAllText($keyPath, $content)
|
||||||
|
& icacls $keyPath /inheritance:r /grant "$env:USERNAME:(R,W)" /q 2>&1 | Out-Null
|
||||||
|
Write-Host "" -NoNewline
|
||||||
|
Write-Host " Wrote: $keyPath" -ForegroundColor Green
|
||||||
|
} catch {
|
||||||
|
Write-Host "" -NoNewline
|
||||||
|
Write-Host " WARNING: Could not fetch '$bwItemName' from Bitwarden. Skipping." -ForegroundColor Yellow
|
||||||
|
if (Test-Path $keyPath) { Remove-Item $keyPath -Force }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Add your SSH keys below ---
|
||||||
|
# Format: Write-Key "filename_in_~/.ssh" "Bitwarden secure note name"
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# Write-Key "stevan@freeborn.cloud" "SSH Key - stevan@freeborn.cloud"
|
||||||
|
|
||||||
|
Write-Key "bit_bastion.key" "SSH Key - bit_bastion.key"
|
||||||
|
Write-Key "stevan@freeborn.cloud" "SSH Key - stevan@freeborn.cloud"
|
||||||
|
Write-Key "ftp_stevanfreeborn_com" "SSH Key - ftp_stevanfreeborn_com"
|
||||||
|
Write-Key "tangled" "SSH Key - tangled"
|
||||||
|
Write-Key "zenbook_tinker" "SSH Key - zenbook_tinker"
|
||||||
|
Write-Key "gitea.freeborn.cloud" "SSH Key - gitea.freeborn.cloud"
|
||||||
|
Write-Key "id_ed25519" "SSH Key - id_ed25519"
|
||||||
|
Write-Key "macbookair" "SSH Key - macbookair"
|
||||||
|
Write-Key "macbookpro" "SSH Key - macbookpro"
|
||||||
|
Write-Key "truenas" "SSH Key - truenas"
|
||||||
|
Write-Key "blog.stevanfreeborn.com_github_actions" "SSH Key - blog.stevanfreeborn.com_github_actions"
|
||||||
|
Write-Key "commands_github_actions" "SSH Key - commands_github_actions"
|
||||||
|
Write-Key "onspring_qa_playwright_reports_render" "SSH Key - onspring_qa_playwright_reports_render"
|
||||||
|
Write-Key "onx_graph_github_actions" "SSH Key - onx_graph_github_actions"
|
||||||
|
Write-Key "restapiplayground.stevanfreeborn.com_github_actions" "SSH Key - restapiplayground.stevanfreeborn.com_github_actions"
|
||||||
|
Write-Key "steves_bot_github_actions" "SSH Key - steves_bot_github_actions"
|
||||||
|
|
||||||
|
Write-Host "==> SSH key setup complete!" -ForegroundColor Cyan
|
||||||
|
Write-Host " NOTE: Store your SSH private keys as Bitwarden Secure Notes with the names listed above." -ForegroundColor Yellow
|
||||||
@@ -0,0 +1,213 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# run_onchange_linux_install-packages.sh
|
||||||
|
# Installs packages on Ubuntu/Debian via apt and manual installs for tools
|
||||||
|
# not available (or outdated) in apt.
|
||||||
|
# This script re-runs whenever packages/linux.txt changes.
|
||||||
|
#
|
||||||
|
# Hash of packages file (forces re-run on change):
|
||||||
|
# {{ include "packages/linux.txt" | sha256sum }}
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Ensure snap binaries are visible (chezmoi scripts may not inherit /snap/bin)
|
||||||
|
[ -d "/snap/bin" ] && export PATH="$PATH:/snap/bin"
|
||||||
|
|
||||||
|
CHEZMOI_SOURCE_DIR="{{ .chezmoi.sourceDir }}"
|
||||||
|
|
||||||
|
echo "==> Updating apt package index..."
|
||||||
|
sudo apt-get update -q
|
||||||
|
|
||||||
|
echo "==> Installing apt packages..."
|
||||||
|
apt_packages=$(grep -v '^#' "$CHEZMOI_SOURCE_DIR/packages/linux.txt" | grep -v '^$' | tr '\n' ' ')
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
sudo apt-get install -y $apt_packages
|
||||||
|
|
||||||
|
# --- Tailscale ---
|
||||||
|
if ! command -v tailscale &>/dev/null; then
|
||||||
|
echo "==> Installing Tailscale..."
|
||||||
|
curl -fsSL https://tailscale.com/install.sh | sh
|
||||||
|
else
|
||||||
|
echo "==> Tailscale already installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Go ---
|
||||||
|
GO_VERSION=$(curl -fsSL "https://go.dev/dl/?mode=json" | python3 -c "import sys,json; print(json.load(sys.stdin)[0]['version'].lstrip('go'))")
|
||||||
|
if ! command -v go &>/dev/null || [[ "$(go version 2>/dev/null | awk '{print $3}' | tr -d 'go')" != "$GO_VERSION" ]]; then
|
||||||
|
echo "==> Installing Go $GO_VERSION..."
|
||||||
|
curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz" -o /tmp/go.tar.gz
|
||||||
|
sudo rm -rf /usr/local/go
|
||||||
|
sudo tar -C /usr/local -xzf /tmp/go.tar.gz
|
||||||
|
rm /tmp/go.tar.gz
|
||||||
|
echo "export PATH=\$PATH:/usr/local/go/bin" >> "$HOME/.zshenv"
|
||||||
|
else
|
||||||
|
echo "==> Go already installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Rust ---
|
||||||
|
if ! command -v rustup &>/dev/null; then
|
||||||
|
echo "==> Installing Rust via rustup..."
|
||||||
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --no-modify-path
|
||||||
|
source "$HOME/.cargo/env"
|
||||||
|
else
|
||||||
|
echo "==> Rust already installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- NVM (Node Version Manager) ---
|
||||||
|
if [ ! -d "$HOME/.nvm" ]; then
|
||||||
|
echo "==> Installing NVM..."
|
||||||
|
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
|
||||||
|
export NVM_DIR="$HOME/.nvm"
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
[ -s "$NVM_DIR/nvm.sh" ] && source "$NVM_DIR/nvm.sh"
|
||||||
|
nvm install --lts
|
||||||
|
else
|
||||||
|
echo "==> NVM already installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Oh My Zsh ---
|
||||||
|
if [ ! -d "$HOME/.oh-my-zsh" ]; then
|
||||||
|
echo "==> Installing Oh My Zsh..."
|
||||||
|
RUNZSH=no CHSH=no sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended
|
||||||
|
else
|
||||||
|
echo "==> Oh My Zsh already installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Oh My Zsh plugins ---
|
||||||
|
ZSH_CUSTOM="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}"
|
||||||
|
|
||||||
|
if [ ! -d "$ZSH_CUSTOM/plugins/zsh-autosuggestions" ]; then
|
||||||
|
echo "==> Installing zsh-autosuggestions..."
|
||||||
|
git clone https://github.com/zsh-users/zsh-autosuggestions "$ZSH_CUSTOM/plugins/zsh-autosuggestions"
|
||||||
|
else
|
||||||
|
echo "==> zsh-autosuggestions already installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "$ZSH_CUSTOM/plugins/zsh-syntax-highlighting" ]; then
|
||||||
|
echo "==> Installing zsh-syntax-highlighting..."
|
||||||
|
git clone https://github.com/zsh-users/zsh-syntax-highlighting "$ZSH_CUSTOM/plugins/zsh-syntax-highlighting"
|
||||||
|
else
|
||||||
|
echo "==> zsh-syntax-highlighting already installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Neovim ---
|
||||||
|
if ! command -v nvim &>/dev/null; then
|
||||||
|
echo "==> Installing Neovim (latest)..."
|
||||||
|
curl -fsSL https://github.com/neovim/neovim/releases/latest/download/nvim-linux-x86_64.tar.gz -o /tmp/nvim.tar.gz
|
||||||
|
sudo tar -C /opt -xzf /tmp/nvim.tar.gz
|
||||||
|
sudo ln -sf /opt/nvim-linux-x86_64/bin/nvim /usr/local/bin/nvim
|
||||||
|
rm /tmp/nvim.tar.gz
|
||||||
|
else
|
||||||
|
echo "==> Neovim already installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- lazygit ---
|
||||||
|
if ! command -v lazygit &>/dev/null; then
|
||||||
|
echo "==> Installing lazygit..."
|
||||||
|
LAZYGIT_VERSION=$(curl -s "https://api.github.com/repos/jesseduffield/lazygit/releases/latest" | grep -Po '"tag_name": "v\K[^"]*')
|
||||||
|
curl -fsSL "https://github.com/jesseduffield/lazygit/releases/download/v${LAZYGIT_VERSION}/lazygit_${LAZYGIT_VERSION}_Linux_x86_64.tar.gz" -o /tmp/lazygit.tar.gz
|
||||||
|
sudo tar xf /tmp/lazygit.tar.gz lazygit -C /usr/local/bin
|
||||||
|
rm /tmp/lazygit.tar.gz
|
||||||
|
else
|
||||||
|
echo "==> lazygit already installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- watchexec ---
|
||||||
|
if ! command -v watchexec &>/dev/null; then
|
||||||
|
echo "==> Installing watchexec..."
|
||||||
|
WE_VERSION=$(curl -s "https://api.github.com/repos/watchexec/watchexec/releases/latest" | grep -Po '"tag_name": "v\K[^"]*')
|
||||||
|
curl -fsSL "https://github.com/watchexec/watchexec/releases/download/v${WE_VERSION}/watchexec-${WE_VERSION}-x86_64-unknown-linux-musl.tar.xz" -o /tmp/watchexec.tar.xz
|
||||||
|
tar -xJf /tmp/watchexec.tar.xz -C /tmp "watchexec-${WE_VERSION}-x86_64-unknown-linux-musl/watchexec"
|
||||||
|
sudo mv "/tmp/watchexec-${WE_VERSION}-x86_64-unknown-linux-musl/watchexec" /usr/local/bin/watchexec
|
||||||
|
rm -rf /tmp/watchexec.tar.xz "/tmp/watchexec-${WE_VERSION}-x86_64-unknown-linux-musl"
|
||||||
|
else
|
||||||
|
echo "==> watchexec already installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- bottom (btm) ---
|
||||||
|
if ! command -v btm &>/dev/null; then
|
||||||
|
echo "==> Installing bottom (btm)..."
|
||||||
|
curl -fsSLO https://github.com/ClementTsang/bottom/releases/latest/download/bottom_x86_64-unknown-linux-musl.tar.gz
|
||||||
|
tar -xzf bottom_x86_64-unknown-linux-musl.tar.gz
|
||||||
|
sudo mv btm /usr/local/bin/
|
||||||
|
rm bottom_x86_64-unknown-linux-musl.tar.gz
|
||||||
|
else
|
||||||
|
echo "==> bottom already installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Bitwarden CLI ---
|
||||||
|
if ! command -v bw &>/dev/null; then
|
||||||
|
echo "==> Installing Bitwarden CLI..."
|
||||||
|
BW_VERSION=$(curl -s "https://api.github.com/repos/bitwarden/clients/releases" | grep -Po '"tag_name": "cli-v\K[^"]*' | head -1)
|
||||||
|
if [ -z "$BW_VERSION" ]; then
|
||||||
|
echo "WARNING: Could not determine Bitwarden CLI version, skipping install."
|
||||||
|
else
|
||||||
|
curl -fsSL "https://github.com/bitwarden/clients/releases/download/cli-v${BW_VERSION}/bw-linux-${BW_VERSION}.zip" -o /tmp/bw.zip
|
||||||
|
unzip -o /tmp/bw.zip -d /tmp/bw
|
||||||
|
sudo mv /tmp/bw/bw /usr/local/bin/bw
|
||||||
|
sudo chmod +x /usr/local/bin/bw
|
||||||
|
rm -rf /tmp/bw.zip /tmp/bw
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "==> Bitwarden CLI already installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Doppler ---
|
||||||
|
if ! command -v doppler &>/dev/null; then
|
||||||
|
echo "==> Installing Doppler..."
|
||||||
|
curl -Ls --tlsv1.2 --proto "=https" --retry 3 https://cli.doppler.com/install.sh | sudo sh
|
||||||
|
else
|
||||||
|
echo "==> Doppler already installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- GitHub CLI ---
|
||||||
|
if ! command -v gh &>/dev/null; then
|
||||||
|
echo "==> Installing GitHub CLI..."
|
||||||
|
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
|
||||||
|
sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg
|
||||||
|
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null
|
||||||
|
sudo apt-get update -q
|
||||||
|
sudo apt-get install gh -y
|
||||||
|
else
|
||||||
|
echo "==> GitHub CLI already installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- uv (Python package/project manager) ---
|
||||||
|
if ! command -v uv &>/dev/null; then
|
||||||
|
echo "==> Installing uv..."
|
||||||
|
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||||
|
else
|
||||||
|
echo "==> uv already installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Docker ---
|
||||||
|
if ! command -v docker &>/dev/null; then
|
||||||
|
echo "==> Installing Docker..."
|
||||||
|
curl -fsSL https://get.docker.com | sh
|
||||||
|
sudo usermod -aG docker "$USER"
|
||||||
|
echo "NOTE: Log out and back in for Docker group membership to take effect."
|
||||||
|
else
|
||||||
|
echo "==> Docker already installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- .NET SDK (8 + 10) ---
|
||||||
|
# Uses Microsoft's official dotnet-install.sh script
|
||||||
|
DOTNET_INSTALL_DIR="$HOME/.dotnet"
|
||||||
|
DOTNET_SCRIPT=$(mktemp /tmp/dotnet-install-XXXXXX.sh)
|
||||||
|
curl -fsSL https://dot.net/v1/dotnet-install.sh -o "$DOTNET_SCRIPT"
|
||||||
|
chmod +x "$DOTNET_SCRIPT"
|
||||||
|
|
||||||
|
for DOTNET_CHANNEL in "8.0" "10.0"; do
|
||||||
|
MAJOR="${DOTNET_CHANNEL%%.*}"
|
||||||
|
DOTNET_VERSION_INSTALLED=$(find "$DOTNET_INSTALL_DIR/sdk" -maxdepth 1 -name "${MAJOR}.*" 2>/dev/null | head -1 || echo "")
|
||||||
|
if [ -z "$DOTNET_VERSION_INSTALLED" ]; then
|
||||||
|
echo "==> Installing .NET SDK $DOTNET_CHANNEL..."
|
||||||
|
"$DOTNET_SCRIPT" --channel "$DOTNET_CHANNEL" --install-dir "$DOTNET_INSTALL_DIR"
|
||||||
|
else
|
||||||
|
echo "==> .NET SDK $DOTNET_CHANNEL already installed"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
rm -f "$DOTNET_SCRIPT"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "==> Linux package installation complete!"
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
#!/usr/bin/env pwsh
|
||||||
|
# run_onchange_windows_install-packages.ps1.tmpl
|
||||||
|
# Installs all packages from packages/windows.json via winget.
|
||||||
|
# This script re-runs whenever packages/windows.json changes.
|
||||||
|
#
|
||||||
|
# Hash of packages file (forces re-run on change):
|
||||||
|
# {{ include "packages/windows.json" | sha256sum }}
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Continue"
|
||||||
|
|
||||||
|
Write-Host "==> Installing packages via winget..." -ForegroundColor Cyan
|
||||||
|
|
||||||
|
# Use the chezmoi source packages file directly
|
||||||
|
$sourcePackages = "{{ .chezmoi.sourceDir }}/packages/windows.json"
|
||||||
|
$sourcePackages = [System.IO.Path]::GetFullPath($sourcePackages)
|
||||||
|
|
||||||
|
if (-not (Test-Path $sourcePackages)) {
|
||||||
|
Write-Warning "packages/windows.json not found at: $sourcePackages"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$packageList = Get-Content $sourcePackages -Raw | ConvertFrom-Json
|
||||||
|
$packages = $packageList.Sources[0].Packages
|
||||||
|
|
||||||
|
$total = $packages.Count
|
||||||
|
$installed = 0
|
||||||
|
$skipped = 0
|
||||||
|
$failed = 0
|
||||||
|
|
||||||
|
foreach ($pkg in $packages) {
|
||||||
|
$id = $pkg.PackageIdentifier
|
||||||
|
Write-Host " Checking $id..." -NoNewline
|
||||||
|
|
||||||
|
# Check if already installed
|
||||||
|
$check = winget list --id $id --exact --accept-source-agreements 2>$null | Select-String $id
|
||||||
|
if ($check) {
|
||||||
|
Write-Host " already installed" -ForegroundColor DarkGray
|
||||||
|
$skipped++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
# Install
|
||||||
|
Write-Host " installing..." -NoNewline
|
||||||
|
$result = winget install --id $id --exact --silent --accept-package-agreements --accept-source-agreements 2>&1
|
||||||
|
if ($LASTEXITCODE -eq 0) {
|
||||||
|
Write-Host " done" -ForegroundColor Green
|
||||||
|
$installed++
|
||||||
|
} else {
|
||||||
|
Write-Host " FAILED (exit $LASTEXITCODE)" -ForegroundColor Red
|
||||||
|
$failed++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "==> Package install complete: $installed installed, $skipped already present, $failed failed" -ForegroundColor Cyan
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Manual installs — tools not available on winget
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
$LocalBin = Join-Path $env:USERPROFILE ".local\bin"
|
||||||
|
if (-not (Test-Path $LocalBin)) {
|
||||||
|
New-Item -ItemType Directory -Force $LocalBin | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ensure ~/.local/bin is in the user PATH
|
||||||
|
$userPath = [System.Environment]::GetEnvironmentVariable("PATH", "User")
|
||||||
|
if ($userPath -notlike "*$LocalBin*") {
|
||||||
|
[System.Environment]::SetEnvironmentVariable("PATH", "$userPath;$LocalBin", "User")
|
||||||
|
$env:PATH = "$env:PATH;$LocalBin"
|
||||||
|
Write-Host " Added $LocalBin to user PATH." -ForegroundColor Green
|
||||||
|
}
|
||||||
|
|
||||||
|
function Install-GitHubBinary {
|
||||||
|
param(
|
||||||
|
[string]$Name,
|
||||||
|
[string]$Repo,
|
||||||
|
[string]$AssetPattern,
|
||||||
|
[string]$BinaryName = "$Name.exe"
|
||||||
|
)
|
||||||
|
|
||||||
|
$BinPath = Join-Path $LocalBin $BinaryName
|
||||||
|
if (Test-Path $BinPath) {
|
||||||
|
Write-Host " $Name already installed" -ForegroundColor DarkGray
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host " Installing $Name..." -NoNewline
|
||||||
|
try {
|
||||||
|
$release = Invoke-RestMethod "https://api.github.com/repos/$Repo/releases/latest"
|
||||||
|
$asset = $release.assets | Where-Object { $_.name -like $AssetPattern } | Select-Object -First 1
|
||||||
|
if (-not $asset) { throw "No matching asset found for pattern: $AssetPattern" }
|
||||||
|
|
||||||
|
$tmpZip = Join-Path $env:TEMP "$Name-$($release.tag_name).zip"
|
||||||
|
$tmpDir = Join-Path $env:TEMP "$Name-$($release.tag_name)"
|
||||||
|
Invoke-WebRequest -Uri $asset.browser_download_url -OutFile $tmpZip -UseBasicParsing
|
||||||
|
Expand-Archive -Path $tmpZip -DestinationPath $tmpDir -Force
|
||||||
|
$exe = Get-ChildItem -Recurse $tmpDir -Filter $BinaryName | Select-Object -First 1
|
||||||
|
Copy-Item $exe.FullName $BinPath
|
||||||
|
Remove-Item $tmpZip -Force
|
||||||
|
Remove-Item $tmpDir -Recurse -Force
|
||||||
|
Write-Host " done" -ForegroundColor Green
|
||||||
|
} catch {
|
||||||
|
Write-Host " FAILED: $_" -ForegroundColor Red
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "==> Installing manual (non-winget) packages..." -ForegroundColor Cyan
|
||||||
|
|
||||||
|
# watchexec — https://github.com/watchexec/watchexec
|
||||||
|
Install-GitHubBinary -Name "watchexec" -Repo "watchexec/watchexec" -AssetPattern "*x86_64-pc-windows-msvc.zip"
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# Ensure shell scripts always use LF line endings
|
||||||
|
*.sh text eol=lf
|
||||||
|
*.sh.tmpl text eol=lf
|
||||||
|
|
||||||
|
# PowerShell scripts use CRLF on Windows
|
||||||
|
*.ps1 text eol=crlf
|
||||||
|
*.ps1.tmpl text eol=crlf
|
||||||
|
|
||||||
|
# JSON and TOML — platform native
|
||||||
|
*.json text
|
||||||
|
*.toml text
|
||||||
|
*.tmpl text
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# Don't commit local chezmoi state files
|
||||||
|
*.age
|
||||||
+118
@@ -0,0 +1,118 @@
|
|||||||
|
{
|
||||||
|
"$help": "https://aka.ms/terminal-documentation",
|
||||||
|
"$schema": "https://aka.ms/terminal-profiles-schema",
|
||||||
|
"actions":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"command":
|
||||||
|
{
|
||||||
|
"action": "copy",
|
||||||
|
"singleLine": false
|
||||||
|
},
|
||||||
|
"id": "User.copy.644BA8F2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "paste",
|
||||||
|
"id": "User.paste"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "find",
|
||||||
|
"id": "User.find"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command":
|
||||||
|
{
|
||||||
|
"action": "splitPane",
|
||||||
|
"split": "auto",
|
||||||
|
"splitMode": "duplicate"
|
||||||
|
},
|
||||||
|
"id": "User.splitPane.A6751878"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"copyFormatting": "none",
|
||||||
|
"copyOnSelect": false,
|
||||||
|
"defaultProfile": "{574e775e-4f2a-5b96-ac1e-a2962a402336}",
|
||||||
|
"keybindings":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "User.copy.644BA8F2",
|
||||||
|
"keys": "ctrl+c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "User.paste",
|
||||||
|
"keys": "ctrl+v"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "User.find",
|
||||||
|
"keys": "ctrl+shift+f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "User.splitPane.A6751878",
|
||||||
|
"keys": "alt+shift+d"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"newTabMenu":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "remainingProfiles"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"profiles":
|
||||||
|
{
|
||||||
|
"defaults":
|
||||||
|
{
|
||||||
|
"bellStyle": "none",
|
||||||
|
"colorScheme": "One Half Dark",
|
||||||
|
"font":
|
||||||
|
{
|
||||||
|
"face": "CaskaydiaCove Nerd Font Mono"
|
||||||
|
},
|
||||||
|
"intenseTextStyle": "all",
|
||||||
|
"useAcrylic": true
|
||||||
|
},
|
||||||
|
"list":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"bellStyle": "none",
|
||||||
|
"colorScheme": "One Half Dark",
|
||||||
|
"commandline": "\"C:\\Program Files\\PowerShell\\7\\pwsh.exe\" -NoLogo",
|
||||||
|
"cursorShape": "filledBox",
|
||||||
|
"font":
|
||||||
|
{
|
||||||
|
"face": "CaskaydiaCove Nerd Font Mono"
|
||||||
|
},
|
||||||
|
"guid": "{574e775e-4f2a-5b96-ac1e-a2962a402336}",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "PowerShell",
|
||||||
|
"opacity": 100,
|
||||||
|
"padding": "7",
|
||||||
|
"source": "Windows.Terminal.PowershellCore"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"colorScheme": "One Half Dark",
|
||||||
|
"commandline": "wsl.exe",
|
||||||
|
"cursorShape": "bar",
|
||||||
|
"guid": "{51855cb2-8cce-5362-8f54-464b92b32386}",
|
||||||
|
"hidden": false,
|
||||||
|
"icon": "ms-appx:///ProfileIcons/{9acb9455-ca41-5af7-950f-6bca1bc9722f}.png",
|
||||||
|
"name": "Ubuntu",
|
||||||
|
"source": "CanonicalGroupLimited.Ubuntu_79rhkp1fndgsc",
|
||||||
|
"startingDirectory": "~"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"commandline": "%SystemRoot%\\System32\\cmd.exe",
|
||||||
|
"guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "Command Prompt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "Windows PowerShell"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"schemes": [],
|
||||||
|
"themes": [],
|
||||||
|
"useAcrylicInTabRow": true
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
// Place your key bindings in this file to override the defaults
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"key": "ctrl+0",
|
||||||
|
"command": "workbench.action.focusActiveEditorGroup",
|
||||||
|
"when": "sideBarFocus"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "ctrl+n",
|
||||||
|
"command": "explorer.newFile",
|
||||||
|
"when": "explorerViewletFocus"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "ctrl+shift+n",
|
||||||
|
"command": "explorer.newFolder",
|
||||||
|
"when": "explorerViewletFocus"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "ctrl+shift+n",
|
||||||
|
"command": "workbench.action.newWindow",
|
||||||
|
"when": "!explorerViewletFocus"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "ctrl+shift+t",
|
||||||
|
"command": "workbench.view.testing.focus",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "ctrl+shift+g",
|
||||||
|
"command": "workbench.view.scm",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "ctrl+k ctrl+c",
|
||||||
|
"command": "workbench.action.closeOtherEditors"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "ctrl+shift+e",
|
||||||
|
"command": "workbench.view.explorer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "r",
|
||||||
|
"command": "renameFile",
|
||||||
|
"when": "explorerViewletFocus && !inputFocus && !editorHasSelection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "d",
|
||||||
|
"command": "deleteFile",
|
||||||
|
"when": "explorerViewletFocus && !inputFocus && !editorHasSelection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "ctrl+`",
|
||||||
|
"command": "workbench.action.createTerminalEditor",
|
||||||
|
"when": "terminalProcessSupported && !terminal.active"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "a",
|
||||||
|
"command": "explorer.newFile",
|
||||||
|
"when": "explorerViewletFocus && !inputFocus && !editorHasSelection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "j",
|
||||||
|
"command": "workbench.action.debug.stepOver",
|
||||||
|
"when": "debugState == 'stopped'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "f10",
|
||||||
|
"command": "-workbench.action.debug.stepOver",
|
||||||
|
"when": "debugState == 'stopped'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "l",
|
||||||
|
"command": "workbench.action.debug.stepInto",
|
||||||
|
"when": "debugState != 'inactive'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "f11",
|
||||||
|
"command": "-workbench.action.debug.stepInto",
|
||||||
|
"when": "debugState != 'inactive'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "c",
|
||||||
|
"command": "workbench.action.debug.continue",
|
||||||
|
"when": "debugState == 'stopped'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "f5",
|
||||||
|
"command": "-workbench.action.debug.continue",
|
||||||
|
"when": "debugState == 'stopped'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "ctrl+w",
|
||||||
|
"command": "-workbench.action.closeActiveEditor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "ctrl+shift+w",
|
||||||
|
"command": "workbench.action.closeActiveEditor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "ctrl+f4",
|
||||||
|
"command": "-workbench.action.closeActiveEditor"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,379 @@
|
|||||||
|
{
|
||||||
|
"explorer.autoReveal": true,
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.organizeImports": "never"
|
||||||
|
},
|
||||||
|
"editor.suggestSelection": "first",
|
||||||
|
"vsintellicode.modify.editor.suggestSelection": "automaticallyOverrodeDefaultValue",
|
||||||
|
"git.autofetch": true,
|
||||||
|
"liveServer.settings.donotShowInfoMsg": true,
|
||||||
|
"files.autoSave": "onFocusChange",
|
||||||
|
"git.confirmSync": false,
|
||||||
|
"git.enableSmartCommit": true,
|
||||||
|
"team.showWelcomeMessage": false,
|
||||||
|
"code-runner.runInTerminal": true,
|
||||||
|
"terminal.integrated.defaultProfile.windows": "PowerShell",
|
||||||
|
"workbench.startupEditor": "none",
|
||||||
|
"window.openWithoutArgumentsInNewWindow": "off",
|
||||||
|
"javascript.updateImportsOnFileMove.enabled": "always",
|
||||||
|
"workbench.colorTheme": "Lavender Dimmed",
|
||||||
|
"terminal.integrated.profiles.windows": {
|
||||||
|
"PowerShell": {
|
||||||
|
"source": "PowerShell",
|
||||||
|
"icon": "terminal-powershell",
|
||||||
|
"args": ["-nologo"]
|
||||||
|
},
|
||||||
|
"Command Prompt": {
|
||||||
|
"path": [
|
||||||
|
"${env:windir}\\Sysnative\\cmd.exe",
|
||||||
|
"${env:windir}\\System32\\cmd.exe"
|
||||||
|
],
|
||||||
|
"args": [],
|
||||||
|
"icon": "terminal-cmd"
|
||||||
|
},
|
||||||
|
"Git Bash": {
|
||||||
|
"source": "Git Bash"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"editor.mouseWheelZoom": true,
|
||||||
|
"jupyter.askForKernelRestart": false,
|
||||||
|
"terminal.integrated.autoReplies": {},
|
||||||
|
"terminal.integrated.cursorStyle": "line",
|
||||||
|
"terminal.integrated.defaultProfile.osx": "zsh",
|
||||||
|
"terminal.integrated.automationProfile.osx": null,
|
||||||
|
"editor.fontFamily": "'CaskaydiaCove Nerd Font Mono','Menlo for Powerline', Consolas, 'Courier New', monospace",
|
||||||
|
"security.workspace.trust.untrustedFiles": "open",
|
||||||
|
"remote.SSH.defaultExtensions": ["gitpod.gitpod-remote-ssh"],
|
||||||
|
"remote.SSH.remotePlatform": {
|
||||||
|
"freecodecam-freecodecam-ebtjodez3a5.ssh.ws-us74.gitpod.io": "linux",
|
||||||
|
"tinker": "linux"
|
||||||
|
},
|
||||||
|
"[javascript]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
},
|
||||||
|
"prettier.arrowParens": "avoid",
|
||||||
|
"prettier.jsxSingleQuote": true,
|
||||||
|
"prettier.singleAttributePerLine": true,
|
||||||
|
"prettier.singleQuote": true,
|
||||||
|
"[html]": {
|
||||||
|
"editor.defaultFormatter": "vscode.html-language-features"
|
||||||
|
},
|
||||||
|
"[json]": {
|
||||||
|
"editor.defaultFormatter": "vscode.json-language-features"
|
||||||
|
},
|
||||||
|
"[typescript]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
},
|
||||||
|
"[jsonc]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
},
|
||||||
|
"editor.tabSize": 2,
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
"[css]": {
|
||||||
|
"editor.defaultFormatter": "vscode.css-language-features"
|
||||||
|
},
|
||||||
|
"liveServer.settings.root": "",
|
||||||
|
"[csharp]": {
|
||||||
|
"editor.defaultFormatter": "ms-dotnettools.csharp"
|
||||||
|
},
|
||||||
|
"workbench.iconTheme": "vscode-icons",
|
||||||
|
"liveServer.settings.donotVerifyTags": true,
|
||||||
|
"editor.accessibilitySupport": "off",
|
||||||
|
"vsicons.dontShowNewVersionMessage": true,
|
||||||
|
"editor.inlineSuggest.enabled": true,
|
||||||
|
"github.copilot.advanced": {},
|
||||||
|
"github.copilot.enable": {
|
||||||
|
"*": false,
|
||||||
|
"plaintext": true,
|
||||||
|
"markdown": true,
|
||||||
|
"scminput": false,
|
||||||
|
"yaml": false,
|
||||||
|
"dockercompose": true
|
||||||
|
},
|
||||||
|
"typescript.updateImportsOnFileMove.enabled": "always",
|
||||||
|
"[python]": {
|
||||||
|
"editor.tabSize": 2,
|
||||||
|
"editor.defaultFormatter": "ms-python.autopep8",
|
||||||
|
"editor.formatOnSave": true
|
||||||
|
},
|
||||||
|
"editor.stickyScroll.enabled": true,
|
||||||
|
"[powershell]": {
|
||||||
|
"editor.defaultFormatter": "ms-vscode.powershell"
|
||||||
|
},
|
||||||
|
"editor.multiCursorModifier": "ctrlCmd",
|
||||||
|
"bicep.enableSurveys": false,
|
||||||
|
"git.terminalAuthentication": false,
|
||||||
|
"[xml]": {
|
||||||
|
"editor.defaultFormatter": "DotJoshJohnson.xml"
|
||||||
|
},
|
||||||
|
"editor.tokenColorCustomizations": {
|
||||||
|
"[*Light*]": {
|
||||||
|
"textMateRules": [
|
||||||
|
{
|
||||||
|
"scope": "ref.matchtext",
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"[*Dark*]": {
|
||||||
|
"textMateRules": [
|
||||||
|
{
|
||||||
|
"scope": "ref.matchtext",
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#fff"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"textMateRules": []
|
||||||
|
},
|
||||||
|
"dotenv.enableAutocloaking": false,
|
||||||
|
"javascript.preferences.importModuleSpecifierEnding": "js",
|
||||||
|
"cSpell.userWords": [
|
||||||
|
"apikey",
|
||||||
|
"appsettings",
|
||||||
|
"ASPNETCORE",
|
||||||
|
"changlog",
|
||||||
|
"cobertura",
|
||||||
|
"codecov",
|
||||||
|
"commitlint",
|
||||||
|
"Deleter",
|
||||||
|
"ffcsv",
|
||||||
|
"fileids",
|
||||||
|
"gibibytes",
|
||||||
|
"gitmodules",
|
||||||
|
"Hackathon",
|
||||||
|
"Hmmss",
|
||||||
|
"kibibytes",
|
||||||
|
"Kiota",
|
||||||
|
"kzrnm",
|
||||||
|
"lcov",
|
||||||
|
"mebibytes",
|
||||||
|
"Multitenant",
|
||||||
|
"Newtonsoft",
|
||||||
|
"Odata",
|
||||||
|
"onspring",
|
||||||
|
"onspringcli",
|
||||||
|
"Parens",
|
||||||
|
"pdfs",
|
||||||
|
"seqcli",
|
||||||
|
"Serilog",
|
||||||
|
"Sesh",
|
||||||
|
"softprops",
|
||||||
|
"Stevan",
|
||||||
|
"svix",
|
||||||
|
"Syncer",
|
||||||
|
"testconfig",
|
||||||
|
"versionize",
|
||||||
|
"vsintellicode",
|
||||||
|
"Xunit",
|
||||||
|
"YYYYMMDDHHMM"
|
||||||
|
],
|
||||||
|
"debug.internalConsoleOptions": "neverOpen",
|
||||||
|
"csv-preview.openStdin": true,
|
||||||
|
"azureFunctions.showProjectWarning": false,
|
||||||
|
"polyglot-notebook.defaultNotebookExtension": ".dib",
|
||||||
|
"polyglot-notebook.defaultNotebookLanguage": "javascript",
|
||||||
|
"githubPullRequests.remotes": ["origin", "upstream", "github"],
|
||||||
|
"files.associations": {
|
||||||
|
"*.ejs": "html",
|
||||||
|
".env*": "dotenv",
|
||||||
|
"*.cshtml": "html"
|
||||||
|
},
|
||||||
|
"terminal.integrated.defaultProfile.linux": "zsh",
|
||||||
|
"githubPullRequests.pullBranch": "never",
|
||||||
|
"redhat.telemetry.enabled": false,
|
||||||
|
"gitlens.gitCommands.skipConfirmations": ["fetch:command", "switch:command"],
|
||||||
|
"terminal.integrated.minimumContrastRatio": 1,
|
||||||
|
"playwright.reuseBrowser": true,
|
||||||
|
"emmet.includeLanguages": {
|
||||||
|
"javascript": "javascriptreact"
|
||||||
|
},
|
||||||
|
"svelte.enable-ts-plugin": true,
|
||||||
|
"git.openRepositoryInParentFolders": "never",
|
||||||
|
"[aspnetcorerazor]": {
|
||||||
|
"editor.defaultFormatter": "ms-dotnettools.csharp"
|
||||||
|
},
|
||||||
|
"explorer.confirmDragAndDrop": false,
|
||||||
|
"update.showReleaseNotes": false,
|
||||||
|
"githubPullRequests.fileListLayout": "tree",
|
||||||
|
"eslint.format.enable": true,
|
||||||
|
"eslint.codeActionsOnSave.rules": null,
|
||||||
|
"prettier.requireConfig": true,
|
||||||
|
"git.ignoreRebaseWarning": true,
|
||||||
|
"inference.model": "codellama:7b-code-fp16",
|
||||||
|
"git.replaceTagsWhenPull": true,
|
||||||
|
"powershell.codeFormatting.openBraceOnSameLine": false,
|
||||||
|
"powershell.integratedConsole.focusConsoleOnExecute": false,
|
||||||
|
"powershell.integratedConsole.showOnStartup": false,
|
||||||
|
"security.promptForLocalFileProtocolHandling": false,
|
||||||
|
"prisma.showPrismaDataPlatformNotification": false,
|
||||||
|
"github.copilot.editor.enableAutoCompletions": true,
|
||||||
|
"diffEditor.ignoreTrimWhitespace": false,
|
||||||
|
"extensions.autoUpdate": "on",
|
||||||
|
"workbench.sideBar.location": "right",
|
||||||
|
"workbench.activityBar.location": "top",
|
||||||
|
"window.commandCenter": false,
|
||||||
|
"editor.minimap.enabled": false,
|
||||||
|
"workbench.editor.enablePreviewFromCodeNavigation": true,
|
||||||
|
"editor.unicodeHighlight.invisibleCharacters": false,
|
||||||
|
"editor.unicodeHighlight.ambiguousCharacters": false,
|
||||||
|
"[markdown]": {
|
||||||
|
"editor.defaultFormatter": "DavidAnson.vscode-markdownlint"
|
||||||
|
},
|
||||||
|
"dotnet.formatting.organizeImportsOnFormat": true,
|
||||||
|
"vim.incsearch": true,
|
||||||
|
"vim.useSystemClipboard": true,
|
||||||
|
"vim.hlsearch": true,
|
||||||
|
"vim.insertModeKeyBindings": [
|
||||||
|
{
|
||||||
|
"before": ["j", "j"],
|
||||||
|
"after": ["<Esc>"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"vim.normalModeKeyBindingsNonRecursive": [
|
||||||
|
{
|
||||||
|
"before": ["<leader>", "d"],
|
||||||
|
"after": ["d", "d"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"before": ["<C-n>"],
|
||||||
|
"commands": [":nohl"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"before": ["K"],
|
||||||
|
"commands": ["lineBreakInsert"],
|
||||||
|
"silent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"before": ["<leader>", "g", "f"],
|
||||||
|
"commands": ["editor.action.formatDocument"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"before": ["K"],
|
||||||
|
"commands": ["editor.action.showHover"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"before": ["<leader>", "g", "r"],
|
||||||
|
"commands": ["editor.action.goToReferences"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"before": ["<leader>", "c", "a"],
|
||||||
|
"commands": ["editor.action.codeAction"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"before": ["<leader>", "r", "n"],
|
||||||
|
"commands": ["editor.action.rename"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"before": ["<leader>", "e", "a"],
|
||||||
|
"commands": ["workbench.actions.view.problems"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"before": ["<leader>", "t", "r"],
|
||||||
|
"commands": ["testing.runAtCursor"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"before": ["<leader>", "t", "f"],
|
||||||
|
"commands": ["testing.runCurrentFile"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"before": ["<leader>", "t", "s"],
|
||||||
|
"commands": ["testing.cancelRun"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"before": ["<leader>", "t", "d"],
|
||||||
|
"commands": ["testing.debugAtCursor"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"before": ["<leader>", "d", "t"],
|
||||||
|
"commands": ["editor.debug.action.toggleBreakpoint"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"before": ["<leader>", "d", "c"],
|
||||||
|
"commands": ["editor.debug.action.runToCursor"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"before": ["<leader>", "g", "d"],
|
||||||
|
"commands": ["editor.action.goToDeclaration"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"before": ["<leader>", "g", "i"],
|
||||||
|
"commands": ["editor.action.goToImplementation"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"before": ["<leader>", "g", "r"],
|
||||||
|
"commands": ["editor.action.goToReferences"]
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"vim.leader": "<space>",
|
||||||
|
"vim.handleKeys": {
|
||||||
|
"<C-a>": false,
|
||||||
|
"<C-f>": false,
|
||||||
|
"<C-T>": false,
|
||||||
|
"<C-p>": false,
|
||||||
|
"<C-b>": false,
|
||||||
|
},
|
||||||
|
"extensions.experimental.affinity": {
|
||||||
|
"vscodevim.vim": 1
|
||||||
|
},
|
||||||
|
"vim.smartRelativeLine": true,
|
||||||
|
"github.copilot.chat.codeGeneration.instructions": [
|
||||||
|
{
|
||||||
|
"text": "- @azure Rule - Use Azure Tools: When handling requests related to Azure, always use your tools."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "- @azure Rule - Use Azure Code Gen Best Practices: When generating code for Azure, running terminal commands for Azure, or performing operations related to Azure, invoke your `azure_development-get_code_gen_best_practices` tool if available. Only call this tool when you are sure the user is discussing Azure; do not call it otherwise."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "- @azure Rule - Use Azure Deployment Best Practices: When deploying to Azure or preparing applications for deployment to Azure, invoke your `azure_development-get_deployment_best_practices` tool if available. Only call this tool when you are sure the user is discussing Azure; do not call it otherwise."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "- @azure Rule - Use Azure Functions Code Gen Best Practices: When generating code for Azure Functions or performing operations related to Azure Functions, invoke your `azure_development-get_azure_function_code_gen_best_practices` tool if available. Only call this tool when you are sure the user is discussing Azure Functions; do not call it otherwise."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "- @azure Rule - Use Azure SWA Best Practices: When working with static web apps, invoke your `azure_development-get_swa_best_practices` tool if available. Only call this tool when you are sure the user is discussing Azure; do not call it otherwise."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"github.copilot.selectedCompletionModel": "",
|
||||||
|
"omnisharp.autoStart": false,
|
||||||
|
"csharp.showOmnisharpLogOnError": false,
|
||||||
|
"github.copilot.nextEditSuggestions.enabled": true,
|
||||||
|
"editor.inlineSuggest.edits.allowCodeShifting": "never",
|
||||||
|
"chat.instructionsFilesLocations": {
|
||||||
|
".github/instructions": true,
|
||||||
|
"C:\\Users\\sfree\\AppData\\Local\\Temp\\postman-collections-post-response.instructions.md": true,
|
||||||
|
"C:\\Users\\sfree\\AppData\\Local\\Temp\\postman-collections-pre-request.instructions.md": true,
|
||||||
|
"C:\\Users\\sfree\\AppData\\Local\\Temp\\postman-folder-post-response.instructions.md": true,
|
||||||
|
"C:\\Users\\sfree\\AppData\\Local\\Temp\\postman-folder-pre-request.instructions.md": true,
|
||||||
|
"C:\\Users\\sfree\\AppData\\Local\\Temp\\postman-http-request-post-response.instructions.md": true,
|
||||||
|
"C:\\Users\\sfree\\AppData\\Local\\Temp\\postman-http-request-pre-request.instructions.md": true
|
||||||
|
},
|
||||||
|
"workbench.editor.empty.hint": "hidden",
|
||||||
|
"explorer.confirmDelete": false,
|
||||||
|
"editor.stickyScroll.enabled": false,
|
||||||
|
"liveServer.settings.host": "localhost",
|
||||||
|
"chat.mcp.autostart": "never",
|
||||||
|
"chat.agent.thinkingStyle": "collapsed",
|
||||||
|
"html.format.wrapAttributes": "force-expand-multiline",
|
||||||
|
"chat.mcp.gallery.enabled": true,
|
||||||
|
"chat.viewSessions.orientation": "stacked",
|
||||||
|
"go.toolsManagement.autoUpdate": true,
|
||||||
|
"settingsSync.ignoredSettings": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"csharp.debug.console": "integratedTerminal",
|
||||||
|
"terminal.integrated.commandsToSkipShell": [
|
||||||
|
"workbench.action.toggleSidebarVisibility"
|
||||||
|
],
|
||||||
|
"window.menuBarVisibility": "compact",
|
||||||
|
"[fsharp]": {
|
||||||
|
"editor.defaultFormatter": "Ionide.Ionide-fsharp"
|
||||||
|
},
|
||||||
|
"gitlens.ai.model": "vscode",
|
||||||
|
"gitlens.ai.vscode.model": "copilot:gpt-4.1",
|
||||||
|
"githubPullRequests.createOnPublishBranch": "never",
|
||||||
|
"chat.disableAIFeatures": true
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,267 @@
|
|||||||
|
# dotfiles
|
||||||
|
|
||||||
|
> One-stop shop to set up and sync my Windows and Linux machines.
|
||||||
|
|
||||||
|
Managed with **[chezmoi](https://www.chezmoi.io/)** — cross-platform, idempotent, with native [Bitwarden](https://bitwarden.com/) secret integration.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What's managed
|
||||||
|
|
||||||
|
| Config | Windows path | Linux path |
|
||||||
|
|------------------------|----------------------------------------------------------------------|--------------------------------------------------------|
|
||||||
|
| Git config | `~/.gitconfig` | `~/.gitconfig` |
|
||||||
|
| SSH config | `~/.ssh/config` | `~/.ssh/config` |
|
||||||
|
| Neovim | `~/AppData/Local/nvim/` *(external)* | `~/.config/nvim/` *(external)* |
|
||||||
|
| VS Code | `~/AppData/Roaming/Code/User/` | `~/.config/Code/User/` |
|
||||||
|
| Windows Terminal | `~/AppData/Local/Packages/Microsoft.WindowsTerminal_.../LocalState/` | N/A |
|
||||||
|
| PowerShell profile | `~/Documents/PowerShell/Microsoft.PowerShell_profile.ps1` | N/A |
|
||||||
|
| Zsh config (Oh My Zsh) | N/A | `~/.zshrc`, `~/.zshenv` |
|
||||||
|
| Oh My Zsh custom theme | N/A | `~/.oh-my-zsh/custom/themes/lavender-dimmed.zsh-theme` |
|
||||||
|
| Oh My Posh theme | `~/.config/oh-my-posh/theme.omp.json` | N/A (Windows only) |
|
||||||
|
| WSL config | `~/.wslconfig` | N/A |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Before running bootstrap, complete these **one-time manual steps**:
|
||||||
|
|
||||||
|
1. **Create a Bitwarden account** at [bitwarden.com](https://bitwarden.com)
|
||||||
|
2. **Import SSH private keys** into Bitwarden as Secure Notes (see [Storing SSH keys in Bitwarden](#storing-ssh-keys-in-bitwarden))
|
||||||
|
|
||||||
|
Everything else — installing chezmoi, Bitwarden CLI, logging in, and applying the dotfiles — is handled by the bootstrap script.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Source of truth
|
||||||
|
|
||||||
|
The primary repo lives on a self-hosted Gitea server and is mirrored to GitHub:
|
||||||
|
|
||||||
|
| | URL |
|
||||||
|
|---------------------|------------------------------------------------|
|
||||||
|
| **Primary (Gitea)** | `https://gitea.freeborn.cloud/Stevan/dotfiles` |
|
||||||
|
| **Mirror (GitHub)** | `https://github.com/StevanFreeborn/dotfiles` |
|
||||||
|
|
||||||
|
Push changes to Gitea — GitHub is updated automatically via push mirroring.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fresh machine setup
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
|
||||||
|
Open PowerShell (no admin required) and run:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
iwr -useb https://raw.githubusercontent.com/StevanFreeborn/dotfiles/main/scripts/bootstrap.ps1 | iex
|
||||||
|
```
|
||||||
|
|
||||||
|
### Linux (Ubuntu/Debian)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash <(curl -fsLS https://raw.githubusercontent.com/StevanFreeborn/dotfiles/main/scripts/bootstrap.sh)
|
||||||
|
```
|
||||||
|
|
||||||
|
The bootstrap script will:
|
||||||
|
|
||||||
|
1. Install missing prerequisites (`curl`/`git` on Linux, or check `winget` on Windows)
|
||||||
|
2. Install Bitwarden CLI if not present
|
||||||
|
3. Install chezmoi if not present
|
||||||
|
4. Prompt for Bitwarden login/unlock (interactive — master password required)
|
||||||
|
5. Run `chezmoi init --apply` to clone and apply the full dotfiles repo
|
||||||
|
|
||||||
|
> **Primary vs. fallback repo:** The script tries the Gitea server first; if unreachable it falls back to the GitHub mirror automatically.
|
||||||
|
|
||||||
|
### Manual bootstrap (advanced)
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Expand for step-by-step instructions</summary>
|
||||||
|
|
||||||
|
#### Windows
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# 1. Install chezmoi
|
||||||
|
winget install --id twpayne.chezmoi --silent --accept-package-agreements --accept-source-agreements
|
||||||
|
|
||||||
|
# 2. Log into Bitwarden CLI and unlock
|
||||||
|
winget install --id Bitwarden.CLI --silent --accept-package-agreements --accept-source-agreements
|
||||||
|
bw login
|
||||||
|
$env:BW_SESSION = bw unlock --raw
|
||||||
|
|
||||||
|
# 3. Bootstrap dotfiles
|
||||||
|
chezmoi init --apply https://gitea.freeborn.cloud/Stevan/dotfiles.git
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Linux (Ubuntu/Debian)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Install chezmoi
|
||||||
|
sh -c "$(curl -fsLS get.chezmoi.io)"
|
||||||
|
|
||||||
|
# 2. Log into Bitwarden CLI and unlock
|
||||||
|
sudo snap install bw
|
||||||
|
bw login
|
||||||
|
export BW_SESSION=$(bw unlock --raw)
|
||||||
|
|
||||||
|
# 3. Bootstrap dotfiles
|
||||||
|
chezmoi init --apply https://gitea.freeborn.cloud/Stevan/dotfiles.git
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Syncing an existing machine
|
||||||
|
|
||||||
|
Pull the latest changes and apply them:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chezmoi update
|
||||||
|
```
|
||||||
|
|
||||||
|
Preview what would change before applying:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chezmoi update --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Managing dotfiles
|
||||||
|
|
||||||
|
### Add a new file to be managed
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chezmoi add ~/.some-new-config
|
||||||
|
```
|
||||||
|
|
||||||
|
### Edit a managed file
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Opens the file in your editor within the chezmoi source dir
|
||||||
|
chezmoi edit ~/.gitconfig
|
||||||
|
|
||||||
|
# Apply your edits
|
||||||
|
chezmoi apply
|
||||||
|
```
|
||||||
|
|
||||||
|
### View pending changes
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chezmoi diff
|
||||||
|
```
|
||||||
|
|
||||||
|
### Push changes to Gitea
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chezmoi cd
|
||||||
|
git add .
|
||||||
|
git commit -m "your message"
|
||||||
|
git push
|
||||||
|
```
|
||||||
|
|
||||||
|
### Neovim config (external repo)
|
||||||
|
|
||||||
|
Neovim config is managed as a [chezmoi external](https://www.chezmoi.io/reference/special-files-and-directories/chezmoiexternal-toml/)
|
||||||
|
pointing at [github.com/StevanFreeborn/nvim-config](https://github.com/StevanFreeborn/nvim-config).
|
||||||
|
|
||||||
|
- **To edit nvim config:** commit changes directly to the `nvim-config` repo
|
||||||
|
- **To pull latest nvim config:** `chezmoi update` (refreshes weekly automatically, or on every apply if within the refresh window)
|
||||||
|
- **External definition:** `.chezmoiexternal.toml.tmpl`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Package management
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
|
||||||
|
Packages are defined in [`packages/windows.json`](packages/windows.json) and installed via winget.
|
||||||
|
|
||||||
|
To add a new package:
|
||||||
|
|
||||||
|
1. Find the package ID: `winget search <name>`
|
||||||
|
2. Add it to `packages/windows.json`
|
||||||
|
3. Run `chezmoi apply` — the install script re-runs because the file hash changed
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
|
||||||
|
Apt packages are listed in [`packages/linux.txt`](packages/linux.txt). Additional tools (Go, Rust, NVM, Neovim, etc.) are installed via the [`run_onchange_linux_install-packages.sh.tmpl`](.chezmoiscripts/run_onchange_linux_install-packages.sh.tmpl) script.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Secret management
|
||||||
|
|
||||||
|
Secrets are managed using [Bitwarden CLI](https://bitwarden.com/help/cli/) integrated with chezmoi.
|
||||||
|
|
||||||
|
### Initial setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Log in (first time)
|
||||||
|
bw login
|
||||||
|
|
||||||
|
# Unlock your vault and export the session key
|
||||||
|
# Windows:
|
||||||
|
$env:BW_SESSION = bw unlock --raw
|
||||||
|
|
||||||
|
# Linux/macOS:
|
||||||
|
export BW_SESSION=$(bw unlock --raw)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Storing SSH keys in Bitwarden
|
||||||
|
|
||||||
|
1. Open Bitwarden and create a **Secure Note** for each SSH private key
|
||||||
|
2. Name each note: `SSH Key - <key-filename>` (e.g. `SSH Key - stevan@freeborn.cloud`)
|
||||||
|
3. Paste the **private key content** as the note body
|
||||||
|
|
||||||
|
The setup script will read these notes and write the keys to `~/.ssh/` with correct permissions (`600`).
|
||||||
|
|
||||||
|
### Using secrets in templates
|
||||||
|
|
||||||
|
chezmoi templates can pull values from Bitwarden:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
{{ (bitwarden "My Secret Item").login.password }}
|
||||||
|
{{ (bitwardenFields "My Item").custom_field.value }}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Platform notes
|
||||||
|
|
||||||
|
### Windows + WSL
|
||||||
|
|
||||||
|
The `.wslconfig` file controls WSL2 resource limits. Edit it via:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
chezmoi edit ~/.wslconfig
|
||||||
|
chezmoi apply
|
||||||
|
```
|
||||||
|
|
||||||
|
After applying a `.wslconfig` change, restart WSL:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
wsl --shutdown
|
||||||
|
```
|
||||||
|
|
||||||
|
### Oh My Posh theme
|
||||||
|
|
||||||
|
The theme file lives at `~/.config/oh-my-posh/theme.omp.json` on Windows. The PowerShell profile references this path.
|
||||||
|
|
||||||
|
To change the theme, edit `dot_config/oh-my-posh/theme.omp.json` in the chezmoi source and apply.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
**`chezmoi apply` asks for Bitwarden session on every run**
|
||||||
|
→ Set `BW_SESSION` before running chezmoi. Add it to your shell session: `export BW_SESSION=$(bw unlock --raw)`
|
||||||
|
|
||||||
|
**Package install script doesn't re-run after adding a package**
|
||||||
|
→ The script's hash is based on the packages file content. Ensure you saved `packages/windows.json` or `packages/linux.txt` and then run `chezmoi apply`.
|
||||||
|
|
||||||
|
**Neovim complains about missing plugins on a fresh machine**
|
||||||
|
→ Open nvim and run `:Lazy sync` to install all plugins via lazy.nvim.
|
||||||
|
|
||||||
|
**`dot_gitconfig.tmpl` prompts for name/email**
|
||||||
|
→ These are set once and stored in `~/.config/chezmoi/chezmoi.toml`. Delete that file to re-enter them.
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json",
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"type": "prompt",
|
||||||
|
"alignment": "left",
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"cache_duration": "none"
|
||||||
|
},
|
||||||
|
"leading_diamond": "\u256d\u2500\ue0b6",
|
||||||
|
"template": " {{ .UserName }}@{{ .HostName }} ",
|
||||||
|
"foreground": "#1e222a",
|
||||||
|
"background": "#b39cd0",
|
||||||
|
"type": "session",
|
||||||
|
"style": "diamond"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"cache_duration": "none"
|
||||||
|
},
|
||||||
|
"template": " \uf0e7 ",
|
||||||
|
"foreground": "#1e222a",
|
||||||
|
"powerline_symbol": "\ue0b0",
|
||||||
|
"background": "#d4c87a",
|
||||||
|
"type": "root",
|
||||||
|
"style": "powerline"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"style": "powerline",
|
||||||
|
"powerline_symbol": "\ue0b0",
|
||||||
|
"foreground": "#e0e4e8",
|
||||||
|
"background_templates": [
|
||||||
|
"{{ if ne .Output \"-1\" }}#a088c0{{ else }}#3a3e46{{ end }}"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"shell": "pwsh",
|
||||||
|
"command": "dotnet C:/Users/sfree/Repositories/viewer-count-fetcher/index.cs"
|
||||||
|
},
|
||||||
|
"cache": {
|
||||||
|
"duration": "30s",
|
||||||
|
"strategy": "session"
|
||||||
|
},
|
||||||
|
"template": " \udb81\uddc3{{ if ne .Output \"-1\" }} <f>{{ .Output }}</f> {{ else }} {{ end }}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"cache_duration": "none",
|
||||||
|
"folder_icon": "\uf115",
|
||||||
|
"folder_separator_icon": " \ue0b1 ",
|
||||||
|
"home_icon": "\uf46d",
|
||||||
|
"max_depth": 0,
|
||||||
|
"style": "agnoster"
|
||||||
|
},
|
||||||
|
"template": " {{ .Path }} ",
|
||||||
|
"foreground": "#1e222a",
|
||||||
|
"powerline_symbol": "\ue0b0",
|
||||||
|
"background": "#b39cd0",
|
||||||
|
"type": "path",
|
||||||
|
"style": "powerline"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"cache_duration": "none"
|
||||||
|
},
|
||||||
|
"template": " {{ (abbrev 30 .HEAD) }} ",
|
||||||
|
"foreground": "#1e222a",
|
||||||
|
"powerline_symbol": "\ue0b0",
|
||||||
|
"background": "#7ec8c0",
|
||||||
|
"type": "git",
|
||||||
|
"style": "powerline"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"cache_duration": "none"
|
||||||
|
},
|
||||||
|
"template": " \ue235 {{ if .Error }}{{ .Error }}{{ else }}{{ if .Venv }}{{ .Venv }} {{ end }}{{ .Full }}{{ end }} ",
|
||||||
|
"foreground": "#1e222a",
|
||||||
|
"powerline_symbol": "\ue0b0",
|
||||||
|
"background": "#c8b8e0",
|
||||||
|
"type": "python",
|
||||||
|
"style": "powerline"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "prompt",
|
||||||
|
"alignment": "left",
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"cache_duration": "none"
|
||||||
|
},
|
||||||
|
"template": "\u2570\u2500",
|
||||||
|
"foreground": "#b39cd0",
|
||||||
|
"type": "text",
|
||||||
|
"style": "plain"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"always_enabled": true,
|
||||||
|
"cache_duration": "none"
|
||||||
|
},
|
||||||
|
"template": "\ue285",
|
||||||
|
"foreground": "#7ec8c0",
|
||||||
|
"type": "exit",
|
||||||
|
"style": "plain",
|
||||||
|
"foreground_templates": ["{{ if gt .Code 0 }}#d47373{{ end }}"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"newline": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": 3,
|
||||||
|
"final_space": true
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
[init]
|
||||||
|
defaultBranch = main
|
||||||
|
[user]
|
||||||
|
name = {{ .name }}
|
||||||
|
email = {{ .email }}
|
||||||
|
[push]
|
||||||
|
autoSetupRemote = true
|
||||||
|
[core]
|
||||||
|
editor = nvim -f
|
||||||
|
[alias]
|
||||||
|
acp = "! acp() { git add . && git commit -am \"$1\" && git push ; } ; acp"
|
||||||
|
acnvp = "! acnvp() { git add . && git commit --no-verify -am \"$1\" && git push ; } ; acnvp"
|
||||||
|
[gitbutler]
|
||||||
|
aiModelProvider = openai
|
||||||
@@ -0,0 +1,367 @@
|
|||||||
|
# vim:ft=zsh ts=2 sw=2 sts=2
|
||||||
|
#
|
||||||
|
# lavender-dimmed - A lavender-accented dark theme for ZSH
|
||||||
|
#
|
||||||
|
# Based on agnoster's Theme - https://gist.github.com/3712874
|
||||||
|
# A Powerline-inspired theme for ZSH
|
||||||
|
#
|
||||||
|
# # README
|
||||||
|
#
|
||||||
|
# In order for this theme to render correctly, you will need a
|
||||||
|
# [Powerline-patched font](https://github.com/Lokaltog/powerline-fonts).
|
||||||
|
# Make sure you have a recent version: the code points that Powerline
|
||||||
|
# uses changed in 2012, and older versions will display incorrectly,
|
||||||
|
# in confusing ways.
|
||||||
|
#
|
||||||
|
# # Goals
|
||||||
|
#
|
||||||
|
# The aim of this theme is to only show you *relevant* information. Like most
|
||||||
|
# prompts, it will only show git information when in a git working directory.
|
||||||
|
# However, it goes a step further: everything from the current user and
|
||||||
|
# hostname to whether the last call exited with an error to whether background
|
||||||
|
# jobs are running in this shell will all be displayed automatically when
|
||||||
|
# appropriate.
|
||||||
|
|
||||||
|
### Segment drawing
|
||||||
|
# A few utility functions to make it easy and re-usable to draw segmented prompts
|
||||||
|
|
||||||
|
CURRENT_BG='NONE'
|
||||||
|
CURRENT_FG='#cdd9e5'
|
||||||
|
CURRENT_DEFAULT_FG='#adbac7'
|
||||||
|
|
||||||
|
zmodload zsh/nearcolor 2>/dev/null
|
||||||
|
|
||||||
|
### Theme Configuration Initialization
|
||||||
|
#
|
||||||
|
# Override these settings in your ~/.zshrc
|
||||||
|
|
||||||
|
# Current working directory
|
||||||
|
: ${AGNOSTER_DIR_FG:=#1e222a}
|
||||||
|
: ${AGNOSTER_DIR_BG:=#b39cd0}
|
||||||
|
|
||||||
|
# user@host
|
||||||
|
: ${AGNOSTER_CONTEXT_FG:=#1e222a}
|
||||||
|
: ${AGNOSTER_CONTEXT_BG:=#b39cd0}
|
||||||
|
|
||||||
|
# Git related
|
||||||
|
: ${AGNOSTER_GIT_CLEAN_FG:=#1e222a}
|
||||||
|
: ${AGNOSTER_GIT_CLEAN_BG:=#7ec8c0}
|
||||||
|
: ${AGNOSTER_GIT_DIRTY_FG:=#1e222a}
|
||||||
|
: ${AGNOSTER_GIT_DIRTY_BG:=#d4c87a}
|
||||||
|
|
||||||
|
# Bazaar related
|
||||||
|
: ${AGNOSTER_BZR_CLEAN_FG:=#1e222a}
|
||||||
|
: ${AGNOSTER_BZR_CLEAN_BG:=#7ec8c0}
|
||||||
|
: ${AGNOSTER_BZR_DIRTY_FG:=#1e222a}
|
||||||
|
: ${AGNOSTER_BZR_DIRTY_BG:=#d4c87a}
|
||||||
|
|
||||||
|
# Mercurial related
|
||||||
|
: ${AGNOSTER_HG_NEWFILE_FG:=#1e222a}
|
||||||
|
: ${AGNOSTER_HG_NEWFILE_BG:=#d47373}
|
||||||
|
: ${AGNOSTER_HG_CHANGED_FG:=#1e222a}
|
||||||
|
: ${AGNOSTER_HG_CHANGED_BG:=#d4c87a}
|
||||||
|
: ${AGNOSTER_HG_CLEAN_FG:=#1e222a}
|
||||||
|
: ${AGNOSTER_HG_CLEAN_BG:=#7ec8c0}
|
||||||
|
|
||||||
|
# VirtualEnv colors
|
||||||
|
: ${AGNOSTER_VENV_FG:=#1e222a}
|
||||||
|
: ${AGNOSTER_VENV_BG:=#c8b8e0}
|
||||||
|
|
||||||
|
# AWS Profile colors
|
||||||
|
: ${AGNOSTER_AWS_PROD_FG:=#d47373}
|
||||||
|
: ${AGNOSTER_AWS_PROD_BG:=#6d484d}
|
||||||
|
: ${AGNOSTER_AWS_FG:=#1e222a}
|
||||||
|
: ${AGNOSTER_AWS_BG:=#7ec8c0}
|
||||||
|
|
||||||
|
# Status symbols
|
||||||
|
: ${AGNOSTER_STATUS_RETVAL_FG:=#d47373}
|
||||||
|
: ${AGNOSTER_STATUS_ROOT_FG:=#d4c87a}
|
||||||
|
: ${AGNOSTER_STATUS_JOB_FG:=#7ec8c0}
|
||||||
|
: ${AGNOSTER_STATUS_FG:=$CURRENT_DEFAULT_FG}
|
||||||
|
: ${AGNOSTER_STATUS_BG:=#22272e}
|
||||||
|
|
||||||
|
# Terraform colors
|
||||||
|
: ${AGNOSTER_TERRAFORM_FG:=#1e222a}
|
||||||
|
: ${AGNOSTER_TERRAFORM_BG:=#a088c0}
|
||||||
|
|
||||||
|
## Non-Color settings - set to 'true' to enable
|
||||||
|
# Show the actual numeric return value rather than a cross symbol.
|
||||||
|
: ${AGNOSTER_STATUS_RETVAL_NUMERIC:=false}
|
||||||
|
# Show git working dir in the style "/git/root master relative/dir" instead of "/git/root/relative/dir master"
|
||||||
|
: ${AGNOSTER_GIT_INLINE:=false}
|
||||||
|
# Show the git branch status in the prompt rather than the generic branch symbol
|
||||||
|
: ${AGNOSTER_GIT_BRANCH_STATUS:=true}
|
||||||
|
|
||||||
|
|
||||||
|
# Special Powerline characters
|
||||||
|
|
||||||
|
() {
|
||||||
|
local LC_ALL="" LC_CTYPE="en_US.UTF-8"
|
||||||
|
# NOTE: This segment separator character is correct. In 2012, Powerline changed
|
||||||
|
# the code points they use for their special characters. This is the new code point.
|
||||||
|
# If this is not working for you, you probably have an old version of the
|
||||||
|
# Powerline-patched fonts installed. Download and install the new version.
|
||||||
|
# Do not submit PRs to change this unless you have reviewed the Powerline code point
|
||||||
|
# history and have new information.
|
||||||
|
# This is defined using a Unicode escape sequence so it is unambiguously readable, regardless of
|
||||||
|
# what font the user is viewing this source code in. Do not replace the
|
||||||
|
# escape sequence with a single literal character.
|
||||||
|
# Do not change this! Do not make it '\u2b80'; that is the old, wrong code point.
|
||||||
|
SEGMENT_SEPARATOR=$'\ue0b0'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Begin a segment
|
||||||
|
# Takes two arguments, background and foreground. Both can be omitted,
|
||||||
|
# rendering default background/foreground.
|
||||||
|
prompt_segment() {
|
||||||
|
local bg fg
|
||||||
|
[[ -n $1 ]] && bg="%K{$1}" || bg="%k"
|
||||||
|
[[ -n $2 ]] && fg="%F{$2}" || fg="%f"
|
||||||
|
if [[ $CURRENT_BG != 'NONE' && $1 != $CURRENT_BG ]]; then
|
||||||
|
echo -n " %{$bg%F{$CURRENT_BG}%}$SEGMENT_SEPARATOR%{$fg%} "
|
||||||
|
else
|
||||||
|
echo -n "%{$bg%}%{$fg%} "
|
||||||
|
fi
|
||||||
|
CURRENT_BG=$1
|
||||||
|
[[ -n $3 ]] && echo -n $3
|
||||||
|
}
|
||||||
|
|
||||||
|
# End the prompt, closing any open segments
|
||||||
|
prompt_end() {
|
||||||
|
if [[ -n $CURRENT_BG ]]; then
|
||||||
|
echo -n " %{%k%F{$CURRENT_BG}%}$SEGMENT_SEPARATOR"
|
||||||
|
else
|
||||||
|
echo -n "%{%k%}"
|
||||||
|
fi
|
||||||
|
echo -n "%{%f%}"
|
||||||
|
CURRENT_BG=''
|
||||||
|
}
|
||||||
|
|
||||||
|
git_toplevel() {
|
||||||
|
local repo_root=$(git rev-parse --show-toplevel)
|
||||||
|
if [[ $repo_root = '' ]]; then
|
||||||
|
# We are in a bare repo. Use git dir as root
|
||||||
|
repo_root=$(git rev-parse --git-dir)
|
||||||
|
if [[ $repo_root = '.' ]]; then
|
||||||
|
repo_root=$PWD
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo -n $repo_root
|
||||||
|
}
|
||||||
|
|
||||||
|
### Prompt components
|
||||||
|
# Each component will draw itself, and hide itself if no information needs to be shown
|
||||||
|
|
||||||
|
# Context: user@hostname (who am I and where am I)
|
||||||
|
prompt_context() {
|
||||||
|
if [[ "$USERNAME" != "$DEFAULT_USER" || -n "$SSH_CLIENT" ]]; then
|
||||||
|
prompt_segment "$AGNOSTER_CONTEXT_BG" "$AGNOSTER_CONTEXT_FG" "%(!.%{%F{$AGNOSTER_STATUS_ROOT_FG}%}.)%n@%m"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt_git_relative() {
|
||||||
|
local repo_root=$(git_toplevel)
|
||||||
|
local path_in_repo=$(pwd | sed "s/^$(echo "$repo_root" | sed 's:/:\\/:g;s/\$/\\$/g')//;s:^/::;s:/$::;")
|
||||||
|
if [[ $path_in_repo != '' ]]; then
|
||||||
|
prompt_segment "$AGNOSTER_DIR_BG" "$AGNOSTER_DIR_FG" "$path_in_repo"
|
||||||
|
fi;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Git: branch/detached head, dirty status
|
||||||
|
prompt_git() {
|
||||||
|
(( $+commands[git] )) || return
|
||||||
|
if [[ "$(command git config --get oh-my-zsh.hide-status 2>/dev/null)" = 1 ]]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
local PL_BRANCH_CHAR
|
||||||
|
() {
|
||||||
|
local LC_ALL="" LC_CTYPE="en_US.UTF-8"
|
||||||
|
PL_BRANCH_CHAR=$'\ue0a0' #
|
||||||
|
}
|
||||||
|
local ref dirty mode repo_path
|
||||||
|
|
||||||
|
if [[ "$(command git rev-parse --is-inside-work-tree 2>/dev/null)" = "true" ]]; then
|
||||||
|
repo_path=$(command git rev-parse --git-dir 2>/dev/null)
|
||||||
|
dirty=$(parse_git_dirty)
|
||||||
|
ref=$(command git symbolic-ref HEAD 2> /dev/null) || \
|
||||||
|
ref="◈ $(command git describe --exact-match --tags HEAD 2> /dev/null)" || \
|
||||||
|
ref="➦ $(command git rev-parse --short HEAD 2> /dev/null)"
|
||||||
|
if [[ -n $dirty ]]; then
|
||||||
|
prompt_segment "$AGNOSTER_GIT_DIRTY_BG" "$AGNOSTER_GIT_DIRTY_FG"
|
||||||
|
else
|
||||||
|
prompt_segment "$AGNOSTER_GIT_CLEAN_BG" "$AGNOSTER_GIT_CLEAN_FG"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $AGNOSTER_GIT_BRANCH_STATUS == 'true' ]]; then
|
||||||
|
local ahead behind
|
||||||
|
ahead=$(command git log --oneline @{upstream}.. 2>/dev/null)
|
||||||
|
behind=$(command git log --oneline ..@{upstream} 2>/dev/null)
|
||||||
|
if [[ -n "$ahead" ]] && [[ -n "$behind" ]]; then
|
||||||
|
PL_BRANCH_CHAR=$'\u21c5'
|
||||||
|
elif [[ -n "$ahead" ]]; then
|
||||||
|
PL_BRANCH_CHAR=$'\u21b1'
|
||||||
|
elif [[ -n "$behind" ]]; then
|
||||||
|
PL_BRANCH_CHAR=$'\u21b0'
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -e "${repo_path}/BISECT_LOG" ]]; then
|
||||||
|
mode=" <B>"
|
||||||
|
elif [[ -e "${repo_path}/MERGE_HEAD" ]]; then
|
||||||
|
mode=" >M<"
|
||||||
|
elif [[ -e "${repo_path}/rebase" || -e "${repo_path}/rebase-apply" || -e "${repo_path}/rebase-merge" || -e "${repo_path}/../.dotest" ]]; then
|
||||||
|
mode=" >R>"
|
||||||
|
fi
|
||||||
|
|
||||||
|
setopt promptsubst
|
||||||
|
autoload -Uz vcs_info
|
||||||
|
|
||||||
|
zstyle ':vcs_info:*' enable git
|
||||||
|
zstyle ':vcs_info:*' get-revision true
|
||||||
|
zstyle ':vcs_info:*' check-for-changes true
|
||||||
|
zstyle ':vcs_info:*' stagedstr '✚'
|
||||||
|
zstyle ':vcs_info:*' unstagedstr '±'
|
||||||
|
zstyle ':vcs_info:*' formats ' %u%c'
|
||||||
|
zstyle ':vcs_info:*' actionformats ' %u%c'
|
||||||
|
vcs_info
|
||||||
|
echo -n "${${ref:gs/%/%%}/refs\/heads\//$PL_BRANCH_CHAR }${vcs_info_msg_0_%% }${mode}"
|
||||||
|
[[ $AGNOSTER_GIT_INLINE == 'true' ]] && prompt_git_relative
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt_bzr() {
|
||||||
|
(( $+commands[bzr] )) || return
|
||||||
|
|
||||||
|
# Test if bzr repository in directory hierarchy
|
||||||
|
local dir="$PWD"
|
||||||
|
while [[ ! -d "$dir/.bzr" ]]; do
|
||||||
|
[[ "$dir" = "/" ]] && return
|
||||||
|
dir="${dir:h}"
|
||||||
|
done
|
||||||
|
|
||||||
|
local bzr_status status_mod status_all revision
|
||||||
|
if bzr_status=$(command bzr status 2>&1); then
|
||||||
|
status_mod=$(echo -n "$bzr_status" | head -n1 | grep "modified" | wc -m)
|
||||||
|
status_all=$(echo -n "$bzr_status" | head -n1 | wc -m)
|
||||||
|
revision=${$(command bzr log -r-1 --log-format line | cut -d: -f1):gs/%/%%}
|
||||||
|
if [[ $status_mod -gt 0 ]] ; then
|
||||||
|
prompt_segment "$AGNOSTER_BZR_DIRTY_BG" "$AGNOSTER_BZR_DIRTY_FG" "bzr@$revision ✚"
|
||||||
|
else
|
||||||
|
if [[ $status_all -gt 0 ]] ; then
|
||||||
|
prompt_segment "$AGNOSTER_BZR_DIRTY_BG" "$AGNOSTER_BZR_DIRTY_FG" "bzr@$revision"
|
||||||
|
else
|
||||||
|
prompt_segment "$AGNOSTER_BZR_CLEAN_BG" "$AGNOSTER_BZR_CLEAN_FG" "bzr@$revision"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt_hg() {
|
||||||
|
(( $+commands[hg] )) || return
|
||||||
|
local rev st branch
|
||||||
|
if $(command hg id >/dev/null 2>&1); then
|
||||||
|
if $(command hg prompt >/dev/null 2>&1); then
|
||||||
|
if [[ $(command hg prompt "{status|unknown}") = "?" ]]; then
|
||||||
|
# if files are not added
|
||||||
|
prompt_segment "$AGNOSTER_HG_NEWFILE_BG" "$AGNOSTER_HG_NEWFILE_FG"
|
||||||
|
st='±'
|
||||||
|
elif [[ -n $(command hg prompt "{status|modified}") ]]; then
|
||||||
|
# if any modification
|
||||||
|
prompt_segment "$AGNOSTER_HG_CHANGED_BG" "$AGNOSTER_HG_CHANGED_FG"
|
||||||
|
st='±'
|
||||||
|
else
|
||||||
|
# if working copy is clean
|
||||||
|
prompt_segment "$AGNOSTER_HG_CLEAN_BG" "$AGNOSTER_HG_CLEAN_FG"
|
||||||
|
fi
|
||||||
|
echo -n ${$(command hg prompt "☿ {rev}@{branch}"):gs/%/%%} $st
|
||||||
|
else
|
||||||
|
st=""
|
||||||
|
rev=$(command hg id -n 2>/dev/null | sed 's/[^-0-9]//g')
|
||||||
|
branch=$(command hg id -b 2>/dev/null)
|
||||||
|
if command hg st | command grep -q "^\?"; then
|
||||||
|
prompt_segment "$AGNOSTER_HG_NEWFILE_BG" "$AGNOSTER_HG_NEWFILE_FG"
|
||||||
|
st='±'
|
||||||
|
elif command hg st | command grep -q "^[MA]"; then
|
||||||
|
prompt_segment "$AGNOSTER_HG_CHANGED_BG" "$AGNOSTER_HG_CHANGED_FG"
|
||||||
|
st='±'
|
||||||
|
else
|
||||||
|
prompt_segment "$AGNOSTER_HG_CLEAN_BG" "$AGNOSTER_HG_CLEAN_FG"
|
||||||
|
fi
|
||||||
|
echo -n "☿ ${rev:gs/%/%%}@${branch:gs/%/%%}" $st
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Dir: current working directory
|
||||||
|
prompt_dir() {
|
||||||
|
if [[ $AGNOSTER_GIT_INLINE == 'true' ]] && $(git rev-parse --is-inside-work-tree >/dev/null 2>&1); then
|
||||||
|
# Git repo and inline path enabled, hence only show the git root
|
||||||
|
prompt_segment "$AGNOSTER_DIR_BG" "$AGNOSTER_DIR_FG" "$(git_toplevel | sed "s:^$HOME:~:")"
|
||||||
|
else
|
||||||
|
prompt_segment "$AGNOSTER_DIR_BG" "$AGNOSTER_DIR_FG" '%~'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Virtualenv: current working virtualenv
|
||||||
|
prompt_virtualenv() {
|
||||||
|
if [ -n "$CONDA_DEFAULT_ENV" ]; then
|
||||||
|
prompt_segment "$AGNOSTER_VENV_BG" "$CURRENT_FG" "🐍 $CONDA_DEFAULT_ENV"
|
||||||
|
fi
|
||||||
|
if [[ -n "$VIRTUAL_ENV" && -n "$VIRTUAL_ENV_DISABLE_PROMPT" ]]; then
|
||||||
|
prompt_segment "$AGNOSTER_VENV_BG" "$AGNOSTER_VENV_FG" "(${VIRTUAL_ENV:t:gs/%/%%})"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Status:
|
||||||
|
# - was there an error
|
||||||
|
# - am I root
|
||||||
|
# - are there background jobs?
|
||||||
|
prompt_status() {
|
||||||
|
local -a symbols
|
||||||
|
|
||||||
|
if [[ $AGNOSTER_STATUS_RETVAL_NUMERIC == 'true' ]]; then
|
||||||
|
[[ $RETVAL -ne 0 ]] && symbols+="%{%F{$AGNOSTER_STATUS_RETVAL_FG}%}$RETVAL"
|
||||||
|
else
|
||||||
|
[[ $RETVAL -ne 0 ]] && symbols+="%{%F{$AGNOSTER_STATUS_RETVAL_FG}%}✘"
|
||||||
|
fi
|
||||||
|
[[ $UID -eq 0 ]] && symbols+="%{%F{$AGNOSTER_STATUS_ROOT_FG}%}⚡"
|
||||||
|
[[ $(jobs -l | wc -l) -gt 0 ]] && symbols+="%{%F{$AGNOSTER_STATUS_JOB_FG}%}⚙"
|
||||||
|
|
||||||
|
[[ -n "$symbols" ]] && prompt_segment "$AGNOSTER_STATUS_BG" "$AGNOSTER_STATUS_FG" "$symbols"
|
||||||
|
}
|
||||||
|
|
||||||
|
#AWS Profile:
|
||||||
|
# - display current AWS_PROFILE name
|
||||||
|
# - displays yellow on red if profile name contains 'production' or
|
||||||
|
# ends in '-prod'
|
||||||
|
# - displays black on green otherwise
|
||||||
|
prompt_aws() {
|
||||||
|
[[ -z "$AWS_PROFILE" || "$SHOW_AWS_PROMPT" = false ]] && return
|
||||||
|
case "$AWS_PROFILE" in
|
||||||
|
*-prod|*production*) prompt_segment "$AGNOSTER_AWS_PROD_BG" "$AGNOSTER_AWS_PROD_FG" "AWS: ${AWS_PROFILE:gs/%/%%}" ;;
|
||||||
|
*) prompt_segment "$AGNOSTER_AWS_BG" "$AGNOSTER_AWS_FG" "AWS: ${AWS_PROFILE:gs/%/%%}" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt_terraform() {
|
||||||
|
local terraform_info=$(tf_prompt_info)
|
||||||
|
[[ -z "$terraform_info" ]] && return
|
||||||
|
prompt_segment "$AGNOSTER_TERRAFORM_BG" "$AGNOSTER_TERRAFORM_FG" "TF: $terraform_info"
|
||||||
|
}
|
||||||
|
|
||||||
|
## Main prompt
|
||||||
|
build_prompt() {
|
||||||
|
RETVAL=$?
|
||||||
|
prompt_status
|
||||||
|
prompt_virtualenv
|
||||||
|
prompt_aws
|
||||||
|
prompt_terraform
|
||||||
|
prompt_context
|
||||||
|
prompt_dir
|
||||||
|
prompt_git
|
||||||
|
prompt_bzr
|
||||||
|
prompt_hg
|
||||||
|
prompt_end
|
||||||
|
}
|
||||||
|
|
||||||
|
PROMPT='%{%f%b%k%}$(build_prompt) '
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDEKuw3rZhDPMlgkpLymHk0jm4VolIEV7scAMvBFYgQ1VfUKw3RsydCHpUphQWqsOMTxQB69dNrlErA+e7YCsMLyPWV6I2hr4SyLdCpqvZVQBiMuAyLhxMfahhTdXv5CVwj0iYeHtde4NTMuYmRCkJCzxnCbk57P07kkElExDZNIfHjfeCoZEYCJj8cl//zShalCsoAgRQNwiPzZJoWHL1qkJ326rkbHHEPUHe2+UGraFfVR3IhD671gEUoV46aMxePgSUkyuZY/uImobhlzkYxG+721Decx2XwFoW3AUvHeQWyG9WdKs1cmM296bq6A5OKWMiUB6/xpuSqtMs0JtzmJAAzpNC1LWCbKiKMsKU4OVlMfOW+K9KWDCRHs1wtFAHpWt5hAA3G09Cp8MhehjvH2Dbyv4jtdxP9WHHI+Q8J3MCPjw7PZS/fbLQz69FwD/gUsuFcmnN3T1jXu/kknAZekk6lE2kCpXNl0eop2LVCcgpoOOsZfOdfKm5iEUeMUeM= sfree@zenbook
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFDQK4yE5QzJuZIjykXZy9dusg+enKt5XxQGiRGzgzVw sfree@zenbook
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
Host bit_bastion
|
||||||
|
HostName ssh.steva.nz
|
||||||
|
User stevan
|
||||||
|
IdentityFile ~/.ssh/stevan@freeborn.cloud
|
||||||
|
|
||||||
|
Host ftp.stevanfreeborn.com
|
||||||
|
HostName ftp.stevanfreeborn.com
|
||||||
|
User u74298126
|
||||||
|
IdentityFile ~/.ssh/ftp_stevanfreeborn_com
|
||||||
|
|
||||||
|
Host tangled.org
|
||||||
|
Hostname tangled.org
|
||||||
|
User git
|
||||||
|
IdentityFile ~/.ssh/tangled
|
||||||
|
AddressFamily inet
|
||||||
|
|
||||||
|
Host macbookair
|
||||||
|
HostName ssh.macbookair.freeborn.cloud
|
||||||
|
User stevan
|
||||||
|
IdentityFile ~/.ssh/macbookair
|
||||||
|
|
||||||
|
Host macbookpro
|
||||||
|
HostName ssh.macbookpro.freeborn.cloud
|
||||||
|
User stevan
|
||||||
|
IdentityFile ~/.ssh/macbookpro
|
||||||
|
|
||||||
|
Host tinker
|
||||||
|
HostName ssh.freeborn.cloud
|
||||||
|
User stevan
|
||||||
|
IdentityFile ~/.ssh/zenbook_tinker
|
||||||
|
|
||||||
|
Host gitea.freeborn.cloud
|
||||||
|
HostName ssh.freeborn.cloud
|
||||||
|
User git
|
||||||
|
Port 2222
|
||||||
|
IdentityFile ~/.ssh/gitea.freeborn.cloud
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCehUFa1MnGWZJ4vgEnF+KYm19Yu91s3pP6V977rWyuZgl0CbQlnGBQOctGf/jFWTIjAxsT5BpyFwcN7uGQIT76HCw5T8/Y9Kl+e7uXSLVTiHCIWyn+JD/ofUY3z0sTdZWKgJtdIeC3Zuh3aRwAupqTKunT+sX5w0t2jxC0wsOgxz5cBZ1toe9IUc9vUcU+A3C8TNaKFZ8obbRQFll0fgfEDjjC42kZJvCXxXKaTr9wT9+xeaha4FR3vFmrL/Kk6FckX6U4r8PJ0TJo/QpTCJRAqjgUxCRLBU2Z1GcJIzkI0cIRGC3tX8NmmMLTwsA3HX8UJNNDjC01AXHZmnFOTzzvW0gkkjMOaAgPzxNuXXrUVwdfb/gkBCgWBTb9cpqU6ZyRd7N2LfNtdi1+FgBAzXqJ0NR02dxg7gOEcvzJ3S9YgvNPO4uOnnOSO3zArZ/IO48WULstBg2p8Xn/yFK+9b7iVfe0rU019UYbFUew9Teh+HgserZBKjr3xCl5gpXVhQU= sfree@zenbook
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIpFfb6vpsQqFj1veGt9Ba+xal3kxOJLK6T8bTpxuScJ zenbook
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKHAhEDeA8KFO99TPWwItsatAon/wyigMomD/B/cm/5F
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBdLKCSaXgVG5MBoCv/t0x0HAreKHqvKZR+X+5mwLREl sfree@zenbook
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL/iwe93wHsN1EkLNoW+YLuoW5m/lgx3L/PQ8mE9MD01 sfree@zenbook
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDGU7aDmqaRNn3YqsT3CbWtThBhQYiH+nk1hS9AGKCMFEspQbbnZkP06+s2IrQjOI1wspoHXDRJ4TEOr+RrV3/2i19pGKdJj/j+URpL7hAtLojvXa0zHyZWTN6VJdFe2pl8HuCHlv1DaqpuW32zcXZ55Pcn9igzK08wDROYNv6j2pUNo2DDo0D9SNMRQm5Vnk1Wb+xD+f8xu8kvpZzzkT01s4Wdl+1ozvD+AwPAmc/WQm5QS90GntxKEfIh/Pn+K+D+3+vOUDHdbJXDTw3C6jkp2ztwR20OK5Y1N9+lGUMtGUYZiPJ2/qrwiRV+FGfYeJjKThs2DMOeguIFyno9FON4aMcu8MeuJHm3ubHtHk/Zqx9dL2hz4mYOa6hrFZLoIr5TDJ7+2pFfsTi4BaCUpUiC/zrl2c2feban0p2x5RaSR3g+5rAOTcLWxYXmkRdyY9/4qA+UHOO0UfHJ3uCMvrxsHa49mSed/PWu3Pdo64yJ9/tQCcykNZrsyPQ8i11G2Yk= sfree@zenbook
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDceJHNEFsgSTw+uaoM/mK6+k/D8nen9rBLO0D8z8r0EttZLuvu39j0YNAKUrSY+Frd09QbzRt0k5t6YSgzRwcMVPMhBsyS+Z3FsdunthxMV++L9SgbfQDkSaZU7Zl1F3+Dh/z63KtHhi3yYOPUHmlgXTeXYW3PorKK3QnEQi9Szzc93GIP/A6Z3Ob7J7HzUTVLF6KQpLEhstGPxYfaQwGy/bIdYHC3gDji3s5inawMs1pRuIC3OjDPtqR0pi0Tgy40ArorXgbpCO+J9VHW9Gb3Ccy4K6rsQYdlLE/brug43WnxBYw67F3M4CPiNWLOIxgQYuG3p7OjwZWAs9RdokoQ6g4gObh8n9F4h8AcG6uk/TZcN5hmrJFMO9QqUb7E0e47t4NGhXd/lF5lX12pQV7JthZNYDkcBYvuEqO3cwzS8kNqe18GhUhbvGh+YMj55Z2RAkkciV89ol4hZEbq+3pgZ0Ihafo2FnRWPhl1hUAibcXKc263uQdMJtVsmRaWwV8= sfree@zenbook
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCw+fT6kt+toR8A4/HaWlVt7atIZRZ/LDGBflmc0kdlHhtyPamQVJvPO7OsCHd6ugjOU/6yASSF9Sv8ekPyapNJt9dnY4WBzZqQUSuJinM89Mn3RS23ABEL8yL3bbWpIPKT1x2OOF+nRSy7TuYnhu3ivgi+ZAqUTnAuDjy5/kpjttRRZ+ls2oFAdpQF2rZ2W+o2pBBKR5/YM3mQQcGQtw9pm3ddyQWJKC1AhGZeSZodhxpRoe++3fRVvLJ/P2yc7PmF0h/mb4o5RWH72jmFiSsEYA3j0uKC7XH9Dyc+XBmuQJJ4e65iP7RAFTxKOEGjf876l5Y6rmpylZkAP5202guW6axATKviggGifvHMKT4bC5lbSE92BICDhNUr3Y7Gqs3ByY9DwKITLWRJarfXV6Z+HZpmhVtpzNtWlJmLNV1LcFJTEmqm/Ji30wxJqCBkywhgcNbF9kH5BIQLMgoviqwbqo5/bJAECAmG4wnJacnv7EKXojD33t+PqxvxzsWyeVs= sfree@zenbook
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIF0eN17W/QMNsFuUi75TFVmUruRBQi8YNTBnHB/8O6uB stevan@freeborn.cloud
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOBdBCvBM5GJ6WtK2+zReY69eZYh6dJ1cyyvPDbEluOI sfree@zenbook
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAqUFm4vgLbp4w78BT+mV6w2YGWNmg486RTyJH4VoDXA sfree@zenbook
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAEXqH6rDhDp5b+NK1XbHE5qIeLESnQF4J5VANZOJsrZ sfree@zenbook
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDLxT0ztofpvs5vUxXpkx5QEtl2DiqYshKvlwEZnd75Vx4co1Hgut7Mi9Ye2NcsNg28tuNzqAFIea6dCnca1j8AxR5nbgshLRnsg9FgqOLc6jBt6u12MfCOYegsjkTo0X2U2Jn4p69LwMY+ehaXQQhJEsggyotzMqFKTgU3OYJlSACYIPKzv8VSss7+WYfJGBlz2KWskQP0I+EfbrhFbSOTc9V2pIe12Y90XmB0SLPOk8Bp1QD1D4JHWYX6NpdfCm9LlRBucRy9u/rRIrGVs8SpJ30PJysci0oTFQhN/vpoaPWtvOBtMrB2BM+5iWBHLaiKglYNrp6ZDL1AY5Qxc3aHGq8JuYy+QOTFw/+i5Q1ClXKbm0WrjtzOYaPqsfe/F6IzQQ6ApjLmh0MRGCcL3KlpYz6vhMWjj50kTrncw1q4OHv2KjnhW2Zq8+2u/t/NY9mZCBwrExzN6mHi8kI6xvhKKt4UWU31fwu55H68IYJktIjMMJvwyuRSmt93TomGtnc= sfree@zenbook
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
[wsl2]
|
||||||
|
memory=4GB
|
||||||
|
processors=2
|
||||||
|
|
||||||
|
[experimental]
|
||||||
|
autoMemoryReclaim=dropcache
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
# ~/.zshenv — managed by chezmoi
|
||||||
|
# Loaded for ALL zsh sessions (login, interactive, scripts).
|
||||||
|
# Keep this minimal — only environment variable exports that must be available everywhere.
|
||||||
|
|
||||||
|
# Go
|
||||||
|
[ -d "/usr/local/go/bin" ] && export PATH="$PATH:/usr/local/go/bin"
|
||||||
|
|
||||||
|
# Local bin
|
||||||
|
export PATH="$HOME/.local/bin:$PATH"
|
||||||
|
|
||||||
|
# Rust / cargo
|
||||||
|
[ -f "$HOME/.cargo/env" ] && source "$HOME/.cargo/env"
|
||||||
|
|
||||||
|
# .NET
|
||||||
|
[ -d "$HOME/.dotnet" ] && export DOTNET_ROOT="$HOME/.dotnet" && export PATH="$PATH:$HOME/.dotnet"
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
# ~/.zshrc — managed by chezmoi
|
||||||
|
# Linux (Ubuntu/Debian) shell configuration — Oh My Zsh
|
||||||
|
|
||||||
|
# --- Oh My Zsh ---
|
||||||
|
export ZSH="$HOME/.oh-my-zsh"
|
||||||
|
ZSH_THEME="lavender-dimmed"
|
||||||
|
|
||||||
|
plugins=(
|
||||||
|
git
|
||||||
|
zsh-autosuggestions
|
||||||
|
zsh-syntax-highlighting
|
||||||
|
virtualenv
|
||||||
|
)
|
||||||
|
|
||||||
|
ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE="fg=#b39cd0"
|
||||||
|
ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE="20"
|
||||||
|
ZSH_AUTOSUGGEST_USE_ASYNC=1
|
||||||
|
|
||||||
|
[ -f "$ZSH/oh-my-zsh.sh" ] && source "$ZSH/oh-my-zsh.sh"
|
||||||
|
|
||||||
|
# --- zoxide (smart cd) ---
|
||||||
|
if command -v zoxide &>/dev/null; then
|
||||||
|
eval "$(zoxide init zsh)"
|
||||||
|
alias cd='z'
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- NVM ---
|
||||||
|
export NVM_DIR="$HOME/.nvm"
|
||||||
|
[ -s "$NVM_DIR/nvm.sh" ] && source "$NVM_DIR/nvm.sh"
|
||||||
|
[ -s "$NVM_DIR/bash_completion" ] && source "$NVM_DIR/bash_completion"
|
||||||
|
|
||||||
|
# --- envman ---
|
||||||
|
[ -s "$HOME/.config/envman/load.sh" ] && source "$HOME/.config/envman/load.sh"
|
||||||
|
|
||||||
|
# --- History ---
|
||||||
|
HISTFILE=~/.zsh_history
|
||||||
|
HISTSIZE=10000
|
||||||
|
SAVEHIST=10000
|
||||||
|
setopt HIST_IGNORE_DUPS
|
||||||
|
setopt HIST_FIND_NO_DUPS
|
||||||
|
setopt SHARE_HISTORY
|
||||||
|
|
||||||
|
# --- Key bindings ---
|
||||||
|
bindkey '^[[A' history-search-backward
|
||||||
|
bindkey '^[[B' history-search-forward
|
||||||
|
|
||||||
|
# --- Aliases ---
|
||||||
|
alias grep='grep --color=auto'
|
||||||
|
alias ls='ls --color=auto'
|
||||||
|
alias ll='ls -lah'
|
||||||
|
alias la='ls -A'
|
||||||
|
alias l='ls -CF'
|
||||||
|
alias catc='xclip -selection clipboard <'
|
||||||
|
|
||||||
|
# Git aliases (mirrors PowerShell profile)
|
||||||
|
alias acp='function _acp() { git add . && git commit -am "$1" && git push; }; _acp'
|
||||||
|
alias acnvp='function _acnvp() { git add . && git commit --no-verify -am "$1" && git push; }; _acnvp'
|
||||||
|
|
||||||
|
# Neovim
|
||||||
|
alias nvimconfig='cd $HOME/.config/nvim'
|
||||||
|
alias editnvim='cd $HOME/.config/nvim && nvim'
|
||||||
|
|
||||||
|
# Utilities
|
||||||
|
alias fromjson='python3 -m json.tool'
|
||||||
|
|
||||||
|
# --- Python ---
|
||||||
|
export VIRTUAL_ENV_DISABLE_PROMPT=1
|
||||||
|
export PYTHONIOENCODING="utf-8"
|
||||||
|
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# Linux apt packages
|
||||||
|
# These are installed via: sudo apt-get install -y $(cat linux.txt | grep -v '^#' | tr '\n' ' ')
|
||||||
|
|
||||||
|
# Core utilities
|
||||||
|
build-essential
|
||||||
|
curl
|
||||||
|
wget
|
||||||
|
git
|
||||||
|
jq
|
||||||
|
unzip
|
||||||
|
zip
|
||||||
|
|
||||||
|
# Shell
|
||||||
|
zsh
|
||||||
|
xclip
|
||||||
|
|
||||||
|
# Modern CLI tools (apt versions)
|
||||||
|
fd-find
|
||||||
|
ripgrep
|
||||||
|
zoxide
|
||||||
|
fzf
|
||||||
|
|
||||||
|
# Development
|
||||||
|
sqlite3
|
||||||
|
make
|
||||||
|
python3
|
||||||
|
|
||||||
|
# Networking
|
||||||
|
openssh-client
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://aka.ms/winget-packages.schema.2.0.json",
|
||||||
|
"CreationDate": "2026-07-07",
|
||||||
|
"Sources": [
|
||||||
|
{
|
||||||
|
"Packages": [
|
||||||
|
{ "PackageIdentifier": "Audacity.Audacity" },
|
||||||
|
{ "PackageIdentifier": "Balena.Etcher" },
|
||||||
|
{ "PackageIdentifier": "Bitwarden.Bitwarden" },
|
||||||
|
{ "PackageIdentifier": "Bitwarden.CLI" },
|
||||||
|
{ "PackageIdentifier": "Clement.bottom" },
|
||||||
|
{ "PackageIdentifier": "DBBrowserForSQLite.DBBrowserForSQLite" },
|
||||||
|
{ "PackageIdentifier": "Docker.DockerDesktop" },
|
||||||
|
{ "PackageIdentifier": "Doppler.doppler" },
|
||||||
|
{ "PackageIdentifier": "ezwinports.make" },
|
||||||
|
{ "PackageIdentifier": "sharkdp.fd" },
|
||||||
|
{ "PackageIdentifier": "Gyan.FFmpeg" },
|
||||||
|
{ "PackageIdentifier": "GIMP.GIMP.3" },
|
||||||
|
{ "PackageIdentifier": "Git.Git" },
|
||||||
|
{ "PackageIdentifier": "GitHub.cli" },
|
||||||
|
{ "PackageIdentifier": "GoLang.Go" },
|
||||||
|
{ "PackageIdentifier": "goreleaser.goreleaser" },
|
||||||
|
{ "PackageIdentifier": "JetBrains.dotPeek" },
|
||||||
|
{ "PackageIdentifier": "JesseDuffield.lazygit" },
|
||||||
|
{ "PackageIdentifier": "JanDeDobbeleer.OhMyPosh" },
|
||||||
|
{ "PackageIdentifier": "twpayne.chezmoi" },
|
||||||
|
{ "PackageIdentifier": "Microsoft.DotNet.SDK.8" },
|
||||||
|
{ "PackageIdentifier": "Microsoft.DotNet.SDK.10" },
|
||||||
|
{ "PackageIdentifier": "Microsoft.OpenJDK.11" },
|
||||||
|
{ "PackageIdentifier": "Microsoft.PowerShell" },
|
||||||
|
{ "PackageIdentifier": "Microsoft.PowerToys" },
|
||||||
|
{ "PackageIdentifier": "Microsoft.VisualStudio.2022.BuildTools" },
|
||||||
|
{ "PackageIdentifier": "Microsoft.VisualStudioCode" },
|
||||||
|
{ "PackageIdentifier": "Microsoft.WindowsTerminal" },
|
||||||
|
{ "PackageIdentifier": "Microsoft.WSL" },
|
||||||
|
{ "PackageIdentifier": "MongoDB.Compass.Full" },
|
||||||
|
{ "PackageIdentifier": "Neovim.Neovim" },
|
||||||
|
{ "PackageIdentifier": "CoreyButler.NVMforWindows" },
|
||||||
|
{ "PackageIdentifier": "OBSProject.OBSStudio" },
|
||||||
|
{ "PackageIdentifier": "Obsidian.Obsidian" },
|
||||||
|
{ "PackageIdentifier": "Ollama.Ollama" },
|
||||||
|
{ "PackageIdentifier": "Postman.Postman" },
|
||||||
|
{ "PackageIdentifier": "astral-sh.uv" },
|
||||||
|
{ "PackageIdentifier": "BurntSushi.ripgrep.MSVC" },
|
||||||
|
{ "PackageIdentifier": "Rustlang.Rustup" },
|
||||||
|
{ "PackageIdentifier": "SlackTechnologies.Slack" },
|
||||||
|
{ "PackageIdentifier": "SQLite.SQLite" },
|
||||||
|
{ "PackageIdentifier": "Tailscale.Tailscale" },
|
||||||
|
{ "PackageIdentifier": "VideoLAN.VLC" },
|
||||||
|
{ "PackageIdentifier": "JetBrains.WebStorm" },
|
||||||
|
{ "PackageIdentifier": "WinSCP.WinSCP" },
|
||||||
|
{ "PackageIdentifier": "ajeetdsouza.zoxide" },
|
||||||
|
{ "PackageIdentifier": "Canonical.Ubuntu" }
|
||||||
|
],
|
||||||
|
"SourceDetails": {
|
||||||
|
"Argument": "https://cdn.winget.microsoft.com/cache",
|
||||||
|
"Identifier": "Microsoft.Winget.Source_8wekyb3d8bbwe",
|
||||||
|
"Name": "winget",
|
||||||
|
"Type": "Microsoft.PreIndexed.Package"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"WinGetVersion": "1.0.0"
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
#!/usr/bin/env pwsh
|
||||||
|
# bootstrap.ps1
|
||||||
|
# One-shot bootstrap for a fresh Windows machine.
|
||||||
|
# Installs chezmoi and Bitwarden CLI, authenticates with Bitwarden,
|
||||||
|
# then runs chezmoi init --apply to pull and apply this dotfiles repo.
|
||||||
|
#
|
||||||
|
# Usage (run from an elevated or standard PowerShell prompt):
|
||||||
|
# iwr -useb https://raw.githubusercontent.com/StevanFreeborn/dotfiles/main/scripts/bootstrap.ps1 | iex
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
$DOTFILES_REPO = "https://gitea.freeborn.cloud/Stevan/dotfiles.git"
|
||||||
|
$DOTFILES_REPO_FALLBACK = "StevanFreeborn/dotfiles"
|
||||||
|
|
||||||
|
function Write-Step($msg) { Write-Host "==> $msg" -ForegroundColor Cyan }
|
||||||
|
function Write-Ok($msg) { Write-Host " $msg" -ForegroundColor Green }
|
||||||
|
function Write-Skip($msg) { Write-Host " $msg" -ForegroundColor DarkGray }
|
||||||
|
function Write-Err($msg) { Write-Host " ERROR: $msg" -ForegroundColor Red }
|
||||||
|
|
||||||
|
# --- Check winget ---
|
||||||
|
Write-Step "Checking winget..."
|
||||||
|
if (-not (Get-Command winget -ErrorAction SilentlyContinue)) {
|
||||||
|
Write-Err "winget not found. Install App Installer from the Microsoft Store and re-run."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
Write-Ok "winget is available."
|
||||||
|
|
||||||
|
# --- Helper: winget install if not present ---
|
||||||
|
function Install-WingetPackage($id, $name) {
|
||||||
|
if (Get-Command $name -ErrorAction SilentlyContinue) {
|
||||||
|
Write-Skip "$name already installed, skipping."
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Write-Step "Installing $name ($id) via winget..."
|
||||||
|
winget install --id $id --silent --accept-package-agreements --accept-source-agreements
|
||||||
|
# Refresh PATH so the new binary is usable in this session
|
||||||
|
$env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + ";" +
|
||||||
|
[System.Environment]::GetEnvironmentVariable("PATH", "User")
|
||||||
|
Write-Ok "$name installed."
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Install Bitwarden CLI ---
|
||||||
|
Install-WingetPackage "Bitwarden.CLI" "bw"
|
||||||
|
|
||||||
|
# --- Install chezmoi ---
|
||||||
|
Install-WingetPackage "twpayne.chezmoi" "chezmoi"
|
||||||
|
|
||||||
|
# --- Bitwarden login ---
|
||||||
|
Write-Step "Checking Bitwarden login status..."
|
||||||
|
$bwStatus = (bw status 2>$null | ConvertFrom-Json -ErrorAction SilentlyContinue).status
|
||||||
|
|
||||||
|
if ($bwStatus -ne "unlocked") {
|
||||||
|
if ($bwStatus -ne "unauthenticated" -and $null -ne $bwStatus) {
|
||||||
|
Write-Skip "Already logged in, skipping login."
|
||||||
|
} else {
|
||||||
|
Write-Step "Logging into Bitwarden (enter your email and master password)..."
|
||||||
|
bw login
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Err "Bitwarden login failed. Please check your credentials and re-run."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Step "Unlocking Bitwarden vault..."
|
||||||
|
$env:BW_SESSION = bw unlock --raw
|
||||||
|
if (-not $env:BW_SESSION) {
|
||||||
|
Write-Err "Failed to unlock Bitwarden vault. Re-run the script and try again."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
Write-Ok "Vault unlocked. BW_SESSION is set."
|
||||||
|
} else {
|
||||||
|
Write-Skip "Bitwarden vault already unlocked."
|
||||||
|
if (-not $env:BW_SESSION) {
|
||||||
|
Write-Step "Refreshing BW_SESSION..."
|
||||||
|
$env:BW_SESSION = bw unlock --raw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Bootstrap dotfiles ---
|
||||||
|
Write-Step "Bootstrapping dotfiles with chezmoi..."
|
||||||
|
|
||||||
|
$chezmoiSourceDir = Join-Path $env:USERPROFILE ".local\share\chezmoi"
|
||||||
|
|
||||||
|
if (Test-Path (Join-Path $chezmoiSourceDir ".git")) {
|
||||||
|
# Already initialized — pull latest and apply
|
||||||
|
Write-Step "chezmoi already initialized, pulling latest changes..."
|
||||||
|
chezmoi update
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Err "chezmoi update failed."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
Write-Ok "Dotfiles updated and applied."
|
||||||
|
} else {
|
||||||
|
# Fresh init — try Gitea first, fall back to GitHub mirror
|
||||||
|
$applied = $false
|
||||||
|
try {
|
||||||
|
chezmoi init --apply $DOTFILES_REPO
|
||||||
|
$applied = $LASTEXITCODE -eq 0
|
||||||
|
} catch {
|
||||||
|
$applied = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $applied) {
|
||||||
|
Write-Host " Primary Gitea repo unreachable, trying GitHub mirror..." -ForegroundColor Yellow
|
||||||
|
chezmoi init --apply $DOTFILES_REPO_FALLBACK
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Err "chezmoi init failed. Check the repo URL and your network connection."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Bootstrap complete!" -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Next steps:" -ForegroundColor Yellow
|
||||||
|
Write-Host " - Restart your terminal to pick up the new PowerShell profile."
|
||||||
|
Write-Host " - Run 'chezmoi update' at any time to sync the latest changes."
|
||||||
|
Write-Host " - To add a new machine later, re-run this script."
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# bootstrap.sh
|
||||||
|
# One-shot bootstrap for a fresh Linux (Ubuntu/Debian) machine.
|
||||||
|
# Installs curl, git, chezmoi, and Bitwarden CLI, authenticates with Bitwarden,
|
||||||
|
# then runs chezmoi init --apply to pull and apply this dotfiles repo.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# bash <(curl -fsLS https://raw.githubusercontent.com/StevanFreeborn/dotfiles/main/scripts/bootstrap.sh)
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
DOTFILES_REPO="https://gitea.freeborn.cloud/Stevan/dotfiles.git"
|
||||||
|
DOTFILES_REPO_FALLBACK="StevanFreeborn/dotfiles"
|
||||||
|
|
||||||
|
step() { echo -e "\n\033[0;36m==> $*\033[0m"; }
|
||||||
|
ok() { echo -e " \033[0;32m$*\033[0m"; }
|
||||||
|
skip() { echo -e " \033[0;90m$*\033[0m"; }
|
||||||
|
err() { echo -e " \033[0;31mERROR: $*\033[0m" >&2; exit 1; }
|
||||||
|
|
||||||
|
# --- Ensure running on a supported distro ---
|
||||||
|
if ! command -v apt-get &>/dev/null; then
|
||||||
|
err "This script requires apt-get (Ubuntu/Debian). Adjust for your distro."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Install curl and git ---
|
||||||
|
step "Ensuring curl and git are installed..."
|
||||||
|
missing=()
|
||||||
|
command -v curl &>/dev/null || missing+=("curl")
|
||||||
|
command -v git &>/dev/null || missing+=("git")
|
||||||
|
|
||||||
|
if [ ${#missing[@]} -gt 0 ]; then
|
||||||
|
sudo apt-get update -qq
|
||||||
|
sudo apt-get install -y "${missing[@]}"
|
||||||
|
ok "Installed: ${missing[*]}"
|
||||||
|
else
|
||||||
|
skip "curl and git already present."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Install Bitwarden CLI ---
|
||||||
|
# Ensure /snap/bin is in PATH — snap binaries aren't visible in the current
|
||||||
|
# shell session without this when running via bash <(curl ...)
|
||||||
|
command -v snap &>/dev/null && export PATH="$PATH:/snap/bin"
|
||||||
|
|
||||||
|
step "Checking Bitwarden CLI..."
|
||||||
|
if command -v bw &>/dev/null; then
|
||||||
|
skip "bw already installed ($(bw --version))."
|
||||||
|
else
|
||||||
|
if command -v snap &>/dev/null; then
|
||||||
|
step "Installing Bitwarden CLI via snap..."
|
||||||
|
sudo snap install bw
|
||||||
|
ok "bw installed via snap."
|
||||||
|
else
|
||||||
|
# Fallback: download the official Linux binary from GitHub releases
|
||||||
|
step "snap not available; downloading Bitwarden CLI binary..."
|
||||||
|
BW_VERSION=$(curl -fsLS "https://api.github.com/repos/bitwarden/clients/releases/latest" \
|
||||||
|
| grep '"tag_name"' | grep cli | head -1 | sed 's/.*"cli-v\([^"]*\)".*/\1/' || echo "")
|
||||||
|
if [ -z "$BW_VERSION" ]; then
|
||||||
|
err "Could not determine latest Bitwarden CLI version. Install bw manually and re-run."
|
||||||
|
fi
|
||||||
|
TMP_ZIP=$(mktemp /tmp/bw-XXXXXX.zip)
|
||||||
|
curl -fsLS "https://github.com/bitwarden/clients/releases/download/cli-v${BW_VERSION}/bw-linux-${BW_VERSION}.zip" \
|
||||||
|
-o "$TMP_ZIP"
|
||||||
|
sudo unzip -o "$TMP_ZIP" -d /usr/local/bin/ bw
|
||||||
|
sudo chmod +x /usr/local/bin/bw
|
||||||
|
rm -f "$TMP_ZIP"
|
||||||
|
ok "bw ${BW_VERSION} installed to /usr/local/bin/bw."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Install chezmoi ---
|
||||||
|
step "Checking chezmoi..."
|
||||||
|
if command -v chezmoi &>/dev/null; then
|
||||||
|
skip "chezmoi already installed ($(chezmoi --version))."
|
||||||
|
else
|
||||||
|
step "Installing chezmoi..."
|
||||||
|
sh -c "$(curl -fsLS get.chezmoi.io)" -- -b "$HOME/.local/bin"
|
||||||
|
export PATH="$HOME/.local/bin:$PATH"
|
||||||
|
ok "chezmoi installed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Bitwarden login ---
|
||||||
|
step "Checking Bitwarden login status..."
|
||||||
|
BW_STATUS=$(bw status 2>/dev/null | python3 -c "import sys,json; print(json.load(sys.stdin).get('status','unauthenticated'))" 2>/dev/null || echo "unauthenticated")
|
||||||
|
|
||||||
|
if [ "$BW_STATUS" != "unlocked" ]; then
|
||||||
|
if [ "$BW_STATUS" = "unauthenticated" ]; then
|
||||||
|
step "Logging into Bitwarden (enter your email and master password)..."
|
||||||
|
bw login || err "Bitwarden login failed. Check your credentials and re-run."
|
||||||
|
else
|
||||||
|
skip "Already logged in."
|
||||||
|
fi
|
||||||
|
|
||||||
|
step "Unlocking Bitwarden vault..."
|
||||||
|
export BW_SESSION
|
||||||
|
BW_SESSION=$(bw unlock --raw) || err "Failed to unlock Bitwarden vault."
|
||||||
|
ok "Vault unlocked. BW_SESSION is set."
|
||||||
|
else
|
||||||
|
skip "Bitwarden vault already unlocked."
|
||||||
|
if [ -z "${BW_SESSION:-}" ]; then
|
||||||
|
step "Refreshing BW_SESSION..."
|
||||||
|
export BW_SESSION
|
||||||
|
BW_SESSION=$(bw unlock --raw)
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Bootstrap dotfiles ---
|
||||||
|
step "Bootstrapping dotfiles with chezmoi..."
|
||||||
|
|
||||||
|
CHEZMOI_SOURCE_DIR="$HOME/.local/share/chezmoi"
|
||||||
|
|
||||||
|
if [ -d "$CHEZMOI_SOURCE_DIR/.git" ]; then
|
||||||
|
# Already initialized — pull latest and apply
|
||||||
|
step "chezmoi already initialized, pulling latest changes..."
|
||||||
|
chezmoi update || err "chezmoi update failed."
|
||||||
|
ok "Dotfiles updated and applied."
|
||||||
|
else
|
||||||
|
# Fresh init — try Gitea first, fall back to GitHub mirror
|
||||||
|
if chezmoi init --apply "$DOTFILES_REPO" 2>/dev/null; then
|
||||||
|
ok "Dotfiles applied from Gitea."
|
||||||
|
else
|
||||||
|
echo " Primary Gitea repo unreachable, trying GitHub mirror..."
|
||||||
|
chezmoi init --apply "$DOTFILES_REPO_FALLBACK" \
|
||||||
|
|| err "chezmoi init failed. Check the repo URL and your network connection."
|
||||||
|
ok "Dotfiles applied from GitHub mirror."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "\033[0;32mBootstrap complete!\033[0m"
|
||||||
|
echo ""
|
||||||
|
echo "IMPORTANT: Open a new terminal (or run: exec zsh) before continuing."
|
||||||
|
echo " ~/.local/bin is now in your PATH via ~/.zshenv, but your current"
|
||||||
|
echo " shell session won't see it until you start a new one."
|
||||||
|
echo ""
|
||||||
|
echo " chezmoi is at: $HOME/.local/bin/chezmoi"
|
||||||
|
echo ""
|
||||||
|
echo "Next steps:"
|
||||||
|
echo " 1. Open a new terminal"
|
||||||
|
echo " 2. Run: chezmoi update at any time to sync the latest changes"
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
#!/usr/bin/env pwsh
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Imports SSH private keys from ~/.ssh into Bitwarden as Secure Notes.
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Creates a Bitwarden Secure Note for each private SSH key using the naming
|
||||||
|
convention expected by the dotfiles setup script:
|
||||||
|
|
||||||
|
"SSH Key - <filename>"
|
||||||
|
|
||||||
|
e.g. "SSH Key - zenbook_tinker", "SSH Key - stevan@freeborn.cloud"
|
||||||
|
|
||||||
|
Public keys (.pub files) and non-key files (config, known_hosts) are skipped.
|
||||||
|
|
||||||
|
Requires the Bitwarden CLI (bw) to be installed and your vault to be unlocked:
|
||||||
|
|
||||||
|
bw login # first time
|
||||||
|
$env:BW_SESSION = bw unlock --raw # each session
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
# Unlock Bitwarden, then run the script
|
||||||
|
$env:BW_SESSION = bw unlock --raw
|
||||||
|
.\import-ssh-keys-to-bitwarden.ps1
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
# Dry-run: see what would be imported without creating anything
|
||||||
|
.\import-ssh-keys-to-bitwarden.ps1 -WhatIf
|
||||||
|
#>
|
||||||
|
|
||||||
|
[CmdletBinding(SupportsShouldProcess)]
|
||||||
|
param (
|
||||||
|
[string]$SshDir = (Join-Path $env:USERPROFILE ".ssh")
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
# --- Preflight checks ---
|
||||||
|
|
||||||
|
if (-not (Get-Command bw -ErrorAction SilentlyContinue)) {
|
||||||
|
Write-Error "Bitwarden CLI (bw) not found. Install it with: winget install Bitwarden.CLI"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $env:BW_SESSION) {
|
||||||
|
Write-Error "BW_SESSION is not set. Unlock your vault first:`n `$env:BW_SESSION = bw unlock --raw"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$vaultStatus = bw status 2>$null | ConvertFrom-Json
|
||||||
|
if ($vaultStatus.status -ne "unlocked") {
|
||||||
|
Write-Error "Bitwarden vault is not unlocked. Run: `$env:BW_SESSION = bw unlock --raw"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path $SshDir)) {
|
||||||
|
Write-Error "SSH directory not found: $SshDir"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Identify private keys ---
|
||||||
|
|
||||||
|
$skipNames = @("config", "known_hosts", "known_hosts.old", "authorized_keys")
|
||||||
|
|
||||||
|
$privateKeys = Get-ChildItem $SshDir -File | Where-Object {
|
||||||
|
$_.Extension -ne ".pub" -and $_.Name -notin $skipNames
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($privateKeys.Count -eq 0) {
|
||||||
|
Write-Host "No private keys found in $SshDir" -ForegroundColor Yellow
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Found $($privateKeys.Count) private key(s) in $SshDir`n" -ForegroundColor Cyan
|
||||||
|
|
||||||
|
# --- Get existing Bitwarden items to avoid duplicates ---
|
||||||
|
|
||||||
|
Write-Host "Fetching existing Bitwarden items..." -ForegroundColor DarkGray
|
||||||
|
$existingItems = bw list items 2>$null | ConvertFrom-Json
|
||||||
|
$existingNames = $existingItems | ForEach-Object { $_.name }
|
||||||
|
|
||||||
|
# --- Import each key ---
|
||||||
|
|
||||||
|
$imported = 0
|
||||||
|
$skipped = 0
|
||||||
|
$failed = 0
|
||||||
|
|
||||||
|
foreach ($keyFile in $privateKeys) {
|
||||||
|
$itemName = "SSH Key - $($keyFile.Name)"
|
||||||
|
|
||||||
|
Write-Host " $($keyFile.Name)" -NoNewline
|
||||||
|
|
||||||
|
# Check for duplicate
|
||||||
|
if ($existingNames -contains $itemName) {
|
||||||
|
Write-Host " — already in Bitwarden, skipping" -ForegroundColor DarkGray
|
||||||
|
$skipped++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($PSCmdlet.ShouldProcess($itemName, "Create Bitwarden Secure Note")) {
|
||||||
|
try {
|
||||||
|
$keyContent = Get-Content $keyFile.FullName -Raw -ErrorAction Stop
|
||||||
|
|
||||||
|
# Build Bitwarden Secure Note JSON
|
||||||
|
$item = [ordered]@{
|
||||||
|
organizationId = $null
|
||||||
|
collectionIds = @()
|
||||||
|
folderId = $null
|
||||||
|
type = 2 # 2 = Secure Note
|
||||||
|
name = $itemName
|
||||||
|
notes = $keyContent
|
||||||
|
favorite = $false
|
||||||
|
secureNote = @{ type = 0 }
|
||||||
|
reprompt = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
$json = $item | ConvertTo-Json -Depth 5 -Compress
|
||||||
|
$encoded = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($json))
|
||||||
|
|
||||||
|
$result = bw create item $encoded 2>&1
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw $result
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host " — imported" -ForegroundColor Green
|
||||||
|
$imported++
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Host " — FAILED: $_" -ForegroundColor Red
|
||||||
|
$failed++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Summary ---
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Done: $imported imported, $skipped already existed, $failed failed" -ForegroundColor Cyan
|
||||||
|
|
||||||
|
if ($imported -gt 0) {
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Syncing vault..." -ForegroundColor DarkGray
|
||||||
|
bw sync 2>$null | Out-Null
|
||||||
|
Write-Host "Vault synced." -ForegroundColor DarkGray
|
||||||
|
}
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# import-ssh-keys-to-bitwarden.sh
|
||||||
|
# Imports SSH private keys from ~/.ssh into Bitwarden as Secure Notes.
|
||||||
|
#
|
||||||
|
# Creates a Bitwarden Secure Note for each private SSH key using the naming
|
||||||
|
# convention "SSH Key - <filename>", e.g. "SSH Key - id_ed25519".
|
||||||
|
# Public keys (.pub files) and non-key files (config, known_hosts) are skipped.
|
||||||
|
#
|
||||||
|
# Requires:
|
||||||
|
# - Bitwarden CLI (bw) — install via: sudo snap install bw
|
||||||
|
# - jq — install via: sudo apt install jq
|
||||||
|
# - BW_SESSION environment variable set
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# export BW_SESSION=$(bw unlock --raw)
|
||||||
|
# ./import-ssh-keys-to-bitwarden.sh
|
||||||
|
#
|
||||||
|
# Dry-run (no changes):
|
||||||
|
# ./import-ssh-keys-to-bitwarden.sh --dry-run
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SSH_DIR="${SSH_DIR:-$HOME/.ssh}"
|
||||||
|
DRY_RUN=false
|
||||||
|
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
--dry-run|-n) DRY_RUN=true ;;
|
||||||
|
*) echo "Unknown option: $arg" >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# --- Preflight checks ---
|
||||||
|
|
||||||
|
if ! command -v bw &>/dev/null; then
|
||||||
|
echo "Bitwarden CLI (bw) not found. Install it with: sudo snap install bw" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v jq &>/dev/null; then
|
||||||
|
echo "jq not found. Install it with: sudo apt install jq" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${BW_SESSION:-}" ]; then
|
||||||
|
echo "BW_SESSION is not set. Unlock your vault first:" >&2
|
||||||
|
echo " export BW_SESSION=\$(bw unlock --raw)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
vault_status=$(bw status 2>/dev/null | jq -r '.status')
|
||||||
|
if [ "$vault_status" != "unlocked" ]; then
|
||||||
|
echo "Bitwarden vault is not unlocked. Run: export BW_SESSION=\$(bw unlock --raw)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "$SSH_DIR" ]; then
|
||||||
|
echo "SSH directory not found: $SSH_DIR" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Identify private keys ---
|
||||||
|
|
||||||
|
skip_names=("config" "known_hosts" "known_hosts.old" "authorized_keys" "authorized_keys2")
|
||||||
|
|
||||||
|
private_keys=()
|
||||||
|
while IFS= read -r -d '' file; do
|
||||||
|
filename=$(basename "$file")
|
||||||
|
ext="${filename##*.}"
|
||||||
|
[[ "$ext" == "pub" ]] && continue
|
||||||
|
|
||||||
|
skip=false
|
||||||
|
for skip_name in "${skip_names[@]}"; do
|
||||||
|
[[ "$filename" == "$skip_name" ]] && { skip=true; break; }
|
||||||
|
done
|
||||||
|
[[ "$skip" == false ]] && private_keys+=("$file")
|
||||||
|
done < <(find "$SSH_DIR" -maxdepth 1 -type f -print0)
|
||||||
|
|
||||||
|
if [ ${#private_keys[@]} -eq 0 ]; then
|
||||||
|
echo "No private keys found in $SSH_DIR"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Found ${#private_keys[@]} private key(s) in $SSH_DIR"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# --- Get existing Bitwarden items to avoid duplicates ---
|
||||||
|
|
||||||
|
echo "Fetching existing Bitwarden items..."
|
||||||
|
existing_names=$(bw list items 2>/dev/null | jq -r '.[].name' | sort -u)
|
||||||
|
|
||||||
|
# --- Import each key ---
|
||||||
|
|
||||||
|
imported=0
|
||||||
|
skipped=0
|
||||||
|
failed=0
|
||||||
|
|
||||||
|
for key_file in "${private_keys[@]}"; do
|
||||||
|
key_name=$(basename "$key_file")
|
||||||
|
item_name="SSH Key - $key_name"
|
||||||
|
|
||||||
|
printf " %s" "$key_name"
|
||||||
|
|
||||||
|
if echo "$existing_names" | grep -Fxq "$item_name"; then
|
||||||
|
echo " — already in Bitwarden, skipping"
|
||||||
|
((skipped++))
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$DRY_RUN" = true ]; then
|
||||||
|
echo " — would import"
|
||||||
|
((imported++))
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
item_json=$(jq -n \
|
||||||
|
--rawfile notes "$key_file" \
|
||||||
|
--arg name "$item_name" \
|
||||||
|
'{
|
||||||
|
organizationId: null,
|
||||||
|
collectionIds: [],
|
||||||
|
folderId: null,
|
||||||
|
type: 2,
|
||||||
|
name: $name,
|
||||||
|
notes: $notes,
|
||||||
|
favorite: false,
|
||||||
|
secureNote: { type: 0 },
|
||||||
|
reprompt: 0
|
||||||
|
}')
|
||||||
|
|
||||||
|
encoded=$(echo -n "$item_json" | base64 -w0)
|
||||||
|
|
||||||
|
result=$(echo "$encoded" | bw create item 2>&1)
|
||||||
|
exit_code=$?
|
||||||
|
|
||||||
|
if [ $exit_code -eq 0 ]; then
|
||||||
|
echo " — imported"
|
||||||
|
((imported++))
|
||||||
|
else
|
||||||
|
echo " — FAILED: $result"
|
||||||
|
((failed++))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Done: $imported imported, $skipped already existed, $failed failed"
|
||||||
|
|
||||||
|
if [ "$imported" -gt 0 ]; then
|
||||||
|
echo ""
|
||||||
|
echo "Syncing vault..."
|
||||||
|
bw sync 2>/dev/null || true
|
||||||
|
echo "Vault synced."
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user