Skip to content

Commit b024ad4

Browse files
authored
Merge pull request #1409 from nvm-windows/feature-firewall
feat(build): GHA prerelease stamp + community trust firewall
2 parents 63b2612 + 99249b0 commit b024ad4

8 files changed

Lines changed: 155 additions & 76 deletions

File tree

‎.github/workflows/release.yml‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ on:
2222
required: false
2323
default: false
2424
type: boolean
25-
hotfix:
26-
description: Optional hotfix stamp (e.g. 1 or hotfix.1 → {manifest}-hotfix.1). Leave empty for manifest version.
25+
prerelease:
26+
description: Optional prerelease stamp appended to manifest (e.g. beta.1 → 2.0.1-beta.1; 1 or hotfix.1 → …-hotfix.1). Leave empty for manifest version.
2727
required: false
2828
default: ""
2929
type: string
@@ -118,7 +118,7 @@ jobs:
118118
PUBLISH_RELEASE: ${{ inputs.publish_release }}
119119
OVERRIDE_EXISTING_RELEASE: ${{ inputs.override_existing_release }}
120120
ARCHES: ${{ steps.matrix.outputs.arches }}
121-
HOTFIX: ${{ inputs.hotfix }}
121+
HOTFIX: ${{ inputs.prerelease }}
122122
run: |
123123
. .\build\common.ps1
124124
$null = Initialize-NvmBuildContext
@@ -130,10 +130,10 @@ jobs:
130130
}
131131
$effective = Resolve-NvmHotfixVersion -BaseVersion $baseVersion -Hotfix $env:HOTFIX
132132
if ($effective -ne $baseVersion) {
133-
Write-Host ("Hotfix stamp -> {0} (base {1})" -f $effective, $baseVersion)
133+
Write-Host ("Prerelease stamp -> {0} (base {1})" -f $effective, $baseVersion)
134134
}
135135
else {
136-
Write-Host ("Release version -> {0} (no hotfix input)" -f $effective)
136+
Write-Host ("Release version -> {0} (no prerelease input)" -f $effective)
137137
}
138138
[Environment]::SetEnvironmentVariable("NVM_CLI_VERSION", $effective, "Process")
139139
if (-not [string]::IsNullOrWhiteSpace($env:GITHUB_ENV)) {
@@ -241,7 +241,7 @@ jobs:
241241
throw "git submodule update failed with exit code $LASTEXITCODE"
242242
}
243243
244-
- name: Stamp CLI manifest version (hotfix / gate)
244+
- name: Stamp CLI manifest version (prerelease / gate)
245245
env:
246246
EFFECTIVE_VERSION: ${{ needs.prepare.outputs.version }}
247247
run: |
@@ -253,7 +253,7 @@ jobs:
253253
$base = (Get-Content -LiteralPath (Get-NvmCliManifestPath) -Raw -Encoding UTF8 | ConvertFrom-Json).version
254254
Set-NvmCliManifestVersion -Version $env:EFFECTIVE_VERSION
255255
if ($env:EFFECTIVE_VERSION -ne [string]$base) {
256-
Write-Host ("Hotfix stamp -> {0} (base {1})" -f $env:EFFECTIVE_VERSION, $base)
256+
Write-Host ("Prerelease stamp -> {0} (base {1})" -f $env:EFFECTIVE_VERSION, $base)
257257
}
258258
259259
- name: Set up Go

‎build/README.md‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ Requires: Go (see `cli/src/go.mod`), [qgo](https://github.com/quikdev/go), Zig (
1818
.\build\main.ps1 -Architecture amd64
1919
.\build\main.ps1 -Architecture arm64 -SkipInstaller
2020
.\build\main.ps1 -Component Cli
21+
.\build\main.ps1 -Hotfix beta.1
22+
.\build\main.ps1 -Hotfix 2
23+
.\build\main.ps1 -Version 2.0.1-hotfix.2
2124
2225
# Public clone (no sync source): download prebuilt sync.exe from the GitHub Release
2326
.\build\main.ps1 -DownloadSync
@@ -29,6 +32,8 @@ Requires: Go (see `cli/src/go.mod`), [qgo](https://github.com/quikdev/go), Zig (
2932

3033
| Flag | Purpose |
3134
|------|---------|
35+
| `-Hotfix` | Same as GHA `prerelease`: any stamp → `{manifest}-{stamp}` (e.g. `beta.1` → `2.0.1-beta.1`). Bare digit `1` → `-hotfix.1` (WiX/Inno revision). Temp-stamps `cli/src/manifest.json` for qgo embed, sets process `NVM_CLI_VERSION`, restores both after build (git stays clean). Mutually exclusive with `-Version`. |
36+
| `-Version` | Full special version override (e.g. `2.0.1-beta.1`). Same temp stamp/restore as `-Hotfix`. Mutually exclusive with `-Hotfix`. |
3237
| `-DownloadSync` | Fetch `nvm---sync.exe` from GitHub Releases instead of compiling sync |
3338
| `-SyncReleaseTag` | Override release tag (default: `v` + `cli/src/manifest.json` version) |
3439
| `-SyncReleaseRepo` | Override `owner/repo` (default: `nvm-windows/nvm`) |
@@ -51,14 +56,14 @@ Workflow: [Release Community Build](../.github/workflows/release.yml) (`workflow
5156
| `architecture` | `both` | `amd64`, `arm64`, or both |
5257
| `publish_release` | true | Draft → upload assets → publish |
5358
| `override_existing_release` | false | Replace setup.exe **and** sync.exe on existing tag |
54-
| `hotfix` | _(empty)_ | Optional stamp: `1` or `hotfix.1` → `{manifest}-hotfix.N` without committing `cli/src/manifest.json` |
59+
| `prerelease` | _(empty)_ | Optional stamp: `beta.1` → `{manifest}-beta.1`; bare `1` → `{manifest}-hotfix.1`. Leave empty for manifest version. |
5560

5661
GitHub Release assets per architecture:
5762

5863
- `nvm---setup.exe` — Inno Setup installer
5964
- `nvm---sync.exe` — prebuilt sync for `-DownloadSync`
6065

61-
Tag = `v` + effective version (`cli/src/manifest.json` version, plus optional `hotfix` stamp). Runner patches manifest before CLI/Inno build so embeds and `AppVersion` match. Inno `VersionInfoVersion` already maps `-hotfix.N` → fourth numeric field (`2.0.1-hotfix.1` → `2.0.1.1`). Hotfix stamps mark the GitHub Release as **`--prerelease`**.
66+
Tag = `v` + effective version (`cli/src/manifest.json` version, plus optional `prerelease` stamp). Runner patches manifest before CLI/Inno build so embeds and `AppVersion` match. Inno `VersionInfoVersion` maps `-hotfix.N` → fourth numeric field (`2.0.1-hotfix.1` → `2.0.1.1`). Any stamped `x.y.z-*` release is marked GitHub **`--prerelease`**.
6267

6368
### WinGet
6469

‎build/common.ps1‎

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,30 +64,43 @@ function Resolve-NvmHotfixVersion {
6464
param(
6565
[Parameter(Mandatory = $true)]
6666
[string]$BaseVersion,
67+
# Optional prerelease stamp (workflow input name may be prerelease/hotfix).
68+
[Alias("Prerelease")]
6769
[string]$Hotfix = ""
6870
)
6971

7072
$base = $BaseVersion.Trim()
7173
if ([string]::IsNullOrWhiteSpace($base)) {
7274
throw "Resolve-NvmHotfixVersion: BaseVersion is empty"
7375
}
76+
if ($base -notmatch '^\d+\.\d+\.\d+$') {
77+
throw ("Resolve-NvmHotfixVersion: base version '{0}' must be major.minor.patch (no prerelease). Stamp via workflow input instead." -f $base)
78+
}
7479

7580
$raw = if ($null -eq $Hotfix) { "" } else { $Hotfix.Trim() }
7681
if ([string]::IsNullOrWhiteSpace($raw)) {
7782
return $base
7883
}
7984

85+
# Allow accidental "-beta.1" / "vbeta.1" from copy-paste.
86+
$raw = $raw -replace '^[vV-]+', ''
87+
if ([string]::IsNullOrWhiteSpace($raw)) {
88+
return $base
89+
}
90+
8091
$suffix = $null
8192
if ($raw -match '^\d+$') {
93+
# Bare digit keeps historical hotfix.N (WiX/Inno revision mapping).
8294
$suffix = "hotfix.$raw"
8395
}
84-
elseif ($raw -match '^(?i)hotfix\.(\d+)$') {
85-
$suffix = "hotfix.$($Matches[1])"
96+
elseif ($raw -match '^[A-Za-z0-9][A-Za-z0-9.-]*$') {
97+
# Any semver-ish prerelease id: beta.1, hotfix.1, rc.1, preview.3, …
98+
$suffix = $raw
8699
}
87100
else {
88101
throw @"
89-
Resolve-NvmHotfixVersion: invalid hotfix input '$raw'.
90-
Use empty (no override), a digit run (e.g. 1 -> -hotfix.1), or hotfix.N (e.g. hotfix.1).
102+
Resolve-NvmHotfixVersion: invalid prerelease stamp '$raw'.
103+
Use empty (manifest version), a digit (1 -> -hotfix.1), or a stamp like beta.1 / hotfix.1 / rc.1.
91104
"@
92105
}
93106

‎build/main.ps1‎

Lines changed: 117 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ param(
77
[switch]$SkipInstaller,
88
[switch]$DownloadSync,
99
[string]$SyncReleaseTag = "",
10-
[string]$SyncReleaseRepo = "nvm-windows/nvm"
10+
[string]$SyncReleaseRepo = "nvm-windows/nvm",
11+
[string]$Hotfix = "",
12+
[string]$Version = ""
1113
)
1214

1315
$ErrorActionPreference = "Stop"
@@ -19,73 +21,132 @@ if ([string]::IsNullOrWhiteSpace($Architecture)) {
1921
$Architecture = Resolve-NvmHostArchitecture
2022
}
2123

22-
$ctx = Initialize-NvmBuildContext -BinRoot $BinRoot
23-
$stepRoot = Join-Path $PSScriptRoot "steps"
24-
$commonArgs = @{
25-
Architecture = $Architecture
26-
BinRoot = $ctx.BinRoot
24+
# Read base from on-disk manifest only (ignore leftover NVM_CLI_VERSION).
25+
$manifestPath = Get-NvmCliManifestPath
26+
if (-not (Test-Path -LiteralPath $manifestPath -PathType Leaf)) {
27+
throw "CLI manifest not found: $manifestPath"
2728
}
28-
$syncArgs = @{
29-
Architecture = $Architecture
30-
BinRoot = $ctx.BinRoot
31-
SyncReleaseTag = $SyncReleaseTag
32-
SyncReleaseRepo = $SyncReleaseRepo
29+
$base = [string](Get-Content -LiteralPath $manifestPath -Raw -Encoding UTF8 | ConvertFrom-Json).version
30+
if ([string]::IsNullOrWhiteSpace($base)) {
31+
throw "CLI manifest does not define version: $manifestPath"
3332
}
34-
if ($DownloadSync) {
35-
$syncArgs["DownloadSync"] = $true
33+
34+
$hotfixSet = -not [string]::IsNullOrWhiteSpace($Hotfix)
35+
$versionSet = -not [string]::IsNullOrWhiteSpace($Version)
36+
if ($hotfixSet -and $versionSet) {
37+
throw "Hotfix and Version are mutually exclusive; set only one."
3638
}
3739

38-
Write-Host "Community build"
39-
Write-Host " RepoRoot -> $($ctx.RepoRoot)"
40-
Write-Host " BinRoot -> $($ctx.BinRoot)"
41-
Write-Host " Architecture -> $Architecture"
42-
Write-Host " Component -> $Component"
43-
Write-Host " SkipInstaller -> $SkipInstaller"
44-
Write-Host " DownloadSync -> $DownloadSync"
45-
if ($DownloadSync) {
46-
Write-Host " SyncReleaseTag -> $(if ([string]::IsNullOrWhiteSpace($SyncReleaseTag)) { '(from CLI manifest)' } else { $SyncReleaseTag })"
47-
Write-Host " SyncReleaseRepo -> $SyncReleaseRepo"
40+
if ($versionSet) {
41+
$effective = $Version.Trim()
42+
}
43+
elseif ($hotfixSet) {
44+
$effective = Resolve-NvmHotfixVersion -BaseVersion $base -Hotfix $Hotfix
45+
}
46+
else {
47+
$effective = $base
4848
}
49-
Write-Host " CLI version -> $($ctx.CliVersion)"
50-
Write-Host " Signing -> Authenticode via Artifact Signing (exes + NVMWindows.Events.dll)"
5149

52-
switch ($Component) {
53-
"All" {
54-
& (Join-Path $stepRoot "Build-Cli.ps1") @commonArgs
55-
& (Join-Path $stepRoot "Build-EventProvider.ps1") @commonArgs
56-
& (Join-Path $stepRoot "Build-Shims.ps1") @commonArgs
57-
& (Join-Path $stepRoot "Build-Sync.ps1") @syncArgs
50+
$priorEnvVersion = [Environment]::GetEnvironmentVariable("NVM_CLI_VERSION")
51+
$manifestStamped = $false
52+
if ($effective -ne $base) {
53+
Set-NvmCliManifestVersion -Version $effective
54+
$manifestStamped = $true
55+
if ($versionSet) {
56+
Write-Host ("Version override -> {0} (base {1})" -f $effective, $base)
5857
}
59-
"Cli" {
60-
& (Join-Path $stepRoot "Build-Cli.ps1") @commonArgs
61-
& (Join-Path $stepRoot "Build-EventProvider.ps1") @commonArgs
58+
else {
59+
Write-Host ("Prerelease stamp -> {0} (base {1})" -f $effective, $base)
6260
}
63-
"Shims" {
64-
& (Join-Path $stepRoot "Build-Shims.ps1") @commonArgs
61+
}
62+
$env:NVM_CLI_VERSION = $effective
63+
64+
try {
65+
$ctx = Initialize-NvmBuildContext -BinRoot $BinRoot
66+
$stepRoot = Join-Path $PSScriptRoot "steps"
67+
$commonArgs = @{
68+
Architecture = $Architecture
69+
BinRoot = $ctx.BinRoot
6570
}
66-
"Sync" {
67-
& (Join-Path $stepRoot "Build-Sync.ps1") @syncArgs
71+
$syncArgs = @{
72+
Architecture = $Architecture
73+
BinRoot = $ctx.BinRoot
74+
SyncReleaseTag = $SyncReleaseTag
75+
SyncReleaseRepo = $SyncReleaseRepo
76+
}
77+
if ($DownloadSync) {
78+
$syncArgs["DownloadSync"] = $true
6879
}
69-
}
7080

71-
$expected = Get-NvmExpectedExePaths -BinRoot $ctx.BinRoot -Component $Component
72-
Write-NvmPayloadSummary -Paths $expected -Title "Executables" -DisplayRoot $ctx.BinRoot -SkipJobSummary
81+
Write-Host "Community build"
82+
Write-Host " RepoRoot -> $($ctx.RepoRoot)"
83+
Write-Host " BinRoot -> $($ctx.BinRoot)"
84+
Write-Host " Architecture -> $Architecture"
85+
Write-Host " Component -> $Component"
86+
Write-Host " SkipInstaller -> $SkipInstaller"
87+
Write-Host " DownloadSync -> $DownloadSync"
88+
if ($DownloadSync) {
89+
Write-Host " SyncReleaseTag -> $(if ([string]::IsNullOrWhiteSpace($SyncReleaseTag)) { '(from CLI manifest)' } else { $SyncReleaseTag })"
90+
Write-Host " SyncReleaseRepo -> $SyncReleaseRepo"
91+
}
92+
if ($hotfixSet) {
93+
Write-Host " Prerelease -> $Hotfix"
94+
}
95+
if ($versionSet) {
96+
Write-Host " Version -> $Version"
97+
}
98+
Write-Host " CLI version -> $($ctx.CliVersion)"
99+
Write-Host " Signing -> Authenticode via Artifact Signing (exes + NVMWindows.Events.dll)"
73100

74-
if ($Component -eq "All" -or $Component -eq "Cli") {
75-
$eventAssets = Get-NvmExpectedEventProviderPaths -BinRoot $ctx.BinRoot
76-
Write-NvmPayloadSummary -Paths $eventAssets -Title "Event provider" -DisplayRoot $ctx.BinRoot -SkipJobSummary
77-
}
101+
switch ($Component) {
102+
"All" {
103+
& (Join-Path $stepRoot "Build-Cli.ps1") @commonArgs
104+
& (Join-Path $stepRoot "Build-EventProvider.ps1") @commonArgs
105+
& (Join-Path $stepRoot "Build-Shims.ps1") @commonArgs
106+
& (Join-Path $stepRoot "Build-Sync.ps1") @syncArgs
107+
}
108+
"Cli" {
109+
& (Join-Path $stepRoot "Build-Cli.ps1") @commonArgs
110+
& (Join-Path $stepRoot "Build-EventProvider.ps1") @commonArgs
111+
}
112+
"Shims" {
113+
& (Join-Path $stepRoot "Build-Shims.ps1") @commonArgs
114+
}
115+
"Sync" {
116+
& (Join-Path $stepRoot "Build-Sync.ps1") @syncArgs
117+
}
118+
}
78119

79-
if ($Component -ne "All") {
80-
Write-Host "Skipping Inno Setup (requires -Component All; got $Component)." -ForegroundColor Yellow
81-
}
82-
elseif ($SkipInstaller) {
83-
Write-Host "Skipping Inno Setup (-SkipInstaller)." -ForegroundColor Yellow
120+
$expected = Get-NvmExpectedExePaths -BinRoot $ctx.BinRoot -Component $Component
121+
Write-NvmPayloadSummary -Paths $expected -Title "Executables" -DisplayRoot $ctx.BinRoot -SkipJobSummary
122+
123+
if ($Component -eq "All" -or $Component -eq "Cli") {
124+
$eventAssets = Get-NvmExpectedEventProviderPaths -BinRoot $ctx.BinRoot
125+
Write-NvmPayloadSummary -Paths $eventAssets -Title "Event provider" -DisplayRoot $ctx.BinRoot -SkipJobSummary
126+
}
127+
128+
if ($Component -ne "All") {
129+
Write-Host "Skipping Inno Setup (requires -Component All; got $Component)." -ForegroundColor Yellow
130+
}
131+
elseif ($SkipInstaller) {
132+
Write-Host "Skipping Inno Setup (-SkipInstaller)." -ForegroundColor Yellow
133+
}
134+
else {
135+
& (Join-Path $stepRoot "Build-Installer.ps1") @commonArgs
136+
$setup = Get-NvmInstallerSetupPath -Version $ctx.CliVersion -Architecture $Architecture -DistRoot $ctx.DistRoot
137+
Write-NvmPayloadSummary -Paths @($setup) -Title "Installer" -DisplayRoot $ctx.RepoRoot
138+
}
139+
140+
Write-Host "Build complete ($Component)."
84141
}
85-
else {
86-
& (Join-Path $stepRoot "Build-Installer.ps1") @commonArgs
87-
$setup = Get-NvmInstallerSetupPath -Version $ctx.CliVersion -Architecture $Architecture -DistRoot $ctx.DistRoot
88-
Write-NvmPayloadSummary -Paths @($setup) -Title "Installer" -DisplayRoot $ctx.RepoRoot
142+
finally {
143+
if ($manifestStamped) {
144+
Set-NvmCliManifestVersion -Version $base
145+
}
146+
if ($null -eq $priorEnvVersion -or $priorEnvVersion -eq "") {
147+
Remove-Item Env:NVM_CLI_VERSION -ErrorAction SilentlyContinue
148+
}
149+
else {
150+
$env:NVM_CLI_VERSION = $priorEnvVersion
151+
}
89152
}
90-
91-
Write-Host "Build complete ($Component)."

‎build/steps/Publish-GitHubRelease.ps1‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -621,8 +621,8 @@ Org Program Files / MSI / Intune: use NVM for Windows Certified Builds.
621621
622622
Assets per arch: Inno Setup installer (``*-setup.exe``) and prebuilt ``sync.exe`` (``*-sync.exe``) for public ``-DownloadSync`` builds.
623623
"@
624-
if ($Version -match '(?i)-hotfix\.\d+') {
625-
$intro += "`n`nHotfix stamp applied at build time via the Community Release ``hotfix`` workflow input (manifest base left unchanged in git)."
624+
if ($Version -match '^\d+\.\d+\.\d+-') {
625+
$intro += "`n`nPrerelease stamp applied at build time via the Community Release ``prerelease`` workflow input (manifest base left unchanged in git)."
626626
}
627627
$commits = Get-NvmReleaseCommitSummarySection -Tag $Tag -HeadRef $headRef
628628
$newContributors = Get-NvmReleaseNewContributorsSection -Tag $Tag -HeadRef $headRef
@@ -675,8 +675,8 @@ function New-NvmDraftRelease {
675675
"--title", $Tag,
676676
"--notes-file", $notesFile
677677
)
678-
# hotfix stamps are intentional GitHub prereleases (validation drops, not "latest").
679-
if ($Version -match '(?i)(alpha|beta|rc|preview|pre|hotfix)') {
678+
# Any stamped semver (x.y.z-*) is a GitHub prerelease (validation drops, not "latest").
679+
if ($Version -match '^\d+\.\d+\.\d+-') {
680680
$createArgs += "--prerelease"
681681
}
682682

‎shim‎

0 commit comments

Comments
 (0)