fix(setup): prefer symlinks on Windows when SeCreateSymbolicLinkPrivilege is available

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) <noreply@anthropic.com>
This commit is contained in:
TTmo123 2026-06-28 22:23:23 +08:00
parent 11de390be1
commit 9d506f4857
1 changed files with 13 additions and 1 deletions

14
setup
View File

@ -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