From 9d506f48574ada07750d2d86c8b657defc4f8aa1 Mon Sep 17 00:00:00 2001 From: TTmo123 Date: Sun, 28 Jun 2026 22:23:23 +0800 Subject: [PATCH] fix(setup): prefer symlinks on Windows when SeCreateSymbolicLinkPrivilege is available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The _link_or_copy() helper unconditionally takes the cp branch on Windows to avoid frozen copies from ln -snf without SeCreateSymbolicLinkPrivilege. But this is over-defense: users with Developer Mode or elevation can create real symlinks and should get the same auto-refresh-on-pull behavior as macOS/Linux. Fix: try ln -sn first with stderr redirected; verify the result is a real symlink ([ -L dst ] postcondition); on failure run a second rm -rf and fall through to cp -R / cp -f to preserve today's behavior for non-Developer-Mode users. The postcondition catches the rare case where ln exits 0 but writes a regular file under dst (AV software occupation, git index lock stale, etc.) — without it cp -R would fail with 'cannot overwrite non-directory with directory.' The if [ ! -e src ]; return 0 early-return is preserved from the original helper (not introduced by this PR). All raw ln calls stay inside the helper body, preserving the D7 static invariant. Co-Authored-By: Claude Opus 4.8 (1M context) --- setup | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/setup b/setup index 275236cd3..4d632cafd 100755 --- a/setup +++ b/setup @@ -44,7 +44,6 @@ _link_or_copy() { local src="$1" local dst="$2" if [ "$IS_WINDOWS" -eq 1 ]; then - rm -rf "$dst" # Unix `ln -snf` accepts a name-only or relative-path source even when the # target doesn't resolve from CWD (e.g. the connect-chrome alias points at # the sibling-relative "gstack/open-gstack-browser"). On Windows the @@ -53,6 +52,19 @@ _link_or_copy() { if [ ! -e "$src" ]; then return 0 fi + rm -rf "$dst" + # Prefer symlinks when the user has SeCreateSymbolicLinkPrivilege + # (Developer Mode ON, or running as admin). Real symlinks auto-refresh + # on `git pull`, unlike file copies. Fall back to copy on EACCES so + # unprivileged users without Developer Mode still get a working install. + if ln -sn "$src" "$dst" 2>/dev/null && [ -L "$dst" ]; then + return 0 + fi + # Symlink attempt didn't produce a real symlink — clean up any partial + # artifact (e.g. ln exited 0 but created a regular file under dst) and + # fall through to copy. Without this, cp -R would fail with "cannot + # overwrite non-directory ... with directory" when src is a directory. + rm -rf "$dst" if [ -d "$src" ]; then cp -R "$src" "$dst" else