aboutsummaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-08-18 22:39:38 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-08-18 22:39:38 +0300
commit9fac51722653fa0b0b6a5786166fb18c763424e0 (patch)
treea8efd3b30cf29a8fb11138b7444bb9baa0dac2fa /scripts
parent0bd445020ea19e0cea13f42a28212db7b2df196d (diff)
feat: make Windows generally available
Diffstat (limited to 'scripts')
-rw-r--r--scripts/changelog.py57
-rw-r--r--scripts/test_changelog.py50
-rw-r--r--scripts/windows_install_conpty_package_test.ps1143
3 files changed, 206 insertions, 44 deletions
diff --git a/scripts/changelog.py b/scripts/changelog.py
index 4d023ccc..9a96ea4e 100644
--- a/scripts/changelog.py
+++ b/scripts/changelog.py
@@ -19,13 +19,17 @@ DEFAULT_RELEASE_REPO = "herdrdev/herdr"
DEFAULT_LATEST_JSON_PATH = Path("website/latest.json")
DEFAULT_PRODUCT_ANNOUNCEMENT_PATH = Path("docs/next/product-announcement.json")
PROTOCOL_SOURCE_PATH = Path("src/protocol/wire.rs")
-ASSET_TARGETS = (
+CORE_ASSET_TARGETS = (
"linux-x86_64",
"linux-aarch64",
"macos-x86_64",
"macos-aarch64",
)
-EXPECTED_ASSET_NAMES = {target: f"herdr-{target}" for target in ASSET_TARGETS}
+ASSET_TARGETS = (*CORE_ASSET_TARGETS, "windows-x86_64")
+EXPECTED_ASSET_NAMES = {
+ **{target: f"herdr-{target}" for target in CORE_ASSET_TARGETS},
+ "windows-x86_64": "herdr-windows-x86_64.zip",
+}
@dataclass(frozen=True)
@@ -169,16 +173,22 @@ def infer_protocol_from_notes(notes: str) -> int | None:
return int(match.group(1))
-def normalize_assets(value: Any, label: str) -> dict[str, str]:
+def normalize_assets(
+ value: Any,
+ label: str,
+ required_targets: tuple[str, ...] = ASSET_TARGETS,
+) -> dict[str, str]:
if not isinstance(value, dict):
raise ChangelogError(f"{label} must be an object")
- missing_targets = [target for target in ASSET_TARGETS if target not in value]
+ missing_targets = [target for target in required_targets if target not in value]
if missing_targets:
raise ChangelogError(f"{label} is missing asset URL for {', '.join(missing_targets)}")
normalized_assets: dict[str, str] = {}
for target in ASSET_TARGETS:
+ if target not in value:
+ continue
url = value.get(target)
if not isinstance(url, str) or not url.strip():
raise ChangelogError(f"{label} is missing asset URL for {target}")
@@ -186,12 +196,22 @@ def normalize_assets(value: Any, label: str) -> dict[str, str]:
return normalized_assets
-def normalize_sha256(value: Any, label: str) -> dict[str, str]:
+def normalize_sha256(
+ value: Any,
+ label: str,
+ required_targets: tuple[str, ...] = ASSET_TARGETS,
+) -> dict[str, str]:
if not isinstance(value, dict):
raise ChangelogError(f"{label} must be an object")
+ missing_targets = [target for target in required_targets if target not in value]
+ if missing_targets:
+ raise ChangelogError(f"{label} has invalid SHA-256 for {missing_targets[0]}")
+
checksums: dict[str, str] = {}
for target in ASSET_TARGETS:
+ if target not in value:
+ continue
checksum = value.get(target)
if not isinstance(checksum, str) or re.fullmatch(
r"[0-9a-fA-F]{64}", checksum.strip()
@@ -225,11 +245,19 @@ def normalize_release_metadata(value: Any, label: str, version: str) -> dict[str
if inferred_protocol is not None:
metadata["protocol"] = inferred_protocol
if "assets" in value:
- metadata["assets"] = normalize_assets(value.get("assets"), f"{label}.assets")
+ metadata["assets"] = normalize_assets(
+ value.get("assets"),
+ f"{label}.assets",
+ required_targets=CORE_ASSET_TARGETS,
+ )
else:
metadata["assets"] = default_release_assets(version)
if "sha256" in value:
- metadata["sha256"] = normalize_sha256(value.get("sha256"), f"{label}.sha256")
+ metadata["sha256"] = normalize_sha256(
+ value.get("sha256"),
+ f"{label}.sha256",
+ required_targets=CORE_ASSET_TARGETS,
+ )
announcement = normalize_announcement(value.get("announcement"), label)
if announcement is not None:
metadata["announcement"] = announcement
@@ -306,11 +334,12 @@ def build_latest_json(
def default_release_assets(version: str, repo: str = DEFAULT_RELEASE_REPO) -> dict[str, str]:
+ """Return legacy release URLs for versions published before Windows stable assets."""
normalized_version = normalize_version(version)
tag = f"v{normalized_version}"
return {
target: f"https://github.com/{repo}/releases/download/{tag}/{EXPECTED_ASSET_NAMES[target]}"
- for target in ASSET_TARGETS
+ for target in CORE_ASSET_TARGETS
}
@@ -468,12 +497,20 @@ def archived_releases_from_current_manifest(manifest: dict[str, Any]) -> dict[st
metadata["protocol"] = protocol
assets = manifest.get("assets")
if isinstance(assets, dict):
- metadata["assets"] = normalize_assets(assets, "current root assets")
+ metadata["assets"] = normalize_assets(
+ assets,
+ "current root assets",
+ required_targets=CORE_ASSET_TARGETS,
+ )
else:
metadata["assets"] = default_release_assets(normalized_version)
sha256 = manifest.get("sha256")
if isinstance(sha256, dict):
- metadata["sha256"] = normalize_sha256(sha256, "current root sha256")
+ metadata["sha256"] = normalize_sha256(
+ sha256,
+ "current root sha256",
+ required_targets=CORE_ASSET_TARGETS,
+ )
announcement = normalize_announcement(manifest.get("announcement"), "current root")
if announcement is not None:
metadata["announcement"] = announcement
diff --git a/scripts/test_changelog.py b/scripts/test_changelog.py
index ad384734..6a9d12dd 100644
--- a/scripts/test_changelog.py
+++ b/scripts/test_changelog.py
@@ -24,20 +24,33 @@ from scripts.changelog import (
)
+def release_assets(version: str) -> dict[str, str]:
+ normalized = version.removeprefix("v")
+ return {
+ **default_release_assets(normalized),
+ "windows-x86_64": f"https://github.com/herdrdev/herdr/releases/download/v{normalized}/herdr-windows-x86_64.zip",
+ }
+
+
def release_sha256() -> dict[str, str]:
return {
"linux-x86_64": "a" * 64,
"linux-aarch64": "b" * 64,
"macos-x86_64": "c" * 64,
"macos-aarch64": "d" * 64,
+ "windows-x86_64": "e" * 64,
}
def release_assets_with_digests() -> list[dict[str, str]]:
return [
{
- "name": f"herdr-{target}",
- "url": f"https://example.com/{target}",
+ "name": (
+ "herdr-windows-x86_64.zip"
+ if target == "windows-x86_64"
+ else f"herdr-{target}"
+ ),
+ "url": f"https://example.com/{target}{'.zip' if target == 'windows-x86_64' else ''}",
"digest": f"sha256:{digest * 64}",
}
for target, digest in (
@@ -45,6 +58,7 @@ def release_assets_with_digests() -> list[dict[str, str]]:
("linux-aarch64", "b"),
("macos-x86_64", "c"),
("macos-aarch64", "d"),
+ ("windows-x86_64", "e"),
)
]
@@ -79,7 +93,7 @@ class ChangelogScriptTests(unittest.TestCase):
build_latest_json(
"0.1.1",
"\n### Fixed\n- One\n\n",
- default_release_assets("0.1.1"),
+ release_assets("0.1.1"),
release_sha256(),
)
)
@@ -92,7 +106,7 @@ class ChangelogScriptTests(unittest.TestCase):
build_latest_json(
"v0.1.1",
"### Fixed\n- Smoothed Claude flapping.\n",
- default_release_assets("0.1.1"),
+ release_assets("0.1.1"),
release_sha256(),
)
)
@@ -107,6 +121,7 @@ class ChangelogScriptTests(unittest.TestCase):
"linux-aarch64": "https://github.com/herdrdev/herdr/releases/download/v0.1.1/herdr-linux-aarch64",
"macos-x86_64": "https://github.com/herdrdev/herdr/releases/download/v0.1.1/herdr-macos-x86_64",
"macos-aarch64": "https://github.com/herdrdev/herdr/releases/download/v0.1.1/herdr-macos-aarch64",
+ "windows-x86_64": "https://github.com/herdrdev/herdr/releases/download/v0.1.1/herdr-windows-x86_64.zip",
},
)
self.assertEqual(manifest["releases"]["0.1.1"]["assets"], manifest["assets"])
@@ -118,7 +133,7 @@ class ChangelogScriptTests(unittest.TestCase):
build_latest_json(
"0.1.1",
"### Fixed\n- One",
- default_release_assets("0.1.1"),
+ release_assets("0.1.1"),
release_sha256(),
announcement={"id": "keybinding-v2", "title": "Keybind Refactor", "body": "body"},
)
@@ -138,7 +153,7 @@ class ChangelogScriptTests(unittest.TestCase):
build_latest_json(
"0.1.2",
"### Fixed\n- Two",
- default_release_assets("0.1.2"),
+ release_assets("0.1.2"),
release_sha256(),
releases={"0.1.1": {"notes": "### Fixed\n- One"}},
)
@@ -147,7 +162,7 @@ class ChangelogScriptTests(unittest.TestCase):
self.assertEqual(list(manifest["releases"]), ["0.1.2", "0.1.1"])
self.assertEqual(manifest["releases"]["0.1.2"]["notes"], "### Fixed\n- Two")
self.assertEqual(manifest["releases"]["0.1.2"]["protocol"], read_protocol_version())
- self.assertEqual(manifest["releases"]["0.1.2"]["assets"], default_release_assets("0.1.2"))
+ self.assertEqual(manifest["releases"]["0.1.2"]["assets"], release_assets("0.1.2"))
self.assertEqual(manifest["releases"]["0.1.1"]["notes"], "### Fixed\n- One")
self.assertEqual(manifest["releases"]["0.1.1"]["assets"], default_release_assets("0.1.1"))
@@ -157,7 +172,7 @@ class ChangelogScriptTests(unittest.TestCase):
build_latest_json(
"0.1.2",
"### Fixed\n- Two",
- default_release_assets("0.1.2"),
+ release_assets("0.1.2"),
release_sha256(),
releases={"0.1.1": {"notes": "### Fixed\n- One", "assets": assets}},
)
@@ -170,7 +185,7 @@ class ChangelogScriptTests(unittest.TestCase):
build_latest_json(
"0.1.2",
"### Fixed\n- Two",
- default_release_assets("0.1.2"),
+ release_assets("0.1.2"),
release_sha256(),
releases={"0.1.1": {"notes": "### Fixed\n- One", "protocol": 7}},
)
@@ -183,7 +198,7 @@ class ChangelogScriptTests(unittest.TestCase):
build_latest_json(
"0.1.2",
"### Fixed\n- Two",
- default_release_assets("0.1.2"),
+ release_assets("0.1.2"),
release_sha256(),
releases={
"0.1.1": {
@@ -337,6 +352,7 @@ class ChangelogScriptTests(unittest.TestCase):
"linux-aarch64": "https://example.com/linux-aarch64",
"macos-x86_64": "https://example.com/macos-x86_64",
"macos-aarch64": "https://example.com/macos-aarch64",
+ "windows-x86_64": "https://example.com/windows-x86_64.zip",
},
"sha256": release_sha256(),
},
@@ -426,6 +442,7 @@ class ChangelogScriptTests(unittest.TestCase):
"linux-aarch64": "https://example.com/linux-aarch64",
"macos-x86_64": "https://example.com/macos-x86_64",
"macos-aarch64": "https://example.com/macos-aarch64",
+ "windows-x86_64": "https://example.com/windows-x86_64.zip",
},
"sha256": release_sha256(),
}
@@ -438,6 +455,7 @@ class ChangelogScriptTests(unittest.TestCase):
"linux-aarch64": "https://example.com/linux-aarch64",
"macos-x86_64": "https://example.com/macos-x86_64",
"macos-aarch64": "https://example.com/macos-aarch64",
+ "windows-x86_64": "https://example.com/windows-x86_64.zip",
},
"sha256": release_sha256(),
}
@@ -446,7 +464,7 @@ class ChangelogScriptTests(unittest.TestCase):
self.assertEqual(canonical, expected)
def test_current_release_assets_must_be_mirrored(self) -> None:
- assets = default_release_assets("0.1.1")
+ assets = release_assets("0.1.1")
ensure_current_release_assets_are_mirrored(
{
"version": "0.1.1",
@@ -472,12 +490,12 @@ class ChangelogScriptTests(unittest.TestCase):
"version": "0.1.1",
"protocol": read_protocol_version(),
"notes": "### Fixed\n- One",
- "assets": default_release_assets("0.1.1"),
+ "assets": release_assets("0.1.1"),
"sha256": release_sha256(),
"releases": {
"0.1.1": {
"notes": "### Fixed\n- One",
- "assets": default_release_assets("0.1.0"),
+ "assets": release_assets("0.1.0"),
"sha256": release_sha256(),
}
},
@@ -497,6 +515,7 @@ class ChangelogScriptTests(unittest.TestCase):
"linux-aarch64": "https://example.com/linux-aarch64",
"macos-x86_64": "https://example.com/macos-x86_64",
"macos-aarch64": "https://example.com/macos-aarch64",
+ "windows-x86_64": "https://example.com/windows-x86_64.zip",
},
"sha256": release_sha256(),
},
@@ -509,6 +528,7 @@ class ChangelogScriptTests(unittest.TestCase):
"linux-aarch64": "https://example.com/linux-aarch64",
"macos-x86_64": "https://example.com/macos-x86_64",
"macos-aarch64": "https://example.com/macos-aarch64",
+ "windows-x86_64": "https://example.com/windows-x86_64.zip",
},
"sha256": release_sha256(),
},
@@ -531,14 +551,14 @@ class ChangelogScriptTests(unittest.TestCase):
"version": "0.1.1",
"protocol": read_protocol_version() + 1,
"notes": "### Fixed\n- One",
- "assets": default_release_assets("0.1.1"),
+ "assets": release_assets("0.1.1"),
"sha256": release_sha256(),
}
expected = {
"version": "0.1.1",
"protocol": read_protocol_version(),
"notes": "### Fixed\n- One",
- "assets": default_release_assets("0.1.1"),
+ "assets": release_assets("0.1.1"),
"sha256": release_sha256(),
}
diff --git a/scripts/windows_install_conpty_package_test.ps1 b/scripts/windows_install_conpty_package_test.ps1
index 95b31c4a..f4c4fc60 100644
--- a/scripts/windows_install_conpty_package_test.ps1
+++ b/scripts/windows_install_conpty_package_test.ps1
@@ -82,7 +82,7 @@ $listener = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Loopba
$listener.Start()
$port = ([System.Net.IPEndPoint]$listener.LocalEndpoint).Port
$listener.Stop()
-$manifest = @{
+$previewManifest = @{
channel = "preview"
base_version = "0.0.0"
build_id = "installer-test"
@@ -94,18 +94,35 @@ $manifest = @{
}
}
} | ConvertTo-Json -Depth 5
-$manifestPath = Join-Path $webRoot "preview.json"
-$manifest | Out-File -LiteralPath $manifestPath -Encoding utf8
+$legacyStableManifest = @{
+ version = "0.0.0"
+ assets = @{}
+} | ConvertTo-Json -Depth 5
+$stableManifest = @{
+ version = "0.0.1"
+ assets = @{
+ "windows-x86_64" = "http://127.0.0.1:$port/herdr-windows-x86_64.zip"
+ }
+ sha256 = @{
+ "windows-x86_64" = $hash
+ }
+} | ConvertTo-Json -Depth 5
+$previewManifestPath = Join-Path $webRoot "preview.json"
+$stableManifestPath = Join-Path $webRoot "latest.json"
+$previewManifest | Out-File -LiteralPath $previewManifestPath -Encoding utf8
+$legacyStableManifest | Out-File -LiteralPath $stableManifestPath -Encoding utf8
$server = $null
$oldHerdrHome = $env:HERDR_HOME
+$oldProcessPath = $env:Path
try {
$server = Start-Process python -ArgumentList @("-m", "http.server", "$port", "--bind", "127.0.0.1", "--directory", $webRoot) -PassThru -WindowStyle Hidden
$env:HERDR_HOME = Join-Path $root "unused\..\home"
- $manifestUrl = "http://127.0.0.1:$port/preview.json"
+ $previewManifestUrl = "http://127.0.0.1:$port/preview.json"
+ $stableManifestUrl = "http://127.0.0.1:$port/latest.json"
for ($attempt = 0; $attempt -lt 20; $attempt++) {
try {
- Invoke-WebRequest -Uri $manifestUrl -UseBasicParsing | Out-Null
+ Invoke-WebRequest -Uri $stableManifestUrl -UseBasicParsing | Out-Null
break
} catch {
if ($attempt -eq 19) { throw }
@@ -113,8 +130,31 @@ try {
}
}
+ $freshStableHome = Join-Path $root "fresh-stable-home"
+ $freshStableBin = Join-Path $root "fresh-stable-bin"
+ $stableManifest | Out-File -LiteralPath $stableManifestPath -Encoding utf8
+ $env:HERDR_HOME = $freshStableHome
+ $env:Path = $oldProcessPath
+ & $installerPath `
+ -ManifestUrl $stableManifestUrl `
+ -InstallDir $freshStableBin
+ $freshStableRelease = Get-ChildItem -LiteralPath (Join-Path $freshStableHome "packages\standalone\releases") -Directory |
+ Where-Object { $_.Name.StartsWith("0.0.1-") } |
+ Select-Object -First 1
+ if ($null -eq $freshStableRelease) {
+ throw "fresh installer did not default to the stable Windows package"
+ }
+
+ $legacyStableManifest | Out-File -LiteralPath $stableManifestPath -Encoding utf8
+ $env:HERDR_HOME = Join-Path $root "unused\..\home"
+ $env:Path = $oldProcessPath
+ & $installerPath `
+ -ManifestUrl $stableManifestUrl `
+ -InstallDir $installDir `
+ -ExpectedBuildId "installer-test"
+
# Keep the existing positional web-installer contract, including Retain in slot five.
- & "$PSScriptRoot\..\website\install.ps1" "preview" $manifestUrl $installDir "installer-test" 3
+ & $installerPath "preview" $previewManifestUrl $installDir "installer-test" 3
$localInstallDir = Join-Path $root "local-bin"
$env:HERDR_HOME = Join-Path $root "local-home"
@@ -136,7 +176,7 @@ try {
$badLocalChecksumRejected = $false
try {
& $installerPath `
- -ManifestUrl "$manifestUrl/unused" `
+ -ManifestUrl "$previewManifestUrl/unused" `
-InstallDir $localInstallDir `
-LocalPackagePath $archive `
-LocalPackageFormat "zip" `
@@ -153,7 +193,7 @@ try {
}
& $installerPath `
- -ManifestUrl "$manifestUrl/unused" `
+ -ManifestUrl "$previewManifestUrl/unused" `
-InstallDir $localInstallDir `
-LocalPackagePath $archive `
-LocalPackageFormat "zip" `
@@ -188,13 +228,14 @@ try {
}
Remove-Item -LiteralPath (Join-Path $releaseDir.FullName "conpty\conpty.dll") -Force
- $badManifest = $manifest | ConvertFrom-Json
+ $badManifest = $previewManifest | ConvertFrom-Json
$badManifest.assets."windows-x86_64".url = "http://127.0.0.1:$port/missing.zip"
- $badManifest | ConvertTo-Json -Depth 5 | Out-File -LiteralPath $manifestPath -Encoding utf8
+ $badManifest | ConvertTo-Json -Depth 5 | Out-File -LiteralPath $previewManifestPath -Encoding utf8
$downloadFailed = $false
try {
& "$PSScriptRoot\..\website\install.ps1" `
- -ManifestUrl $manifestUrl `
+ -Channel preview `
+ -ManifestUrl $previewManifestUrl `
-InstallDir $installDir `
-ExpectedBuildId "installer-test"
} catch {
@@ -210,7 +251,7 @@ try {
throw "failed repair removed the existing release"
}
- $manifest | Out-File -LiteralPath $manifestPath -Encoding utf8
+ $previewManifest | Out-File -LiteralPath $previewManifestPath -Encoding utf8
$stagedConpty = Join-Path $releasesDir ".staging.$($releaseDir.Name).$PID\conpty\conpty.dll"
$transientLockState = @{ Handle = $null; Acquired = $false; Released = $false }
@@ -245,8 +286,9 @@ try {
}.GetNewClosure()
$transientLockBreakpoint = Set-PSBreakpoint -Script $installerPath -Variable "backupDir" -Mode Write -Action $lockStagedFileTransiently
try {
- & "$PSScriptRoot\..\website\install.ps1" `
- -ManifestUrl $manifestUrl `
+ & $installerPath `
+ -Channel preview `
+ -ManifestUrl $previewManifestUrl `
-InstallDir $installDir `
-ExpectedBuildId "installer-test"
if (-not $transientLockState.Acquired) {
@@ -285,8 +327,9 @@ try {
try {
$swapFailed = $false
try {
- & "$PSScriptRoot\..\website\install.ps1" `
- -ManifestUrl $manifestUrl `
+ & $installerPath `
+ -Channel preview `
+ -ManifestUrl $previewManifestUrl `
-InstallDir $installDir `
-ExpectedBuildId "installer-test"
} catch {
@@ -317,7 +360,8 @@ try {
}
& "$PSScriptRoot\..\website\install.ps1" `
- -ManifestUrl $manifestUrl `
+ -Channel preview `
+ -ManifestUrl $previewManifestUrl `
-InstallDir $installDir `
-ExpectedBuildId "installer-test"
if (-not (Test-Path -LiteralPath (Join-Path $installDir "conpty\conpty.dll") -PathType Leaf)) {
@@ -329,7 +373,8 @@ try {
Move-Item -LiteralPath $x64HostDir -Destination $junctionTarget
New-Item -ItemType Junction -Path $x64HostDir -Target $junctionTarget | Out-Null
& "$PSScriptRoot\..\website\install.ps1" `
- -ManifestUrl $manifestUrl `
+ -Channel preview `
+ -ManifestUrl $previewManifestUrl `
-InstallDir $installDir `
-ExpectedBuildId "installer-test"
$repairedHostDir = Get-Item -LiteralPath (Join-Path $installDir "conpty\x64") -Force
@@ -340,7 +385,8 @@ try {
$rejected = $false
try {
& "$PSScriptRoot\..\website\install.ps1" `
- -ManifestUrl $manifestUrl `
+ -Channel preview `
+ -ManifestUrl $previewManifestUrl `
-InstallDir $installDir `
-ExpectedBuildId "different-build"
} catch {
@@ -352,8 +398,67 @@ try {
if (-not $rejected) {
throw "installer accepted a manifest that did not match the updater-selected build"
}
+
+ $stableManifest | Out-File -LiteralPath $stableManifestPath -Encoding utf8
+ & "$PSScriptRoot\..\website\install.ps1" `
+ -Channel stable `
+ -ManifestUrl $stableManifestUrl `
+ -InstallDir $installDir
+ $stableReleaseDir = Get-ChildItem -LiteralPath (Join-Path $herdrHome "packages\standalone\releases") -Directory |
+ Where-Object { $_.Name.StartsWith("0.0.1-") } |
+ Select-Object -First 1
+ if ($null -eq $stableReleaseDir) {
+ throw "installer did not install the stable Windows package"
+ }
+ foreach ($relative in $required) {
+ if (-not (Test-Path -LiteralPath (Join-Path $installDir $relative) -PathType Leaf)) {
+ throw "stable installer did not activate required file $relative"
+ }
+ }
+
+ $customPreviewManifestPath = Join-Path $webRoot "candidate.json"
+ $customPreviewManifest = $previewManifest | ConvertFrom-Json
+ $customPreviewManifest.PSObject.Properties.Remove("channel")
+ $customPreviewManifest | ConvertTo-Json -Depth 5 | Out-File -LiteralPath $customPreviewManifestPath -Encoding utf8
+ $fakeBin = Join-Path $root "fake-existing"
+ New-Item -ItemType Directory -Force -Path $fakeBin | Out-Null
+ @'
+@echo off
+if "%1"=="channel" if "%2"=="show" (
+ echo preview
+ exit /b 0
+)
+exit /b 1
+'@ | Out-File -LiteralPath (Join-Path $fakeBin "herdr.cmd") -Encoding ascii
+
+ $preserveHome = Join-Path $root "preserve-home"
+ $preserveBin = Join-Path $root "preserve-bin"
+ $env:HERDR_HOME = $preserveHome
+ $env:Path = "$fakeBin;$oldProcessPath"
+ & "$PSScriptRoot\..\website\install.ps1" `
+ -ManifestUrl "http://127.0.0.1:$port/candidate.json" `
+ -InstallDir $preserveBin `
+ -ExpectedBuildId "installer-test"
+ $preservedPreview = Get-ChildItem -LiteralPath (Join-Path $preserveHome "packages\standalone\releases") -Directory |
+ Where-Object { $_.Name.StartsWith("0.0.0-preview.installer-test-") } |
+ Select-Object -First 1
+ if ($null -eq $preservedPreview) {
+ throw "installer did not preserve the existing preview channel"
+ }
+
+ & "$PSScriptRoot\..\website\install.ps1" `
+ -Channel stable `
+ -ManifestUrl $stableManifestUrl `
+ -InstallDir $preserveBin
+ $explicitStable = Get-ChildItem -LiteralPath (Join-Path $preserveHome "packages\standalone\releases") -Directory |
+ Where-Object { $_.Name.StartsWith("0.0.1-") } |
+ Select-Object -First 1
+ if ($null -eq $explicitStable) {
+ throw "explicit stable channel did not override the existing preview channel"
+ }
} finally {
$env:HERDR_HOME = $oldHerdrHome
+ $env:Path = $oldProcessPath
if ($null -ne $server -and -not $server.HasExited) {
Stop-Process -Id $server.Id -Force -ErrorAction SilentlyContinue
}