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