singularity-forge/scripts
Jeremy McSpadden c0342c0883 fix: recover + prevent #1364 .gsd/ data-loss (v2.30.0–v2.38.0) (#1635)
* fix: add recovery script for #1364 .gsd/ data-loss regression

Adds scripts/recover-gsd-1364.sh to help users whose .gsd/ files were
deleted by the ensureGitignore bug in v2.33.x–v2.35.x.

The script handles both damage scenarios:
- Scenario A: .gsd files deleted in working tree but not yet committed
- Scenario B: git rm --cached .gsd/ was committed (files gone from HEAD)

Steps performed:
1. Detects whether the repo is affected (symlink check, .gitignore scan,
   git history scan)
2. Finds the last clean commit before ".gsd" was added to .gitignore
3. Restores all deleted .gsd/ files via git checkout <clean-commit> -- .gsd/
4. Removes the bare ".gsd" line from .gitignore
5. Stages both changes and prints the ready-to-commit command

Supports --dry-run to preview without making changes.
Safe to run on unaffected repos — exits early with no modifications.

Closes #1364

* fix: add Windows PowerShell recovery script for #1364

Adds scripts/recover-gsd-1364.ps1, a PowerShell equivalent of the bash
recovery script for users on Windows.

Windows-specific differences handled:
- Junction detection: GSD's migrateToExternalState() uses symlinkSync()
  with type "junction" on Windows instead of a POSIX symlink. The script
  checks Get-Item.LinkType for both "SymbolicLink" and "Junction" so
  migrated repos exit cleanly on step 1.
- .gitignore rewrite uses [System.IO.File]::WriteAllLines() with UTF-8
  no-BOM encoding to match git's expectations on Windows, rather than
  shell redirection which can introduce BOM or CRLF issues.
- All git invocations use execFileSync-style array args via Invoke-Git
  helper — no shell string eval, no quoting edge cases.
- Colour output uses Write-Host -ForegroundColor instead of ANSI escapes.
- -DryRun is a proper PowerShell switch parameter.

Also updates recover-gsd-1364.sh header to:
- Clarify it is Linux/macOS only
- Point Windows users to the .ps1
- Correct the affected version range to v2.30.0-v2.35.x (was 2.33.x)
- Reference the three residual vectors on v2.36.0-v2.38.0 (PR #1635)

Usage on Windows:
  powershell -ExecutionPolicy Bypass -File scripts\recover-gsd-1364.ps1
  powershell -ExecutionPolicy Bypass -File scripts\recover-gsd-1364.ps1 -DryRun

* fix(gsd): close residual #1364 data-loss vectors on v2.36.0+

Two targeted fixes that close the three remaining paths where .gsd/
tracked files can still be silently deleted after the v2.36.0 fix.

--- Path 1: hasGitTrackedGsdFiles fails open on git error (gitignore.ts)

nativeLsFiles() swallows git failures via allowFailure=true and returns
[], making hasGitTrackedGsdFiles() indistinguishable between "nothing
tracked" and "git failed". On any transient git failure (locked index,
binary not on PATH, corrupted .git/index), the function returned false
and .gsd was added to .gitignore, deleting all tracked state.

Fix: after nativeLsFiles returns [], verify git is reachable with a
cheap rev-parse call. If git is unavailable, return true (fail safe —
assume tracked). The outer catch also returns true instead of false.

--- Path 2: migration never cleans git index (migrate-external.ts)

migrateToExternalState() correctly creates the .gsd symlink/junction but
never ran `git rm -r --cached .gsd/`. All previously tracked .gsd/* files
remained in the git index pointing through the new symlink, which git
cannot follow — causing PROJECT.md, milestones/, REQUIREMENTS.md etc. to
appear as deleted in git status immediately after every migration.

Fix: after the symlink is verified, run:
  git rm -r --cached --ignore-unmatch .gsd
--ignore-unmatch makes this a no-op on fresh/untracked projects.

--- Path 3: race between migration and ensureGitignore

Resolved by Path 2. If migration always cleans the index, the race
window (another process converting .gsd/ to a symlink between the
migrateToExternalState() and ensureGitignore() calls) is harmless —
the index is already clean and there is nothing to lose.

--- Tests added (gitignore-tracked-gsd.test.ts)

- hasGitTrackedGsdFiles returns true (fail-safe) when git is unavailable
  (simulated via .git/index.lock to force git ls-files failure)
- migrateToExternalState cleans git index so tracked files don't show
  as deleted after successful migration

Fixes residual vectors from #1364 (original fix: #1367, v2.36.0)

* fix(recovery): add Scenario C support to recover-gsd-1364 scripts

Scenario C: .gsd/ is already a symlink/junction (migration succeeded on
the filesystem) but `git rm -r --cached .gsd/` was never run, leaving
tracked .gsd/* files appearing as deleted in git status.

Both bash and PowerShell scripts previously exited early at Step 1 when
they detected a symlink. Now they continue with a dedicated Scenario C
path through all steps:

- Step 1: sets GSD_IS_SYMLINK flag, continues instead of exiting
- Step 2: inverted .gitignore check — warns if .gsd is MISSING (should
  be present for external-state layout) rather than if it's present
- Step 3: skips commit-history scan (index issue only, no file restore
  needed); exits clean if no stale entries found
- Step 4: skips damage-commit search (nothing to restore from history)
- Step 5: runs `git rm -r --cached --ignore-unmatch .gsd` to clean the
  stale index entries instead of restoring files from a prior commit
- Step 6: appends .gsd to .gitignore instead of removing it
- Step 7: stages only .gitignore (not .gsd/) to avoid the "gitignored
  path" error; the index cleanup from Step 5 is already staged
- Summary: uses a distinct commit message for Scenario C

Smoke-tested against a synthetic repo that replicates the exact Scenario
C failure mode (symlink in place, git rm --cached never run).
2026-03-20 13:26:09 -06:00
..
bump-version.mjs feat(ci): automate prod-release with version bump, changelog, and tag push (#1194) 2026-03-18 11:17:43 -06:00
check-skill-references.mjs fix: remove broken SwiftUI skill and add CI reference check (#1476) (#1520) 2026-03-19 18:04:37 -06:00
ci_monitor.cjs feat: add GitHub Workflows skill with CI workflow and ci_monitor tool (#294) 2026-03-13 22:31:17 -06:00
ci_monitor.md feat: add GitHub Workflows skill with CI workflow and ci_monitor tool (#294) 2026-03-13 22:31:17 -06:00
copy-export-html.cjs refactor: extract inline build scripts from package.json to files 2026-03-16 13:34:05 -05:00
copy-resources.cjs fix: apply pi manifest opt-out to extension-discovery.ts (#1545) 2026-03-20 08:11:51 -06:00
copy-themes.cjs refactor: extract inline build scripts from package.json to files 2026-03-16 13:34:05 -05:00
dev.js refactor: deduplicate help text, cross-platform validate-pack, fix dev.js 2026-03-16 13:29:31 -05:00
generate-changelog.mjs feat(ci): automate prod-release with version bump, changelog, and tag push (#1194) 2026-03-18 11:17:43 -06:00
install-hooks.sh feat: add pre-commit secret scanner and CI secret detection (#1148) 2026-03-18 08:33:17 -06:00
install-pi-global.js Add pi global install scripts (#57) 2026-03-11 14:34:03 -06:00
link-workspace-packages.cjs fix: detect broken install and add Windows symlink fallback (#890) 2026-03-17 09:35:57 -06:00
postinstall.js fix: strip clack UI from postinstall, keep silent Playwright download (#783) 2026-03-16 21:35:04 -06:00
preview-dashboard.ts feat(dashboard): two-column layout with redesigned widget (#1530) 2026-03-19 20:07:18 -06:00
recover-gsd-1364.ps1 fix: recover + prevent #1364 .gsd/ data-loss (v2.30.0–v2.38.0) (#1635) 2026-03-20 13:26:09 -06:00
recover-gsd-1364.sh fix: recover + prevent #1364 .gsd/ data-loss (v2.30.0–v2.38.0) (#1635) 2026-03-20 13:26:09 -06:00
secret-scan.sh feat: add pre-commit secret scanner and CI secret detection (#1148) 2026-03-18 08:33:17 -06:00
sync-pkg-version.cjs feat: vendor Pi source into workspace monorepo 2026-03-12 21:55:17 -06:00
uninstall-pi-global.js Add pi global install scripts (#57) 2026-03-11 14:34:03 -06:00
update-changelog.mjs feat(ci): automate prod-release with version bump, changelog, and tag push (#1194) 2026-03-18 11:17:43 -06:00
validate-pack.js fix: detect broken install and add Windows symlink fallback (#890) 2026-03-17 09:35:57 -06:00
validate-pack.sh fix: broken npm install — remove bundleDependencies, use postinstall symlinks (#369) 2026-03-14 10:04:12 -06:00
verify-s03.sh feat(wizard): add BRAVE_ANSWERS_KEY support 2026-03-10 22:44:28 -06:00
verify-s04.sh Replace remaining 'get stuff done' instances in verify script 2026-03-11 08:11:11 -06:00
version-stamp.mjs feat(ci): implement three-stage promotion pipeline (Dev → Test → Prod) (#1098) 2026-03-18 00:40:06 -06:00
watch-resources.js fix: read resources from dist/ to prevent branch-drift in npm-link setups (#314) 2026-03-14 11:47:03 -06:00