Skip to content

Commit 76a2a2f

Browse files
committed
fix(windows-bootstrap): stabilize WSL provisioning and package gating
Move Windows package/bootstrap inputs into chezmoidata overlays, improve WSL cloud-init templating, and align Brew condition handling so WSL/private exclusions render consistently across bootstrap paths. Made-with: Cursor
1 parent da515ee commit 76a2a2f

26 files changed

Lines changed: 489 additions & 99 deletions

AGENTS.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ Deleting a file from the chezmoi source does **not** remove it from the target (
122122
- **OpenCode**`home/dot_config/opencode/opencode.jsonc.tmpl` (→ `~/.config/opencode/opencode.jsonc`). This is the global OpenCode config: model, MCP servers, permissions, etc. It is a chezmoi template (uses `.chezmoi.homeDir` for the Obsidian vault path). Edit the source here when updating OpenCode settings.
123123
- **OpenCode global agent rules**`home/dot_config/opencode/AGENTS.md` (→ `~/.config/opencode/AGENTS.md`). Universal agent rules that apply across all OpenCode sessions and projects. Edit the source here and run `chezmoi apply`. Never edit `~/.config/opencode/AGENTS.md` directly.
124124
- **Other config**`home/dot_config/` includes tmux, mise, finicky; `home/private_dot_gnupg/` for GnuPG (private permissions).
125-
- **Executable**`home/dot_scripts/executable_brew-review` (→ `~/.scripts/brew-review`) is the Homebrew drift review script. `home/dot_scripts/executable_7zw` (→ `~/.scripts/7zw`) is a 7-zip wrapper. Both live in `dot_scripts/` — not `dot_zfunctions/` (see Brew section below).
125+
- **Executable**`home/dot_scripts/executable_brew-review` (→ `~/.scripts/brew-review`) is the Homebrew drift review script, and `home/dot_scripts/executable_choco-review` is the Chocolatey drift review script (Windows source-side usage from Git Bash/WSL). `home/dot_scripts/executable_7zw` (→ `~/.scripts/7zw`) is a 7-zip wrapper. All script helpers live in `dot_scripts/` — not `dot_zfunctions/` (see Brew section below).
126126
- **Bootstrap**`home/.chezmoiscripts/run_once_before_bootstrap.sh.tmpl` runs once before other updates (install deps, brew bundle, oh-my-zsh, mise, etc.). It is OS-aware (darwin/linux) and sets Codespaces overrides when `codespaces` is true.
127127
- **Root-level (not in source state)**`install.sh.tmpl` and `install.ps1.tmpl` are installer templates for release assets; `.macos` holds macOS defaults; `.gitignore` excludes local/private artifacts (e.g. `*.local.*`, vim swap/undo). Do not add ignored patterns to the source state.
128128
- **README "What you get" table** – The tool table in `README.md` is sorted by category: shell/prompt, terminal emulators, multiplexer, editor, dev tools, version control, security, package/runtime management, then platform-specific utilities. When adding or removing a managed tool, update this table and preserve the sort order. Platform columns (macOS, Linux, Windows, Codespaces) must reflect what `.chezmoiignore` actually deploys.
@@ -137,14 +137,14 @@ Deleting a file from the chezmoi source does **not** remove it from the target (
137137
- **Native Windows** is supported — `chezmoi apply` deploys cross-platform configs (git, nvim, mise, opencode) and Windows-specific configs (WezTerm), while skipping Unix-only targets (zsh, brew, tmux, ghostty, kitty, finicky, gnupg, scripts, opencode-shims).
138138
- **OS-conditional ignores** in `home/.chezmoiignore` use `{{ if eq .chezmoi.os "windows" }}` and `{{ if ne .chezmoi.os "windows" }}` blocks to control which targets are deployed per platform.
139139
- **Bash chezmoiscripts** (`run_onchange_after_bootstrap.sh.tmpl`, `run_onchange_after_brew_review.sh.tmpl`, `run_onchange_after_tmux_symlinks.sh.tmpl`) are wrapped in `{{ if ne .chezmoi.os "windows" }}` guards so they render to empty on Windows (chezmoi skips empty scripts).
140-
- **Windows bootstrap**`home/.chezmoiscripts/run_onchange_after_bootstrap_windows.ps1.tmpl` installs packages via Chocolatey (`choco install -y`), runs `mise install`, and syncs Neovim plugins. Runs only on Windows.
140+
- **Windows bootstrap**`home/.chezmoiscripts/run_onchange_after_bootstrap_windows.ps1.tmpl` installs packages via Chocolatey (`choco install -y`) and Winget, runs `mise install`, and syncs Neovim plugins. `home/.chezmoiscripts/run_onchange_after_choco_review_windows.ps1.tmpl` runs interactive Chocolatey drift review after overlay changes when `bash` is available. Runs only on Windows. The elevated block does **not** skip Chocolatey/WSL when `CI` is set (same code path as interactive runs; `windows-elevation.ps1.tmpl` still avoids UAC in CI by running the block in-process). Package lists live in `home/.chezmoidata/chocolatey/*.yaml` and `home/.chezmoidata/winget/*.yaml` as `overlays.<name>.packages` (same merge idea as `home/.chezmoidata/brew/`: overlay dicts merge; lists under each overlay are concatenated in lexical overlay name order).
141141
- **`install.ps1.tmpl`** at the repo root is the Windows installer template equivalent of `install.sh.tmpl`: installs Chocolatey, chezmoi, and git, then runs `chezmoi init --apply`.
142142
- **WezTerm**`home/dot_config/wezterm/wezterm.lua` (→ `~/.config/wezterm/wezterm.lua`). Windows terminal emulator with kitty graphics protocol support. Ignored on non-Windows via `.chezmoiignore`.
143143
- **OpenCode**`home/dot_config/opencode/opencode.jsonc.tmpl` renders MCP and permission data from `home/.chezmoidata/mcps/*.yaml` and `home/.chezmoidata/agent-permissions/*.yaml`. Atlassian entries are gated by `conditions` in those overlays (for example `private: false`), so they are excluded on personal machines. Since Windows is always personal, this also covers Windows.
144144
- **Package manager** — Windows uses Chocolatey (`choco`), not winget or scoop.
145145
- **When adding new configs**, decide if the target is cross-platform, Unix-only, or Windows-only, and update `home/.chezmoiignore` accordingly.
146146
- **When adding new chezmoiscripts**, bash scripts (`.sh.tmpl`) must be guarded with `{{ if ne .chezmoi.os "windows" }}` and PowerShell scripts (`.ps1.tmpl`) with `{{ if eq .chezmoi.os "windows" }}` so they render to empty on the wrong OS.
147-
- **WSL** — The Windows bootstrap script provisions WSL Ubuntu non-interactively via cloud-init. It writes a cloud-config to `~/.cloud-init/Ubuntu.user-data` (using the Windows username), installs Ubuntu with `--no-launch`, then launches and waits for cloud-init to create the user, clone the dotfiles repo, install chezmoi, and run `chezmoi init --apply --source=...`. Inside WSL, `chezmoi.os` is `"linux"` so the full Unix config stack (zsh, brew, tmux, etc.) applies without modification.
147+
- **WSL** — The Windows bootstrap script provisions WSL Ubuntu non-interactively via cloud-init. The cloud-init YAML lives in `home/.chezmoidata/wsl/00-ubuntu-user-data.yaml` as `windowsWsl.ubuntuCloudInitUserData` (static data; `.chezmoi.toml` `[data] wsl` remains the boolean “running inside WSL”). On **first** WSL setup only, `~/.local/share/chezmoi` is created with **`cp -a` from the Windows chezmoi source** on `/mnt/<drive>/...` (see `home/.chezmoitemplates/wsl-windows-chezmoi-source-mount-path.tmpl`: maps `.chezmoi.sourceDir`). If that directory already exists, Windows scripts and cloud-init **do not** remove or overwrite it — ongoing dotfile work is done inside WSL. Shared Windows WSL script inputs (`profile.windows.wslUser` + mount path) are built in `home/.chezmoitemplates/wsl-windows-script-context.json.tmpl` (`toJson` / `fromJson`) for `run_onchange_after_bootstrap_windows.ps1.tmpl` and `run_after_95_wsl_ensure_windows.ps1.tmpl`. Set `profile.windows.wslUser` in `home/.chezmoidata/profile.yaml` (required on Windows; empty fails apply for WSL/bootstrap scripts). Scripts substitute `__WSL_CHEZMOI_SOURCE__` and `__WSL_USER__` into that blob at apply time, write `~/.cloud-init/Ubuntu.user-data`, install Ubuntu with `--no-launch`, and wait for cloud-init. Inside WSL, `chezmoi.os` is `"linux"` so the full Unix config stack (zsh, brew, tmux, etc.) applies without modification.
148148

149149
---
150150

docs/agent-permissions.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,4 @@ In this repo, Atlassian-specific permission keys live in `home/.chezmoidata/agen
108108
1. Render OpenCode config with `chezmoi execute-template`.
109109
2. Run test suite (`tests/source/chezmoi.bats`).
110110
3. Run `pre-commit run --all-files` before commit.
111+

home/.chezmoi.toml.tmpl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
{{- $codespaces:= env "CODESPACES" | not | not -}}
22
{{- $ci := env "CI" | not | not -}}
3+
{{- $wsl := env "WSL_DISTRO_NAME" | not | not -}}
34
{{- $private := or (eq .chezmoi.os "windows") (stat (joinPath .chezmoi.homeDir ".private") | not | not) -}}
45
{{- $obsidianVaultPath := joinPath .chezmoi.homeDir "Documents" "Obsidian" "Vault" -}}
56
[data]
67
codespaces = {{ $codespaces }}
78
ci = {{ $ci }}
9+
wsl = {{ $wsl }}
810
private = {{ $private }}
911
obsidianVaultPath = {{ $obsidianVaultPath | quote }}
1012

home/.chezmoidata/brew/00-base.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ brew:
2020
conditions:
2121
- { kind: env, name: CODESPACES, op: unset }
2222
- { kind: env, name: CI, op: unset }
23+
- { kind: env, name: WSL, op: unset }
2324
- type: brew
2425
name: colordiff
2526
conditions:
@@ -98,6 +99,7 @@ brew:
9899
conditions:
99100
- { kind: env, name: CODESPACES, op: unset }
100101
- { kind: env, name: CI, op: unset }
102+
- { kind: env, name: WSL, op: unset }
101103
- type: brew
102104
name: tmux
103105
conditions:
@@ -112,6 +114,7 @@ brew:
112114
conditions:
113115
- { kind: env, name: CODESPACES, op: unset }
114116
- { kind: env, name: CI, op: unset }
117+
- { kind: env, name: WSL, op: unset }
115118
- type: brew
116119
name: wakatime-cli
117120
conditions:
@@ -134,10 +137,12 @@ brew:
134137
name: 1password # work
135138
conditions:
136139
- { kind: os, op: is, value: mac }
140+
- { kind: env, name: PRIVATE, op: unset }
137141
- type: cask
138142
name: 1password-cli # work
139143
conditions:
140144
- { kind: os, op: is, value: mac }
145+
- { kind: env, name: PRIVATE, op: unset }
141146
- type: cask
142147
name: alfred # better spotlight
143148
conditions:

home/.chezmoidata/brew/10-chipwolf.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ brew:
1414
name: aws-vault
1515
conditions:
1616
- { kind: env, name: CODESPACES, op: unset }
17+
- { kind: env, name: PRIVATE, op: unset }
1718
- type: brew
1819
name: awscli
1920
conditions:
2021
- { kind: env, name: CODESPACES, op: unset }
22+
- { kind: env, name: PRIVATE, op: unset }
2123
- type: brew
2224
name: bitwarden-cli
2325
conditions:
@@ -30,6 +32,7 @@ brew:
3032
name: btop
3133
conditions:
3234
- { kind: env, name: CODESPACES, op: unset }
35+
- { kind: env, name: WSL, op: unset }
3336
- type: brew
3437
name: cbonsai
3538
conditions:
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Chocolatey packages for Windows bootstrap (`run_onchange_after_bootstrap_windows.ps1.tmpl`).
2+
# Same overlay idea as `home/.chezmoidata/brew/`: add `10-*.yaml` files; each overlay's `packages` list is concatenated in lexical overlay name order.
3+
chocolatey:
4+
overlays:
5+
base:
6+
packages:
7+
- bat
8+
- bitwarden-cli
9+
- discord
10+
- eza
11+
- fd
12+
- fzf
13+
- gnupg
14+
- jq
15+
- mise
16+
- nerd-fonts-FiraCode
17+
- neovim
18+
- obsidian
19+
- oh-my-posh
20+
- opencode
21+
- powershell-core
22+
- powertoys
23+
- ripgrep
24+
- sysinternals
25+
- tree-sitter
26+
- treesizefree
27+
- wingetui
28+
- winlibs
29+
- wezterm

home/.chezmoidata/profile.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,6 @@ profile:
88
# signingKey: "0x4C90101E11349775"
99
codespaces:
1010
gitName: "Chip Wolf ‮"
11+
windows:
12+
# Linux username for WSL Ubuntu (cloud-init user, wsl.conf default). Lowercase. Required on Windows; bootstrap/WSL scripts fail apply if empty.
13+
wslUser: "wolf"
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Winget package ids for Windows bootstrap (`run_onchange_after_bootstrap_windows.ps1.tmpl`).
2+
# Overlay merge matches `home/.chezmoidata/brew/` and chocolatey: each overlay contributes a `packages` list, concatenated in lexical overlay name order.
3+
winget:
4+
overlays:
5+
base:
6+
packages:
7+
- Wakatime.CLIWakatime
8+
- Wakatime.DesktopWakatime
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# cloud-init user-data for WSL Ubuntu first boot (written to %USERPROFILE%\.cloud-init\Ubuntu.user-data).
2+
# Placeholders: __WSL_USER__ (profile.windows.wslUser), __WSL_CHEZMOI_SOURCE__ (Windows chezmoi source as /mnt/<drive>/... path, baked in at apply).
3+
# Seeds WSL once from the Windows chezmoi source (cp from drvfs). If ~/.local/share/chezmoi already exists, skips copy and init.
4+
windowsWsl:
5+
ubuntuCloudInitUserData: |
6+
#cloud-config
7+
users:
8+
- name: __WSL_USER__
9+
groups: [adm,dialout,cdrom,floppy,sudo,audio,dip,video,plugdev,netdev]
10+
sudo: ALL=(ALL) NOPASSWD:ALL
11+
shell: /bin/bash
12+
13+
write_files:
14+
- path: /etc/wsl.conf
15+
append: true
16+
content: |
17+
[user]
18+
default=__WSL_USER__
19+
20+
runcmd:
21+
- |
22+
sudo -u __WSL_USER__ bash -lc '
23+
if [ -d /home/__WSL_USER__/.local/share/chezmoi ]; then
24+
exit 0
25+
fi
26+
27+
mkdir -p /home/__WSL_USER__/.local/share
28+
cp -a __WSL_CHEZMOI_SOURCE__ /home/__WSL_USER__/.local/share/chezmoi
29+
30+
if ! command -v chezmoi >/dev/null 2>&1; then
31+
sh -c "`$(curl -fsLS get.chezmoi.io)" -- -b "`$HOME/.local/bin"
32+
fi
33+
34+
PATH="`$HOME/.local/bin:`$PATH"
35+
WSL_DISTRO_NAME="${WSL_DISTRO_NAME:-Ubuntu}" DOTFILES_SKIP_BITWARDEN=1 \
36+
"`$HOME/.local/bin/chezmoi" init --apply --guess-repo-url=false \
37+
--source="`$HOME/.local/share/chezmoi" --exclude=externals
38+
'

home/.chezmoiexternal.toml.tmpl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
{{- if and (not .codespaces) (not .ci) (not .wsl) }}
12
["Documents/Obsidian/Vault"]
23
type = "git-repo"
34
url = "https://github.com/ChipWolf/obsidian-on.git"
5+
{{- end }}

0 commit comments

Comments
 (0)