fix: restore pinned Git in each Windows bootstrap stage
This commit is contained in:
@@ -443,8 +443,6 @@ function Get-Uv {
|
||||
# the same store slot (<store>\git-<version>-<target>\) pm uses. Returns the
|
||||
# git.exe path, or $null when no pinned artifact exists for this target.
|
||||
function Get-PinnedGit {
|
||||
$existing = Get-Command git -ErrorAction SilentlyContinue
|
||||
if ($existing) { return $existing.Source } # dev shortcut; fetches nothing
|
||||
$target = "win32-$(Get-WindowsArch)"
|
||||
$pin = $script:GitPinFiles[$target]
|
||||
if (-not $pin) { return $null }
|
||||
@@ -482,18 +480,14 @@ function Get-PinnedGit {
|
||||
return $gitExe
|
||||
}
|
||||
|
||||
# Ensure a usable git for the rest of the ladder: pinned pm store slot
|
||||
# first, then PATH. Returns $true on success.
|
||||
# Each -Stage is a new PowerShell process. Restore the pinned pm store Git
|
||||
# PATH in every stage that invokes git; never inherit an unpinned system Git.
|
||||
function Ensure-Git {
|
||||
$g = Get-PinnedGit
|
||||
if (-not $g) { return $false }
|
||||
if ($g -ne "git") {
|
||||
# Store-staged git: expose cmd + usr\bin on this process's PATH so
|
||||
# bare `git` works for the rest of the ladder (the same dirs pm's
|
||||
# git package env() composes).
|
||||
$gitEntry = Split-Path (Split-Path $g -Parent) -Parent
|
||||
$env:Path = "$gitEntry\cmd;$gitEntry\usr\bin;$env:Path"
|
||||
}
|
||||
# The same dirs pm's git package env() composes.
|
||||
$gitEntry = Split-Path (Split-Path $g -Parent) -Parent
|
||||
$env:Path = "$gitEntry\cmd;$gitEntry\usr\bin;$env:Path"
|
||||
return $true
|
||||
}
|
||||
|
||||
@@ -552,12 +546,13 @@ $Stages = @(
|
||||
$Stages += @{ name = "complete"; title = "Finish install"; category = "runtime"; needs_user_input = $false }
|
||||
function Stage-Prerequisites {
|
||||
if (-not (Ensure-Git)) {
|
||||
Fail "git is required. Install Git for Windows: https://git-scm.com/download/win"
|
||||
Fail "no pinned Git artifact for this Windows architecture"
|
||||
}
|
||||
Log "prerequisites ok (git)"
|
||||
}
|
||||
|
||||
function Stage-Repository {
|
||||
if (-not (Ensure-Git)) { Fail "no pinned Git artifact for this Windows architecture" }
|
||||
# An interrupted clone from an older installer can leave a .git with no
|
||||
# initial commit, where stash/checkout abort ("You do not have the initial
|
||||
# commit yet", #40998). Move it aside -- never delete it, it may hold
|
||||
@@ -886,7 +881,10 @@ function Confirm-DesktopArtifact {
|
||||
|
||||
function Stage-Complete {
|
||||
$commit = $Commit
|
||||
if (-not $commit) { $commit = Invoke-Native { git -C $InstallDir rev-parse HEAD 2>$null } }
|
||||
if (-not $commit) {
|
||||
if (-not (Ensure-Git)) { Fail "no pinned Git artifact for this Windows architecture" }
|
||||
$commit = Invoke-Native { git -C $InstallDir rev-parse HEAD 2>$null }
|
||||
}
|
||||
if ($commit) {
|
||||
$marker = [ordered]@{
|
||||
schemaVersion = 1
|
||||
|
||||
60
tests/scripts/install/test_install_ps1_staged_git.py
Normal file
60
tests/scripts/install/test_install_ps1_staged_git.py
Normal file
@@ -0,0 +1,60 @@
|
||||
"""The bootstrap's separately launched stages must each use PM's pinned Git."""
|
||||
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
INSTALLER = Path(__file__).resolve().parents[3] / "scripts" / "install.ps1"
|
||||
|
||||
|
||||
@pytest.mark.platforms("windows")
|
||||
def test_stage_processes_restore_pinned_git_and_never_fall_back(tmp_path):
|
||||
powershell = shutil.which("powershell.exe")
|
||||
system_git = shutil.which("git")
|
||||
assert powershell and system_git
|
||||
|
||||
origin = tmp_path / "origin"
|
||||
origin.mkdir()
|
||||
subprocess.run([system_git, "-C", str(origin), "init", "-q", "-b", "main"], check=True)
|
||||
(origin / "README").write_text("fixture", encoding="utf-8")
|
||||
subprocess.run([system_git, "-C", str(origin), "add", "README"], check=True)
|
||||
subprocess.run([system_git, "-C", str(origin), "-c", "user.name=Smoke",
|
||||
"-c", "user.email=smoke@example.invalid", "commit", "-qm", "initial"], check=True)
|
||||
expected = subprocess.run([system_git, "-C", str(origin), "rev-parse", "HEAD"],
|
||||
check=True, capture_output=True, text=True).stdout.strip()
|
||||
|
||||
home = tmp_path / "home"
|
||||
store = tmp_path / "tools"
|
||||
env = dict(os.environ, HERMES_HOME=str(home), HERMES_RUNTIME_DIR=str(store),
|
||||
HERMES_REPO_URL=str(origin))
|
||||
|
||||
def stage(name):
|
||||
result = subprocess.run([powershell, "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass",
|
||||
"-File", str(INSTALLER), "-Stage", name, "-Json"],
|
||||
env=env, capture_output=True, text=True, timeout=240)
|
||||
frames = [json.loads(line) for line in result.stdout.splitlines() if line.startswith("{")]
|
||||
assert result.returncode == 0 and len(frames) == 1 and frames[0]["ok"], result.stdout + result.stderr
|
||||
|
||||
stage("prerequisites")
|
||||
staged = list(store.glob("git-*/cmd/git.exe"))
|
||||
assert len(staged) == 1
|
||||
stage("repository") # new process; prerequisites' PATH cannot propagate
|
||||
stage("complete") # the marker's bare git call is also a new process
|
||||
checkout = home / "hermes-agent"
|
||||
actual = subprocess.run([str(staged[0]), "-C", str(checkout), "rev-parse", "HEAD"],
|
||||
check=True, capture_output=True, text=True).stdout.strip()
|
||||
marker = json.loads((checkout / ".hermes-bootstrap-complete").read_text(encoding="utf-8-sig"))
|
||||
assert actual == expected == marker["pinnedCommit"]
|
||||
|
||||
# Even when system Git is on PATH, an unsupported pin must fail closed.
|
||||
probe = (f". '{INSTALLER}'; "
|
||||
"$script:GitPinFiles.Remove(('win32-' + (Get-WindowsArch))); "
|
||||
"if (Ensure-Git) { exit 1 } else { exit 0 }")
|
||||
refused = subprocess.run([powershell, "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass",
|
||||
"-Command", probe], env=env, capture_output=True, text=True, timeout=30)
|
||||
assert refused.returncode == 0, refused.stdout + refused.stderr
|
||||
Reference in New Issue
Block a user