7076d40191
- zsh prompt: replace oh-my-posh with powerlevel10k via zinit, with the instant-prompt preamble near the top of .zshrc for fast startup - remove thefuck/thefk entirely: .zshrc aliases, install-thefk, home_config/thefuck, and its .stow-global-ignore entry - link-dotfiles: one-shot host-aware bootstrap — detect macOS vs Linux, install GNU stow if missing (brew / apt/dnf/pacman), then stow both trees - retire oh-my-posh: delete the bash + pwsh installers and home_config/ohmyposh (starship covers PowerShell separately) - README: document the new bootstrap and prompt setup
117 lines
4.5 KiB
PowerShell
117 lines
4.5 KiB
PowerShell
#!/usr/bin/env pwsh
|
|
<#
|
|
.SYNOPSIS
|
|
Windows equivalent of ./link-dotfiles (the GNU stow script).
|
|
|
|
.DESCRIPTION
|
|
stow can't run natively on Windows, so this mirrors what it does:
|
|
home_root/* -> ~\<name> (e.g. ~\.zshrc)
|
|
home_config/* -> ~\.config\<name> (e.g. ~\.config\nvim)
|
|
|
|
Directories are linked with junctions and files with symlinks/hardlinks,
|
|
none of which need administrator rights (junctions and hardlinks never do;
|
|
symlinks are attempted first in case Developer Mode is on, then we fall
|
|
back). Re-running is safe: correct links are skipped, and any real file or
|
|
directory that would be clobbered is moved aside to <name>.bak first.
|
|
|
|
.EXAMPLE
|
|
pwsh -File .\link-dotfiles.ps1
|
|
#>
|
|
[CmdletBinding()]
|
|
param(
|
|
# Print what would happen without changing anything.
|
|
[switch]$DryRun
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
$repo = $PSScriptRoot
|
|
$home_ = $HOME
|
|
|
|
# stow metadata files we must never link into the home tree.
|
|
$skip = @('.stow-local-ignore', '.stow-global-ignore')
|
|
|
|
function Get-LinkTarget {
|
|
param([string]$Path)
|
|
$item = Get-Item -LiteralPath $Path -Force -ErrorAction SilentlyContinue
|
|
if ($item -and $item.LinkType) { return $item.Target }
|
|
return $null
|
|
}
|
|
|
|
function Link-Item {
|
|
param(
|
|
[string]$Source, # absolute path inside the repo
|
|
[string]$Dest # absolute path under the home tree
|
|
)
|
|
|
|
$isDir = (Get-Item -LiteralPath $Source -Force).PSIsContainer
|
|
|
|
# Already linked to the right place? Nothing to do.
|
|
$existingTarget = Get-LinkTarget $Dest
|
|
if ($existingTarget -and ($existingTarget -ieq $Source)) {
|
|
Write-Host " ok $Dest" -ForegroundColor DarkGray
|
|
return
|
|
}
|
|
|
|
if (Test-Path -LiteralPath $Dest) {
|
|
if ($existingTarget) {
|
|
# A link, but pointing elsewhere -> replace it.
|
|
if ($DryRun) { Write-Host " relink $Dest (was -> $existingTarget)" -ForegroundColor Yellow; return }
|
|
Remove-Item -LiteralPath $Dest -Force -Recurse
|
|
} else {
|
|
# A real file/dir -> back it up rather than destroy it.
|
|
$bak = "$Dest.bak"
|
|
Write-Host " backup $Dest -> $bak" -ForegroundColor Yellow
|
|
if (-not $DryRun) { Move-Item -LiteralPath $Dest -Destination $bak -Force }
|
|
}
|
|
}
|
|
|
|
if ($DryRun) { Write-Host " link $Dest -> $Source" -ForegroundColor Cyan; return }
|
|
|
|
$parent = Split-Path $Dest -Parent
|
|
if (-not (Test-Path $parent)) { New-Item -ItemType Directory -Path $parent -Force | Out-Null }
|
|
|
|
if ($isDir) {
|
|
New-Item -ItemType Junction -Path $Dest -Target $Source | Out-Null
|
|
Write-Host " junc $Dest -> $Source" -ForegroundColor Green
|
|
} else {
|
|
# Prefer a real symlink (needs admin or Developer Mode); fall back to a
|
|
# hardlink (same volume, no admin); finally just copy.
|
|
try {
|
|
New-Item -ItemType SymbolicLink -Path $Dest -Target $Source -ErrorAction Stop | Out-Null
|
|
Write-Host " link $Dest -> $Source" -ForegroundColor Green
|
|
} catch {
|
|
try {
|
|
New-Item -ItemType HardLink -Path $Dest -Target $Source -ErrorAction Stop | Out-Null
|
|
Write-Host " hard $Dest -> $Source" -ForegroundColor Green
|
|
} catch {
|
|
Copy-Item -LiteralPath $Source -Destination $Dest -Force
|
|
Write-Host " copy $Dest (could not link; copied)" -ForegroundColor Magenta
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Write-Host "home_root -> $home_"
|
|
Get-ChildItem -LiteralPath (Join-Path $repo 'home_root') -Force |
|
|
Where-Object { $skip -notcontains $_.Name } |
|
|
ForEach-Object { Link-Item $_.FullName (Join-Path $home_ $_.Name) }
|
|
|
|
$configDir = Join-Path $home_ '.config'
|
|
Write-Host "home_config -> $configDir"
|
|
Get-ChildItem -LiteralPath (Join-Path $repo 'home_config') -Force |
|
|
Where-Object { $skip -notcontains $_.Name } |
|
|
ForEach-Object { Link-Item $_.FullName (Join-Path $configDir $_.Name) }
|
|
|
|
# Windows Terminal lives in a package folder outside the stow tree, so link its
|
|
# settings.json explicitly. Skipped silently if Terminal isn't installed.
|
|
$wtPkg = Get-ChildItem (Join-Path $env:LOCALAPPDATA 'Packages') -Filter 'Microsoft.WindowsTerminal_*' -Directory -ErrorAction SilentlyContinue |
|
|
Select-Object -First 1
|
|
$wtSrc = Join-Path $repo 'windows\windows-terminal\settings.json'
|
|
if ($wtPkg -and (Test-Path $wtSrc)) {
|
|
$wtDest = Join-Path $wtPkg.FullName 'LocalState\settings.json'
|
|
Write-Host "windows-terminal -> $wtDest"
|
|
Link-Item $wtSrc $wtDest
|
|
}
|
|
|
|
Write-Host "Done." -ForegroundColor Green
|