Cross-platform dotfiles and application settings manager. Symlink-first -- change a setting in your editor, it's immediately tracked in git.
Perch manages symlinks, registry entries, VS Code extensions, PowerShell modules, global npm packages, and system package installation across Windows, Linux, and macOS.
# One-liner from a fresh Windows machine (run as admin):
irm https://raw.githubusercontent.com/Laoujin/perch/master/bootstrap.ps1 | iex
# Or with options:
.\bootstrap.ps1 -SkipDeploy # Clone & build only
.\bootstrap.ps1 -ConfigDir D:\my-config # Custom paths
.\bootstrap.ps1 -ConfigRepo YourOrg/your-config # Different config repoInstalls .NET 10 SDK and git if missing, clones Perch and your config repo, builds, and runs the first deploy. Idempotent -- safe to re-run.
# Prerequisites: .NET 10 SDK
# Build
dotnet build
# Run directly
dotnet run --project src/Perch.Cli -- deploy --config-path /path/to/your/config-repo
# Or install as a global tool
dotnet pack src/Perch.Cli -c Release
dotnet tool install --global --add-source src/Perch.Cli/bin/Release Perch.Cli
perch deploy --config-path /path/to/your/config-repoAfter the first run, the config path is saved so subsequent commands don't need --config-path.
Creates symlinks, sets registry values, installs packages, extensions, and modules.
perch deploy [--config-path <path>] [--dry-run] [--interactive] [--output Pretty|Json]
| Flag | Description |
|---|---|
--config-path |
Path to config repo (saved for future runs) |
--dry-run |
Preview what would happen without making changes |
--interactive, -i |
Prompt before each module (apply / skip / abort) |
--output |
Pretty (default, live table) or Json |
Before deploying, Perch creates a snapshot of all target files so you can restore them later.
Shows drift between your config repo and the current system state.
perch status [--config-path <path>] [--drift-only] [--output Pretty|Json]
| Flag | Description |
|---|---|
--drift-only |
Only show items that are missing, drifted, or errored |
--output |
Pretty (default, grouped by category) or Json |
Checks: symlinks, registry entries, global packages, VS Code extensions, PowerShell modules, and system packages.
Shows installed applications and whether they match a known module.
List and restore from pre-deploy snapshots.
Snapshot the filesystem to detect changes made outside of Perch.
Register git clean filters defined in module manifests.
Output a shell tab-completion script.
Perch reads a config repo -- a folder of modules, each with a manifest.yaml and the actual config files. On deploy, it symlinks those files to their expected locations on the system. Edit a config file in its usual location and the change is immediately reflected in the repo.
your-config-repo/
git/
manifest.yaml
.gitconfig
.gitignore_global
vscode/
manifest.yaml
settings.json
keybindings.json
snippets/
powershell/
manifest.yaml
profiles/
scripts/
packages.yaml # System packages to install
machines/ # Optional per-machine overrides
base.yaml
DESKTOP-ABC.yaml
Perch scans */manifest.yaml one level deep in the config repo. Each subfolder with a manifest.yaml is a module.
display-name: Git
enabled: true # Optional, default true
platforms: # Optional, runs on all if omitted
- Windows
- Linux
- MacOS
links:
- source: .gitconfig # Relative to module folder
target: # Platform-specific targets
windows: "%USERPROFILE%\\.gitconfig"
linux: "$HOME/.gitconfig"
macos: "$HOME/.gitconfig"
- source: scripts
target: "%USERPROFILE%\\scripts"
link-type: junction # 'symlink' (default) or 'junction'
registry: # Windows only
- key: HKEY_CURRENT_USER\Software\App
name: SettingName
value: 42
type: dword # dword, string, etc.
global-packages:
manager: npm # npm or bun
packages:
- prettier
- eslint_d
vscode-extensions:
- dbaeumer.vscode-eslint
- esbenp.prettier-vscode
ps-modules:
- Posh-Git
- PSReadLine
hooks:
pre-deploy: setup.ps1 # Run before deploying this module
post-deploy: cleanup.ps1 # Run after deploying this moduleTarget paths support environment variables: %USERPROFILE%, %APPDATA%, %LOCALAPPDATA%, $HOME, $XDG_CONFIG_HOME, etc. Custom variables can be defined in machine profiles.
Declare system-level packages in a packages.yaml at the config repo root:
packages:
- name: Git.Git
manager: winget
- name: Mozilla.Firefox
manager: winget
- name: curl
manager: apt
- name: git
manager: brewSupported managers: winget, chocolatey, apt, brew. Each is only applied on its matching platform.
On Windows, winget is recommended over chocolatey -- winget detects all installed software (including apps installed via choco, MSI, or manually) through the Windows ARP registry.
Place YAML files in a machines/ folder to customize per-machine behavior:
machines/base.yaml-- defaults for all machinesmachines/HOSTNAME.yaml-- overrides for a specific machine (matched by$env:COMPUTERNAME)
include-modules: # Only deploy these modules (whitelist)
- git
- vscode
exclude-modules: # Skip these modules (blacklist)
- cmder
variables: # Custom variables for target path expansion
PROJECTS: "D:\\Projects"Hostname-specific values override base values. Variables are merged (hostname wins on conflict).
src/
Perch.Core/ # Engine: symlinks, registry, packages, status, deploy
Perch.Cli/ # CLI commands (Spectre.Console)
Perch.Desktop/ # Desktop GUI (first-run wizard)
tests/
Perch.Core.Tests/ # Unit + integration tests
dotnet build # Build (warnings as errors)
dotnet test # Run all tests.NET 10, C# latest, NUnit 4, NSubstitute 5, Roslynator analyzers.