Skip to content

feat(setup): add first-class native Windows support - #44

Open
andrew-codes wants to merge 3 commits into
mainfrom
fm/devtools-windows-ho243
Open

feat(setup): add first-class native Windows support#44
andrew-codes wants to merge 3 commits into
mainfrom
fm/devtools-windows-ho243

Conversation

@andrew-codes

Copy link
Copy Markdown
Owner

Intent

The developer is migrating their personal dev environment from Ansible playbooks to a Nix/nix-darwin/home-manager flake, currently focused on the macOS (Apple Silicon) portion while keeping the legacy Ansible/Windows path as unmigrated reference. Across the session they wanted Nix to install and configure a specific set of tools (1Password and its CLI/SSH agent, Docker, kubectl, Flux, GitOps, Terraform, Ansible, Raycast, Volta for Node, Telepresence, Lens, various AXI npm tools, Go-based CLIs like no-mistakes and treehouse, plannotator, and a Mac App Store app), migrate their custom bin scripts and zsh completions into the repo as symlinks, and convert those scripts and rebuild.sh from bash to zsh. They also asked for OS-specific gitconfig via git includes, split SSH config to keep private host entries out of the public repo, an optional ~/.env secret-stubbing mechanism, pi agent hooks/subagents wiring, disabling Spotlight indexing, making setup.sh auto-dispatch by OS/platform with a clear unsupported message, and a README centered on capabilities, install steps, and supported platforms. Explicit constraints included preferring toolchain-managed/pinned installs over unpinned curl-pipe-sh scripts, never using em dashes, not auto-adding co-author lines, and changing Homebrew cleanup to not remove unreferenced packages (with AGENTS.md updated to record that as the deliberate choice).

What Changed

  • Added setup/windows.sh, a full native-Windows setup path (Git Bash, not WSL) covering winget package installs, Nerd Font and kubeseal downloads, dotfile symlinking, registry/system defaults, 1Password commit signing, and the Windows OpenSSH Client capability, plus setup.sh dispatch to it and a repo-root guard so an out-of-repo copy or the ~/.local/bin/devtools-rebuild symlink resolves correctly.
  • Added the bash side of the shared shell: home/.bash_profile, home/.bashrc (PATH, session vars, ~/.env sourcing with unset-secret warnings, aliases, completions, starship), Windows-aware wezterm.lua default_prog and starship.toml entries, and reworked home/.gitconfig-windows / home/.ssh/config-windows for native Windows.
  • Converted the home/bin/ scripts to portable bash so they run on both platforms, including CRLF stripping in aup and kaup when parsing native netstat/tasklist output.
  • Documented the two-platform contract: new setup/windows-parity.md mapping every macOS package, config, and system default to its Windows decision, plus readme.md and AGENTS.md updates and home.nix adjustments.

Note on open items from the pipeline: two low-severity infos remain unaddressed - a home/.bashrc secret-name guard that lets an empty or digit-leading key reach ${!_var}, and the readme.md install summary not listing the new OpenSSH Client step. The Windows-only halves of setup/windows.sh (winget installs, downloads, symlink step, registry defaults, OpenSSH capability) could not be executed from the macOS machine this ran on and remain unverified; shellcheck and bash -n passed, and the shared shell, bin scripts, wezterm, and setup entrypoints were exercised end to end.

Risk Assessment

✅ Low: Every prior finding, including the destructive repo-root resolution bug, is fixed with a matching fail-loud guard, the docs and parity contract were updated in step, shellcheck is clean, and the only remaining items are two cosmetic robustness/documentation nits confined to the additive Windows path.

Testing

I exercised the branch the way a Windows user would meet it, from macOS: a real login shell over the new .bash_profile/.bashrc in a throwaway HOME (PATH, session vars, /.env secrets warning, aliases, completions, starship prompt), the aup/kaup Windows branch against stub netstat/tasklist/taskkill emitting genuine CRLF, the devtools-rebuild symlink path into setup/windows.sh, ./setup.sh OS dispatch, the bash-converted home/bin commands against a scratch git repo, and the wezterm.lua Windows default-shell branch under lua. Each of the three fixes in the review commit was run at both HEAD1 and HEAD, and the before/after transcripts show the old behavior failing exactly as described (name printed as "?", a "killed" message while taskkill errored, and a repo root resolving to ~/.local) and the new behavior correct. nix eval confirms the macOS configuration still evaluates and the npm/Go/secret lists match between home.nix and setup/windows.sh. Everything passed; the only gap is that the winget installs, registry defaults, symlink step and the new OpenSSH capability step cannot execute outside Git Bash, so they remain untested here.

  • Evidence: Windows Git Bash startup, rendered with the shell's real colors (local file: /var/folders/mc/mzp6sm4d449b277kc1h4y3kw0000gn/T/no-mistakes-evidence/01KZRJZZ0FS379JNTRKG9G2VKN/windows-bash-startup.png)
Evidence: Same startup transcript as rendered HTML
<!doctype html><meta charset="utf-8"><title>Windows Git Bash startup</title>
<style>body{background:#1b1b26;margin:0;padding:28px;font-family:"Hack Nerd Font","JetBrains Mono",Menlo,monospace}
h1{color:#f8f8f2;font-size:15px;font-weight:600;margin:0 0 4px}
p{color:#9aa0b5;font-size:12px;margin:0 0 18px;max-width:70ch;line-height:1.5}
pre{color:#f8f8f2;font-size:12.5px;line-height:1.55;white-space:pre-wrap;word-break:break-word;margin:0;
 background:#12121b;border:1px solid #2a2a3c;border-radius:8px;padding:18px}</style>
<h1>What a Windows shell looks like after setup/windows.sh</h1>
<p>Real transcript of <code>bash --login -i</code> against this branch's <code>home/.bash_profile</code> and
<code>home/.bashrc</code>, symlinked into a throwaway HOME the way step 8 links them. Colours are the actual
escape sequences the shell emitted, including the starship prompt from the shared <code>starship.toml</code>
and the unset-secret warning.</p>
<pre>### login shell start (bash --login -i), HOME=/var/folders/mc/mzp6sm4d449b277kc1h4y3kw0000gn/T/tmp.rRZ1uYsPBt

bash: cannot set terminal process group (92717): Inappropriate ioctl for device
bash: no job control in this shell
⚠  Unset secrets in ~/.env: CONTEXT7_API_KEY
   Set them there before using tooling that needs them.
<span style="font-weight:600;color:#8be9fd">01KZRJZZ0FS379JNTRKG9G2VKN</span> on <span style="font-weight:600;color:#bd93f9"> HEAD</span> 
<span style="color:#bd93f9">❯</span> echo &quot;--- PATH front (home.nix&#x27;s PATH loop, ported to bash) ---&quot;
--- PATH front (home.nix&#x27;s PATH loop, ported to bash) ---
<span style="font-weight:600;color:#8be9fd">01KZRJZZ0FS379JNTRKG9G2VKN</span> on <span style="font-weight:600;color:#bd93f9"> HEAD</span> 
<span style="color:#bd93f9">❯</span> echo &quot;$PATH&quot; | tr &#x27;:&#x27; &#x27;\n&#x27; | head -3
~/.local/bin
~/go/bin
/opt/homebrew/bin
<span style="font-weight:600;color:#8be9fd">01KZRJZZ0FS379JNTRKG9G2VKN</span> on <span style="font-weight:600;color:#bd93f9"> HEAD</span> 
<span style="color:#bd93f9">❯</span> echo

<span style="font-weight:600;color:#8be9fd">01KZRJZZ0FS379JNTRKG9G2VKN</span> on <span style="font-weight:600;color:#bd93f9"> HEAD</span> 
<span style="color:#bd93f9">❯</span> echo &quot;--- session variables ---&quot;
--- session variables ---
<span style="font-weight:600;color:#8be9fd">01KZRJZZ0FS379JNTRKG9G2VKN</span> on <span style="font-weight:600;color:#bd93f9"> HEAD</span> 
<span style="color:#bd93f9">❯</span> echo &quot;EDITOR=$EDITOR  REPO_HOME=$REPO_HOME  VOLTA_HOME=$VOLTA_HOME&quot;
EDITOR=nvim  REPO_HOME=~/developer/repos  VOLTA_HOME=/Users/andrew/.volta
<span style="font-weight:600;color:#8be9fd">01KZRJZZ0FS379JNTRKG9G2VKN</span> on <span style="font-weight:600;color:#bd93f9"> HEAD</span> 
<span style="color:#bd93f9">❯</span> 
&lt;purpose: 1Password serves a named pipe on Windows)&quot;
SSH_AUTH_SOCK=[unset]  (.bashrc sets none on purpose: 1Password serves a named pipe on Windows)
<span style="font-weight:600;color:#8be9fd">01KZRJZZ0FS379JNTRKG9G2VKN</span> on <span style="font-weight:600;color:#bd93f9"> HEAD</span> 
<span style="color:#bd93f9">❯</span> echo

<span style="font-weight:600;color:#8be9fd">01KZRJZZ0FS379JNTRKG9G2VKN</span> on <span style="font-weight:600;color:#bd93f9"> HEAD</span> 
<span style="color:#bd93f9">❯</span> echo &quot;--- secrets sourced from ~/.env under set -a ---&quot;
--- secrets sourced from ~/.env under set -a ---
<span style="font-weight:600;color:#8be9fd">01KZRJZZ0FS379JNTRKG9G2VKN</span> on <span style="font-weight:600;color:#bd93f9"> HEAD</span> 
<span style="color:#bd93f9">❯</span> echo &quot;ALREADY_SET_KEY=$ALREADY_SET_KEY&quot;
ALREADY_SET_KEY=sk-live-value
<span style="font-weight:600;color:#8be9fd">01KZRJZZ0FS379JNTRKG9G2VKN</span> on <span style="font-weight:600;color:#bd93f9"> HEAD</span> 
<span style="color:#bd93f9">❯</span> echo &quot;CONTEXT7_API_KEY=[${CONTEXT7_API_KEY:-empty}]&quot;
CONTEXT7_API_KEY=[empty]
<span style="font-weight:600;color:#8be9fd">01KZRJZZ0FS379JNTRKG9G2VKN</span> on <span style="font-weight:600;color:#bd93f9"> HEAD</span> 
<span style="color:#bd93f9">❯</span> echo

<span style="font-weight:600;color:#8be9fd">01KZRJZZ0FS379JNTRKG9G2VKN</span> on <span style="font-weight:600;color:#bd93f9"> HEAD</span> 
<span style="color:#bd93f9">❯</span> echo &quot;--- aliases (home.nix&#x27;s shellAliases) ---&quot;
--- aliases (home.nix&#x27;s shellAliases) ---
<span style="font-weight:600;color:#8be9fd">01KZRJZZ0FS379JNTRKG9G2VKN</span> on <span style="font-weight:600;color:#bd93f9"> HEAD</span> 
<span style="color:#bd93f9">❯</span> alias m; alias cc; alias ..
alias m=&#x27;git switch main&#x27;
alias cc=&#x27;claude --dangerously-skip-permissions&#x27;
alias ..=&#x27;cd ..&#x27;
<span style="font-weight:600;color:#8be9fd">01KZRJZZ0FS379JNTRKG9G2VKN</span> on <span style="font-weight:600;color:#bd93f9"> HEAD</span> 
<span style="color:#bd93f9">❯</span> echo

<span style="font-weight:600;color:#8be9fd">01KZRJZZ0FS379JNTRKG9G2VKN</span> on <span style="font-weight:600;color:#bd93f9"> HEAD</span> 
<span style="color:#bd93f9">❯</span> echo &quot;--- completions loaded from ~/.config/bash/bin-completion ---&quot;
--- completions loaded from ~/.config/bash/bin-completion ---
<span style="font-weight:600;color:#8be9fd">01KZRJZZ0FS379JNTRKG9G2VKN</span> on <span style="font-weight:600;color:#bd93f9"> HEAD</span> 
<span style="color:#bd93f9">❯</span> complete -p aup db gco 2&gt;/dev/null || echo &quot;(none registered)&quot;
complete -F _aup_complete aup
complete -F _db_complete db
complete -F _gco_complete gco
<span style="font-weight:600;color:#8be9fd">01KZRJZZ0FS379JNTRKG9G2VKN</span> on <span style="font-weight:600;color:#bd93f9"> HEAD</span> 
<span style="color:#bd93f9">❯</span> logout
</pre>
Evidence: aup / kaup CRLF fix, before and after

### home/bin/aup 3000 -- Windows branch (no lsof; netstat/tasklist emit CRLF) $ aup 3000 # (before: HEAD1) ? 7421<CR> LISTENING 0.0.0.0:3000 $ aup 3000 # (after: HEAD) node.exe 7421 LISTENING 0.0.0.0:3000 ### home/bin/kaup 3000 -- Windows branch (taskkill rejects a pid with a stray CR) $ kaup 3000 # (before: HEAD1) ERROR: The process "7421<CR>" not found.<CR> Killed process 7421<CR> listening on port 3000 $ kaup 3000 # (after: HEAD) Killed process 7421 listening on port 3000

### home/bin/aup 3000 -- Windows branch (no lsof; netstat/tasklist emit CRLF)

$ aup 3000        # (before: HEAD~1)
?	7421<CR>	LISTENING	0.0.0.0:3000
  [exit 0]

$ aup 3000        # (after: HEAD)
node.exe	7421	LISTENING	0.0.0.0:3000
  [exit 0]

### home/bin/kaup 3000 -- Windows branch (taskkill rejects a pid with a stray CR)

$ kaup 3000        # (before: HEAD~1)
ERROR: The process "7421<CR>" not found.<CR>
Killed process 7421<CR> listening on port 3000
  [exit 0]

$ kaup 3000        # (after: HEAD)
Killed process 7421 listening on port 3000
  [exit 0]

### macOS branch unchanged: aup on a port this shell is really listening on

$ aup 58205
lsof: WARNING: can't stat() smbfs file system /Volumes/.timemachine/nas-01._smb._tcp.local./2331A137-DEC5-4A6E-A00C-942D434CEB2A/backup
      Output information may be incomplete.
      assuming "dev=36000195" from mount table
Python  95787 andrew    3u  IPv4 0x57dc5614b195759a      0t0  TCP 127.0.0.1:58205 (LISTEN)
Evidence: setup.sh dispatch and windows.sh repo-root resolution

$ ./setup.sh # on an unsupported platform (macOS reporting x86_64) ==> Detected: macos / x86_64 Error: Unsupported platform: macos / x86_64 Currently supported: macOS on Apple Silicon (arm64), Windows on x86_64. $ /.local/bin/devtools-rebuild # (before: HEAD1) resolved repo root: .../home/.local contains setup/windows.sh: NO -- ~/.dotfiles and every dotfile symlink would point here $ ~/.local/bin/devtools-rebuild # (after: HEAD) resolved repo root: .../HEAD contains setup/windows.sh: yes $ /tmp/loose-copy/windows.sh # after: script copied out of the repo Error: could not locate the repo root (resolved ...).

### 1. ./setup.sh dispatch

$ ./setup.sh            # on an unsupported platform (macOS reporting x86_64)
==> Detected: macos / x86_64
Error: Unsupported platform: macos / x86_64
Currently supported: macOS on Apple Silicon (arm64), Windows on x86_64.
  [exit 1]

$ OSTYPE=msys ./setup.sh   # Windows: dispatches to setup/windows.sh, which
                           # then refuses because this is not Git Bash
==> Detected: windows / x86_64
Error: winget is not on PATH.
It ships with Windows 11 as App Installer. Install or update it from
the Microsoft Store ('App Installer'), then re-run this script.
  [exit 1]

### 2. repo-root resolution in setup/windows.sh

$ ~/.local/bin/devtools-rebuild        # (before: HEAD~1)
  resolved repo root: /var/folders/mc/mzp6sm4d449b277kc1h4y3kw0000gn/T/tmp.uxfZvq2Jn0/home/.local
  contains setup/windows.sh: NO -- ~/.dotfiles and every dotfile symlink would point here
  Error: setup/windows.sh has to run from Git for Windows' bash.
  Detected OSTYPE='darwin25.4.0'.
  [exit 1]

$ ~/.local/bin/devtools-rebuild        # (after: HEAD)
  resolved repo root: /private/var/folders/mc/mzp6sm4d449b277kc1h4y3kw0000gn/T/tmp.uxfZvq2Jn0/HEAD
  contains setup/windows.sh: yes
  Error: setup/windows.sh has to run from Git for Windows' bash.
  Detected OSTYPE='darwin25.4.0'.
  [exit 1]

$ /tmp/loose-copy/windows.sh          # after: script copied out of the repo
  Error: could not locate the repo root (resolved /private/var/folders/mc/mzp6sm4d449b277kc1h4y3kw0000gn/T/tmp.uxfZvq2Jn0).
  [exit 1]
Evidence: wezterm default_prog on Windows and macOS, plus the shared starship prompt

$ wezterm on Windows, machine-wide Git install default_prog = { .../ProgramFiles\Git\bin\bash.exe, -l } $ wezterm on Windows, per-user Git install only (%ProgramFiles% unset) default_prog = { .../LocalAppData\Programs\Git\bin\bash.exe, -l } $ wezterm on Windows, no Git installed anywhere default_prog = (unset -- WezTerm's own default shell) $ wezterm on macOS (the branch must not fire) default_prog = (unset -- WezTerm's own default shell)

### wezterm.lua default_prog on Windows

$ wezterm on Windows, machine-wide Git install
  default_prog = { /var/folders/mc/mzp6sm4d449b277kc1h4y3kw0000gn/T/tmp.l3INRvvFZj/ProgramFiles\Git\bin\bash.exe, -l }
$ wezterm on Windows, per-user Git install only (%ProgramFiles% unset)
  default_prog = { /var/folders/mc/mzp6sm4d449b277kc1h4y3kw0000gn/T/tmp.l3INRvvFZj/LocalAppData\Programs\Git\bin\bash.exe, -l }
$ wezterm on Windows, no Git installed anywhere
  default_prog = (unset -- WezTerm's own default shell)
$ wezterm on macOS (the branch must not fire)
  default_prog = (unset -- WezTerm's own default shell)

### shared home/.config/starship.toml, rendered by starship

  %{�[1;36m%}01KZRJZZ0FS379JNTRKG9G2VKN%{�[0m%} on %{�[1;35m%} HEAD%{�[0m%} 
%{�[35m%}❯%{�[0m%} 
Evidence: home/bin commands running under bash
### home/bin commands under bash, PATH=~/.local/bin (shebang: #!/usr/bin/env bash)

$ st
  On branch main
  nothing to commit, working tree clean

$ glg
  * 36aec82 - (HEAD -> main) third commit (0 seconds ago) <Tester>
  * 3774e1b - second commit (0 seconds ago) <Tester>
  * 4cfa896 - first commit (0 seconds ago) <Tester>
$ projs
  alpha-api
  alpha-web
  beta-tools

$ projs alpha
  alpha-api
  alpha-web

$ gnxt main   # from the first commit, step forward one
  Previous HEAD position was 4cfa896 first commit
  HEAD is now at 3774e1b second commit
  HEAD is now: second commit

$ echo scratch > b.txt; stash; st
  Saved working directory and index state WIP on main: 36aec82 third commit
  (stash list: 1 entry)

$ rh          # reset --hard + clean -fd
  HEAD is now at 36aec82 third commit
  Removing untracked.txt
  worktree after: 0 changed paths

$ gwta ../wt-demo demo
  Preparing worktree (checking out 'demo')
  HEAD is now at 36aec82 third commit
  worktrees: 2
- Outcome: ⚠️ 1 warning across 1 run (8m41s)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

🔧 **Rebase** - 1 issue found → auto-fixed ✅
  • ⚠️ readme.md - merge conflict rebasing onto origin/main

🔧 Fix applied.
✅ Re-checked - no issues remain.

⚠️ **Review** - 2 infos
  • 🚨 setup/windows.sh:22 - SCRIPT_DIR is computed as dirname "${BASH_SOURCE[0]}"/.. which does not resolve symlinks. Step 8 links this file to ~/.local/bin/devtools-rebuild, and readme.md documents devtools-rebuild as the way to apply later changes on Windows. Invoked that way, BASH_SOURCE[0] is ~/.local/bin/devtools-rebuild, so SCRIPT_DIR becomes $HOME/.local. Step 3 then does rm -f ~/.dotfiles and relinks it to $HOME/.local, and Step 8 recreates every dotfile link pointing at $HOME/.local/home/... (dangling), removing the correct links on the way; the for script in &#34;$SCRIPT_DIR&#34;/home/bin/* loop matches nothing and creates a symlink literally named *. rebuild.sh already handles this with SCRIPT_DIR=&#34;${0:A:h}&#34; and a comment explaining the devtools-rebuild case. Fix: resolve the source path first, e.g. SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")/.." && pwd)".
  • ⚠️ home/bin/kaup:14 - netstat is a native Windows program, so its output lines end in CRLF and the last awk field ($5, the PID) carries a trailing \r. taskkill /F /PID &#34;1234\r&#34; fails, its error goes to stderr (only stdout is redirected) and the script has no set -e, so kaup then prints "Killed process 1234 listening on port N" while the process is still alive. setup/windows.sh already applies | tr -d &#39;\r&#39; to native-program output twice, so the pattern is known; add the same here (e.g. pipe the netstat output through tr -d &#39;\r&#39;).
  • ⚠️ home/bin/aup:14 - Same CRLF problem on the read side: the PID field parsed out of netstat retains a trailing \r, so tasklist /FI &#34;PID eq 1234\r&#34; never matches and every row prints the ? placeholder instead of a process name, with the stray CR also mangling the printf'd line in the terminal. Strip \r from the netstat output before the awk parse.
  • ℹ️ setup/windows.sh:336 - mkdir -p &#34;$FONT_EXTRACT&#34; &#34;$FONT_DIR&#34; is repeated verbatim on lines 335 and 336. Harmless, but one of the two should go.
  • ℹ️ home/.bashrc:128 - The required-secret list now exists in three places (secretEnvVars in home.nix, SECRET_ENV_VARS in setup/windows.sh, _secret_vars here), and readme.md's Customizing table instructs bumping all three. The shell copy is avoidable: the warning could iterate the keys already stubbed into ~/.env and report the ones with empty values, which keeps the list at two declarations. This changes what the warning covers slightly (any empty key in ~/.env, not a hardcoded set), so it is a call for the author.
  • ℹ️ home/.gitconfig-windows:9 - core.sshCommand is pinned to the absolute C:/Windows/System32/OpenSSH/ssh.exe. The reasoning for not using a bare ssh.exe is sound, but the OpenSSH Client is an optional Windows feature and setup/windows.sh never checks for it, so on a machine without it every git fetch/push fails with a bare 'cannot run' error and nothing in the setup output hints at the cause. A preflight [ -f /c/Windows/System32/OpenSSH/ssh.exe ] || warn ... in windows.sh would surface it at setup time.
  • ℹ️ setup/windows.sh:469 - home/.config/herdr is linked as a directory into both ~/.config/herdr and %APPDATA%\herdr. %APPDATA%&lt;app> is conventionally where a Windows app writes runtime state, not just config, and the tracked directory already contains a herdr-written .plugins.lock - so anything herdr writes there (state, tokens) lands in this public checkout. That is the pattern AGENTS.md warns about for third-party writes through out-of-store symlinks. The parity doc records the double link as deliberate, and herdr is not installed on Windows today, so this is a heads-up rather than a defect.
  • ℹ️ home.nix:288 - The comment above the .ssh/config-os entry still says the OS-specific bits are '(the 1Password IdentityAgent path on macOS; nothing needed on WSL)'. This branch retargets the Windows path to native Windows and rewrites home/.ssh/config-windows accordingly, so the WSL reference is now wrong.

🔧 Fix: Fix Windows setup script root resolution, CRLF parsing, and OpenSSH step
2 infos still open:

  • ℹ️ home/.bashrc:144 - The derived-secret loop validates the key with case &#34;$_var&#34; in *[!A-Za-z0-9_]*) continue, which accepts two names that are not valid bash identifiers: the empty string (a line beginning with =) and a digit-leading name (e.g. 1PASSWORD_TOKEN=, plausible in this environment). ${!_var:-} then aborts that command with bash: : invalid variable name / bash: 1PASSWORD_TOKEN: invalid variable name on stderr at every shell start, and in the empty case a blank entry is appended to the reported list. Sourcing continues, so the impact is a spurious error line rather than a broken rc. Fix: extend the guard to case &#34;$_var&#34; in &#39;&#39; | [0-9]* | *[!A-Za-z0-9_]*) continue ;; esac.
  • ℹ️ readme.md:51 - The Windows install summary enumerates what setup/windows.sh does, and item 7 still ends at "applies the Windows system defaults, and wires up 1Password commit signing" - it does not mention the new step that installs the Windows OpenSSH Client capability, which is the one that can raise an extra UAC prompt on a fresh machine. setup/windows-parity.md was updated; this list was not.
⚠️ **Test** - 1 warning
  • ⚠️ setup/windows.sh:174 - The Windows-only half of setup/windows.sh cannot be executed from this macOS machine: the script exits at its Git Bash preflight, so the winget package installs (step 1-4), the Nerd Font and kubeseal downloads, the dotfile symlink step 8, the registry/system defaults step 12, and the new OpenSSH Client capability step 13 are all unverified. Root resolution, the new repo-root guard, and every shared shell/bin/wezterm file were verified; the install and system-mutation steps need a real Windows machine before this can be called proven.
  • bash run-shell-startup.sh &lt;repo&gt; - login shell against home/.bash_profile + home/.bashrc in a throwaway HOME: PATH loop, session vars, ~/.env sourcing, unset-secret warning, aliases, bin-completions, starship prompt
  • bash run-crlf-port-tools.sh &lt;repo&gt; 249fb25 - home/bin/aup and home/bin/kaup Windows branch (no lsof; stub netstat/tasklist/taskkill emitting CRLF), run at HEAD~1 and HEAD; plus aup on a real listening port to confirm the macOS branch is unchanged
  • bash run-setup-entrypoints.sh &lt;repo&gt; 249fb25 - ./setup.sh unsupported-platform and windows dispatch; setup/windows.sh repo-root resolution via a /.local/bin/devtools-rebuild symlink at HEAD1 vs HEAD; new root guard against a copy outside the repo
  • bash run-bin-scripts.sh &lt;repo&gt; - st, glg, projs, projs alpha, gnxt, stash, rh, gwta under bash from ~/.local/bin against a scratch git repo
  • bash run-wezterm-and-prompt.sh &lt;repo&gt; - wezterm.lua default_prog under lua with a stubbed wezterm module for machine-wide Git, per-user-only Git, no Git, and macOS; starship prompt rendered from home/.config/starship.toml
  • nix eval .#darwinConfigurations.mac.system.outPath - macOS configuration still evaluates after the home.nix changes
  • grep comparison of globalNpmPackages / goPackages / secretEnvVars in home.nix against GLOBAL_NPM_PACKAGES / GO_PACKAGES / SECRET_ENV_VARS in setup/windows.sh
✅ **Document** - passed

✅ No issues found.

⚠️ **Lint** - 1 info
  • ℹ️ setup/windows.sh:48 - shfmt -d reports diffs in setup/windows.sh (and pre-existing ones in setup/macOS.sh, home/.bashrc, rebuild.sh). The repo has no shfmt config or CI enforcing it, and applying it would collapse the deliberately aligned trailing-comment columns in WINGET_PACKAGES and the reg_* block, hurting readability. Left unformatted on purpose; adopting shfmt repo-wide would be a separate decision.
✅ **Push** - passed

✅ No issues found.

Nix has no native Windows support, so setup/windows.sh carries alone what
nix-darwin and home-manager carry on macOS: winget packages, dotfile symlinks,
activation-equivalent steps and system defaults. setup.sh dispatches to it when
it detects Git for Windows' bash, so one command still works on either OS.

The target is native Windows with Git Bash, not WSL, which is what the ticket
describes: bash as WezTerm's default shell, and the repo already cloned with
Git for Windows. home/.gitconfig-windows and home/.ssh/config-windows keep
their names and their place in the existing -os include scheme; only their
contents moved from WSL to native Windows.

setup/windows-parity.md is the inventory the ticket asks for: every macOS
package, config and system default mapped to implemented, deliberately skipped
with a reason, or deferred. Notable decisions recorded there are PowerToys
standing in for Raycast, twg having no Windows build at all (its installer
refuses anything but macOS and Linux), and herdr left to a manual install
because its only Windows path is an unpinned `irm | iex`.

Supporting changes:

- home/bin scripts move from zsh to bash so they run on both platforms. aup
  and kaup gain netstat/taskkill branches for the missing lsof.
- starship's settings move out of home.nix into a tracked
  home/.config/starship.toml that both platforms symlink, so the prompt is
  genuinely shared rather than macOS only.
- session-start.sh reaches through $VOLTA_HOME instead of a hard-coded
  ~/.volta.

Validated on macOS only, since the Windows path cannot execute here:
shellcheck and bash -n across every script, nix flake check plus a full darwin
eval, a Lua harness proving WezTerm resolves Git Bash (which caught a
nil-in-table bug that silently skipped every candidate), unit tests for the
linker's backup and idempotency contract, and a stubbed end-to-end dry run
whose second and third passes change nothing on disk.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant