Skip to content

Installer: versioned folder layout with Current junction#2028

Draft
tyrielv wants to merge 2 commits into
microsoft:masterfrom
tyrielv:tyrielv/versioned-layout
Draft

Installer: versioned folder layout with Current junction#2028
tyrielv wants to merge 2 commits into
microsoft:masterfrom
tyrielv:tyrielv/versioned-layout

Conversation

@tyrielv

@tyrielv tyrielv commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Switches the installer from flat binary deployment to a versioned folder structure:

C:\Program Files\VFS for Git\
├── Versions\
│   ├── 1.0.24205.1\     ← old version (kept by GC)
│   └── 1.0.24210.3\     ← current
├── Current → Versions\1.0.24210.3\   (junction)
└── (legacy flat binaries, if migrating)

Changes

  • Versioned deployment: Files install to {app}\Versions\{version} with a Current junction
  • PATH update: Points to {app}\Current\ instead of flat {app}; cleans up legacy entry on upgrade
  • PendingUpgrade removed: Junction swap replaces the staged-upgrade mechanism entirely (~280 lines removed)
  • Version GC: Keeps 1 old version; only deletes when no running GVFS.Mount references it
  • Flat-layout migration: Detects legacy layout, moves binaries if mounts stopped
  • Near-atomic junction swap: Uses Current.new + rename strategy to minimize unavailable window
  • Upgrade tests: 3 new scenarios (fresh versioned, versioned→versioned, flat→versioned)

Motivation

With versioned folders, upgrades become zero-downtime: new version goes in a new folder, junction swaps immediately, running mounts continue from their old version folder until remounted. This eliminates the PendingUpgrade complexity and will enable simplifying the GVFS service to reduce elevation surface

Part of Plan UAC-Free Install Modernization (Phase 1)

Replaces flat {app}\ deployment with versioned Versions\<ver>\ + Current junction.
Eliminates PendingUpgrade staging flow — new version goes to new folder, junction
swaps atomically, mounts continue running from old version until unmounted.

Changes:
- [Files]: Deploy to {app}\Versions\{version}\ instead of {app}\
- [Registry]: PATH points to {app}\Current (junction) instead of {app}\
- [Dirs]: ProgramData under versioned folder
- [Code]: Add CreateOrUpdateCurrentJunction() — creates/updates junction post-install
- [Code]: Add GarbageCollectOldVersions() — keeps 1 most recent old version, deletes older
  (skips versions with running mounts detected via Get-Process gvfs.mount | .Path)
- [Code]: Remove KeepMountsRunning variable, IsNormalInstall/IsStagingInstall checks
- [Code]: Remove StagingUpdateService, ShowMountChoiceDialog (no longer needed)
- [Code]: Simplify PrepareToInstall — no mount detection, no staging, just stop service
- [Code]: Update InstallGVFSService to reference {app}\Current\GVFS.Service.exe
- [Code]: Update MountRepos, MigrateConfigAndStatusCacheFiles, WriteOnDiskVersion16CapableFile
  to use Current junction paths
- CurStepChanged: Remove staging logic, call CreateOrUpdateCurrentJunction + GarbageCollectOldVersions
- CurUninstallStepChanged: Remove {app}\Current from PATH instead of {app}

Flat-layout migration stub added (detects {app}\GVFS.exe, logs version) but defers
actual file move to future PR to reduce complexity.

Assisted-by: Claude Sonnet 4.5
Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
@tyrielv tyrielv force-pushed the tyrielv/versioned-layout branch from 344af6c to ab2195f Compare June 18, 2026 01:10
Fixes 5 critical issues in the Phase 2 versioned-layout installer:

1. Forward reference violation - Move CreateOrUpdateCurrentJunction,
   GetFileVersion, IsProcessRunningFromPath, and GarbageCollectOldVersions
   before CurStepChanged. Inno Setup Pascal does not allow forward refs.

2. Service starts before junction exists - Create Current junction in
   CurStepChanged(ssInstall) BEFORE file extraction, ensuring the junction
   exists when InstallGVFSService runs (AfterInstall callback during file
   copy). Service binPath references {app}\Current\GVFS.Service.exe.

3. Old PATH entry not cleaned up - On upgrade from flat to versioned layout,
   remove legacy {app} PATH entry in CurStepChanged(ssPostInstall). New
   entry points to {app}\Current.

4. Non-atomic junction swap - Use Current.new temporary: create junction at
   Current.new, rmdir old Current (check ResultCode), rename Current.new to
   Current. Eliminates window where Current doesn't exist.

5. Add upgrade test scenarios - Add versioned-fresh-install,
   versioned-upgrade, and flat-to-versioned-upgrade scenarios to
   .github/workflows/upgrade-tests.yaml with full test implementations.

Assisted-by: Claude Sonnet 4.5
Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
@tyrielv tyrielv force-pushed the tyrielv/versioned-layout branch from ab2195f to 410f0d2 Compare June 18, 2026 18:03
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