@rightkit/release 0.2.105 → 0.2.106
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/addon-command.mjs +1 -1
- package/background-process.mjs +33 -0
- package/build-release.mjs +47 -6
- package/cache-command.mjs +2 -2
- package/cache-policy.mjs +1 -1
- package/cargo-contract.mjs +230 -230
- package/cargo-guard.mjs +4 -4
- package/cargo-target.mjs +53 -48
- package/cli/right-release.mjs +3 -2
- package/create-mac-updater.mjs +1 -1
- package/deps.mjs +1 -1
- package/direct-bootstrap.mjs +84 -12
- package/github-release.mjs +1 -1
- package/heavy-command.mjs +1 -1
- package/macos-signing-identity.mjs +13 -0
- package/managed-cargo.mjs +29 -0
- package/mirror-root-artifact.mjs +1 -1
- package/model-promote.mjs +1 -1
- package/notarize-macos.mjs +1 -1
- package/nsis-payload.mjs +1 -1
- package/package.json +2 -2
- package/preflight.mjs +1 -1
- package/process-liveness.mjs +1 -1
- package/progress-control.mjs +1 -1
- package/pub-upload.mjs +1 -1
- package/publish-cargo.mjs +1 -1
- package/publish-swift.mjs +1 -1
- package/publish-update.mjs +1 -1
- package/registry-parity.mjs +1 -1
- package/release-invocation.mjs +1 -1
- package/release-state.mjs +2 -1
- package/release-token.mjs +1 -1
- package/release.mjs +18 -11
- package/rightkit-versions.json +141 -140
- package/sign-macos.mjs +4 -3
- package/sign-release-manifest.mjs +7 -5
- package/sign-updater.mjs +1 -1
- package/sign-windows.mjs +3 -15
- package/signing-env.mjs +29 -0
- package/standalone-clone-verify.mjs +1 -1
- package/upload-large.mjs +1 -1
- package/upload-release.mjs +1 -1
package/deps.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { pathToFileURL } from "node:url";
|
|
4
|
-
import { spawnSync } from "
|
|
4
|
+
import { spawnSync } from "./background-process.mjs";
|
|
5
5
|
import { existsSync } from "node:fs";
|
|
6
6
|
|
|
7
7
|
const args = process.argv.slice(2);
|
package/direct-bootstrap.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { mkdirSync, readFileSync, realpathSync, statSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
4
|
-
import { spawnSync } from "
|
|
4
|
+
import { spawnSync } from "./background-process.mjs";
|
|
5
5
|
import { RIGHTRELEASE_MANIFEST_SIGNER, signReleaseManifestWithAzure } from "./sign-release-manifest.mjs";
|
|
6
6
|
import { validateCycloneDxSbom, validateInTotoSlsaProvenance } from "./supply-chain-evidence.mjs";
|
|
7
7
|
|
|
@@ -222,11 +222,13 @@ export function renderPowerShellBootstrap({
|
|
|
222
222
|
expectedStatus = "complete",
|
|
223
223
|
healthAssertions = null,
|
|
224
224
|
preflightArgs = [],
|
|
225
|
+
priorExecutableMinVersion = null,
|
|
225
226
|
allowLocalReleaseRoot = false,
|
|
226
227
|
}) {
|
|
227
228
|
if (!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(repository)) throw new Error("invalid GitHub repository");
|
|
228
229
|
if (!VERSION.test(bootstrapVersion)) throw new Error("invalid bootstrap version");
|
|
229
230
|
if (typeof allowLocalReleaseRoot !== "boolean") throw new Error("allowLocalReleaseRoot must be boolean");
|
|
231
|
+
if (priorExecutableMinVersion !== null && !VERSION.test(priorExecutableMinVersion)) throw new Error("priorExecutableMinVersion must be a semantic version");
|
|
230
232
|
assertRelativeFile(executablePath, "executable path");
|
|
231
233
|
if (!Array.isArray(acceptedManifestSigners) || !acceptedManifestSigners.length) throw new Error("accepted manifest signers are required");
|
|
232
234
|
const signers = {};
|
|
@@ -265,6 +267,7 @@ export function renderPowerShellBootstrap({
|
|
|
265
267
|
statusArgs,
|
|
266
268
|
healthAssertions: assertions,
|
|
267
269
|
preflightArgs,
|
|
270
|
+
...(priorExecutableMinVersion ? { priorExecutableMinVersion } : {}),
|
|
268
271
|
...(allowLocalReleaseRoot ? { allowLocalReleaseRoot: true } : {}),
|
|
269
272
|
};
|
|
270
273
|
// Bind generated contract metadata so a caller cannot pass a marker plus an
|
|
@@ -293,6 +296,55 @@ export function renderPowerShellBootstrap({
|
|
|
293
296
|
"function Remove-StableJunction([string]$Path) { $Item = Get-Item -LiteralPath $Path -Force -ErrorAction SilentlyContinue; if ($null -eq $Item) { return }; if ($Item.LinkType -ne 'Junction') { throw ('Stable pointer path is not a junction: ' + $Path) }; [IO.Directory]::Delete($Path) }",
|
|
294
297
|
"function Resolve-HealthPath($Value, [string]$JsonPath) { $CurrentValue = $Value; foreach ($Segment in $JsonPath.Split('.')) { if ($Segment -match '^\\d+$') { $Index = [int]$Segment; if ($null -eq $CurrentValue -or $Index -ge @($CurrentValue).Count) { throw ('Health path is missing: ' + $JsonPath) }; $CurrentValue = @($CurrentValue)[$Index] } else { $Property = $CurrentValue.PSObject.Properties[$Segment]; if ($null -eq $Property) { throw ('Health path is missing: ' + $JsonPath) }; $CurrentValue = $Property.Value } }; return $CurrentValue }",
|
|
295
298
|
"function Assert-Health($Health, [string]$ActiveVersion) { foreach ($Assertion in $Contract.healthAssertions) { $Actual = Resolve-HealthPath $Health ([string]$Assertion.path); if ($Assertion.PSObject.Properties.Name -contains 'equals') { $Expected = $Assertion.equals; if ($Expected -is [string]) { $Expected = $Expected.Replace('{version}', $ActiveVersion).Replace('{current}', $Current).Replace('{installRoot}', $InstallRoot) }; if ($Actual -ne $Expected) { throw ('Health assertion failed: ' + $Assertion.path) } } elseif ($Assertion.nonempty -eq $true) { if ($null -eq $Actual -or ([string]$Actual).Trim().Length -eq 0) { throw ('Health assertion failed: ' + $Assertion.path) } } elseif (@($Actual).Count -lt [int]$Assertion.minCount) { throw ('Health assertion failed: ' + $Assertion.path) } } }",
|
|
299
|
+
"function Resolve-PriorVersion($PriorExePath, $PriorLeaf) { if ($PriorExePath -and (Test-Path -LiteralPath $PriorExePath -PathType Leaf)) { $Info = (Get-Item -LiteralPath $PriorExePath).VersionInfo; $Candidate = [string]$Info.ProductVersion; if ([string]::IsNullOrWhiteSpace($Candidate)) { $Candidate = [string]$Info.FileVersion }; $Match = [regex]::Match($Candidate, '(\\d+\\.\\d+\\.\\d+)'); if ($Match.Success) { return $Match.Groups[1].Value } }; if ($PriorLeaf) { $LeafMatch = [regex]::Match([string]$PriorLeaf, '^(\\d+\\.\\d+\\.\\d+)(?:-|$)'); if ($LeafMatch.Success) { return $LeafMatch.Groups[1].Value } }; return $null }",
|
|
300
|
+
// Every executable the installer invokes (prior probes, activation,
|
|
301
|
+
// health) runs through one bounded launcher: a hung or TUI-launching
|
|
302
|
+
// legacy binary must never stall installation or rollback forever.
|
|
303
|
+
String.raw`function ConvertTo-NativeArgument([AllowEmptyString()][string]$Value) {
|
|
304
|
+
# Start-Process joins ArgumentList with spaces; quote each argv element for
|
|
305
|
+
# the Windows native command-line parser, including empty/trailing-slash args.
|
|
306
|
+
return ('"' + [regex]::Replace([regex]::Replace($Value, '(\\*)"', '$1$1\"'), '(\\+)$', '$1$1') + '"')
|
|
307
|
+
}
|
|
308
|
+
function Invoke-BoundedExe($Path, [string[]]$Arguments, [int]$TimeoutSeconds) {
|
|
309
|
+
$Out = Join-Path ([IO.Path]::GetTempPath()) ('rkexe-' + [guid]::NewGuid() + '.out'); $Err = "$Out.err"
|
|
310
|
+
$Proc = $null; $ExitCode = $null; $Failure = $null; $TimedOut = $false; $Stdout = ''; $Stderr = ''
|
|
311
|
+
try {
|
|
312
|
+
$StartOptions = @{ FilePath=$Path; NoNewWindow=$true; PassThru=$true; RedirectStandardOutput=$Out; RedirectStandardError=$Err }
|
|
313
|
+
if ($Arguments.Count) { $StartOptions.ArgumentList = (($Arguments | ForEach-Object { ConvertTo-NativeArgument $_ }) -join ' ') }
|
|
314
|
+
$Proc = Start-Process @StartOptions
|
|
315
|
+
# Windows PowerShell 5.1 must acquire the handle before waiting; otherwise
|
|
316
|
+
# ExitCode can remain null even after a successful native process exits.
|
|
317
|
+
$NativeProcessHandle = $Proc.Handle
|
|
318
|
+
if (-not $Proc.WaitForExit($TimeoutSeconds * 1000)) {
|
|
319
|
+
$TimedOut = $true
|
|
320
|
+
try { Stop-Process -Id $Proc.Id -Force -ErrorAction Stop; $null = $Proc.WaitForExit(2000) } catch {}
|
|
321
|
+
$Failure = 'Timed out after ' + $TimeoutSeconds + 's: ' + $Path
|
|
322
|
+
} else {
|
|
323
|
+
$ExitCode = $Proc.ExitCode
|
|
324
|
+
if ($null -eq $ExitCode) { $Failure = 'Native exit code unavailable: ' + $Path }
|
|
325
|
+
elseif ($ExitCode -ne 0) { $Failure = 'Command failed (exit ' + $ExitCode + '): ' + $Path }
|
|
326
|
+
}
|
|
327
|
+
} catch { $Failure = $_.Exception.Message }
|
|
328
|
+
finally {
|
|
329
|
+
try {
|
|
330
|
+
if (Test-Path -LiteralPath $Out) { $Stdout = [IO.File]::ReadAllText($Out) }
|
|
331
|
+
if (Test-Path -LiteralPath $Err) { $Stderr = [IO.File]::ReadAllText($Err) }
|
|
332
|
+
} finally {
|
|
333
|
+
if ($null -ne $Proc) { $Proc.Dispose() }
|
|
334
|
+
Remove-Item -LiteralPath $Out,$Err -Force -ErrorAction SilentlyContinue
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
$Global:LASTEXITCODE = $ExitCode
|
|
338
|
+
if ($Failure) {
|
|
339
|
+
$Newline = [Environment]::NewLine
|
|
340
|
+
$Exception = New-Object System.Exception ($Failure + $Newline + 'stdout:' + $Newline + $Stdout + $Newline + 'stderr:' + $Newline + $Stderr)
|
|
341
|
+
$Exception.Data['Path'] = $Path; $Exception.Data['Arguments'] = $Arguments
|
|
342
|
+
$Exception.Data['ExitCode'] = $ExitCode; $Exception.Data['TimedOut'] = $TimedOut
|
|
343
|
+
$Exception.Data['Stdout'] = $Stdout; $Exception.Data['Stderr'] = $Stderr
|
|
344
|
+
throw $Exception
|
|
345
|
+
}
|
|
346
|
+
return $Stdout
|
|
347
|
+
}`,
|
|
296
348
|
...(allowLocalReleaseRoot ? [
|
|
297
349
|
"New-Item -ItemType Directory -Force -Path $VersionsRoot | Out-Null",
|
|
298
350
|
"$LocalReleaseRoot = $null; if ($ReleaseRoot) { $LocalReleaseRoot = [IO.Path]::GetFullPath($ReleaseRoot); if (-not (Test-Path -LiteralPath $LocalReleaseRoot -PathType Container)) { throw 'Local release root is missing' } }",
|
|
@@ -310,7 +362,7 @@ export function renderPowerShellBootstrap({
|
|
|
310
362
|
"if (-not $Version) { $Version = ((Invoke-RestMethod " + psQuote("https://api.github.com/repos/" + repository + "/releases/latest") + ").tag_name -replace '^v','') }",
|
|
311
363
|
"if ($Version -notmatch '^(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)$') { throw 'Invalid stable release version' }",
|
|
312
364
|
"$PriorTarget = $null; if (Test-Path $Current) { $PriorTarget = (Get-Item $Current).Target }",
|
|
313
|
-
"$PriorVersion = if ($PriorTarget) { ((Split-Path ([string]$PriorTarget) -Leaf)
|
|
365
|
+
"$PriorVersion = if ($PriorTarget) { Resolve-PriorVersion (Join-Path $Current " + psQuote(executablePath.replaceAll("/", "\\")) + ") (Split-Path ([string]$PriorTarget) -Leaf) } else { $null }",
|
|
314
366
|
"if ($PriorVersion -and -not $AllowDowngrade -and ([version]$Version -lt [version]$PriorVersion)) { throw 'Downgrade requires -AllowDowngrade' }",
|
|
315
367
|
"$ReleaseBase = " + psQuote("https://github.com/" + repository + "/releases/download/v") + " + $Version",
|
|
316
368
|
"$Work = Join-Path ([IO.Path]::GetTempPath()) (" + psQuote(product + "-install-") + " + [guid]::NewGuid())",
|
|
@@ -332,7 +384,7 @@ export function renderPowerShellBootstrap({
|
|
|
332
384
|
"if ($Version -notmatch '^(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)$') { throw 'Invalid stable release version' }",
|
|
333
385
|
"$ReleaseBase = " + psQuote("https://github.com/" + repository + "/releases/download/v") + " + $Version",
|
|
334
386
|
"$PriorTarget = $null; if (Test-Path $Current) { $PriorTarget = (Get-Item $Current).Target }",
|
|
335
|
-
"$PriorVersion = if ($PriorTarget) { ((Split-Path ([string]$PriorTarget) -Leaf)
|
|
387
|
+
"$PriorVersion = if ($PriorTarget) { Resolve-PriorVersion (Join-Path $Current " + psQuote(executablePath.replaceAll("/", "\\")) + ") (Split-Path ([string]$PriorTarget) -Leaf) } else { $null }",
|
|
336
388
|
"if ($PriorVersion -and -not $AllowDowngrade -and ([version]$Version -lt [version]$PriorVersion)) { throw 'Downgrade requires -AllowDowngrade' }",
|
|
337
389
|
] : []),
|
|
338
390
|
"if ($Manifest.signingKeyId -ne $VerifiedKeyId -or $Manifest.signatureAlgorithm -ne 'authenticode-catalog-sha256' -or $Manifest.signatureProvider -ne 'windows-authenticode-catalog' -or $Manifest.signatureProviderVersion -ne 1) { throw 'Release manifest signer identity mismatch' }",
|
|
@@ -367,9 +419,14 @@ export function renderPowerShellBootstrap({
|
|
|
367
419
|
"$StatusArgsTemplate = " + psArray(statusArgs),
|
|
368
420
|
"$PreflightArgsTemplate = " + psArray(preflightArgs),
|
|
369
421
|
"$PreflightArgs = @(Expand-BootstrapArgs $PreflightArgsTemplate $VersionRoot $Version)",
|
|
370
|
-
|
|
422
|
+
// A prior executable predating the installer verb surface (statusArgs /
|
|
423
|
+
// activationArgs) must never be invoked: legacy binaries can fall through
|
|
424
|
+
// to an interactive UI whose startup side effects (services, daemons,
|
|
425
|
+
// resident workers) corrupt the new install's own probes.
|
|
426
|
+
"$PriorCliCapable = $true; if ($Contract.priorExecutableMinVersion) { $PriorCliCapable = ($null -ne $PriorVersion -and ([version]$PriorVersion -ge [version]$Contract.priorExecutableMinVersion)) }",
|
|
427
|
+
"$PriorStatus = $null; if ($PriorTarget -and $PriorCliCapable) { $PriorExe = Join-Path $Current " + psQuote(executablePath.replaceAll("/", "\\")) + "; $PriorStatusArgs = @(Expand-BootstrapArgs $StatusArgsTemplate ([string]$PriorTarget) $PriorVersion); try { $PriorStatus = ((Invoke-BoundedExe $PriorExe $PriorStatusArgs 60) | Out-String).Trim() } catch {} }",
|
|
371
428
|
"@{ schemaVersion=1; priorTarget=$PriorTarget; priorVersion=$PriorVersion; targetVersion=$Version; priorStatus=$PriorStatus } | ConvertTo-Json -Depth 8 | Set-Content -Encoding UTF8 $Journal",
|
|
372
|
-
"if ($PreflightArgs.Count) {
|
|
429
|
+
"if ($PreflightArgs.Count) { Invoke-BoundedExe $VersionExe $PreflightArgs 120 | Out-Null; if ($LASTEXITCODE -ne 0) { throw 'Activation preflight failed' } }",
|
|
373
430
|
"Remove-StableJunction $NextLink; Remove-StableJunction $PreviousLink",
|
|
374
431
|
"New-Item -ItemType Junction -Path $NextLink -Target $VersionRoot | Out-Null",
|
|
375
432
|
"$Switched = $false; $PriorDetached = $false",
|
|
@@ -377,18 +434,32 @@ export function renderPowerShellBootstrap({
|
|
|
377
434
|
" if (Test-Path $Current) { Rename-Item -LiteralPath $Current -NewName (Split-Path -Leaf $PreviousLink); $PriorDetached = $true }; Rename-Item -LiteralPath $NextLink -NewName (Split-Path -Leaf $Current); $Switched = $true",
|
|
378
435
|
" $StableExe = Join-Path $Current " + psQuote(executablePath.replaceAll("/", "\\")),
|
|
379
436
|
" $ActivationArgs = @(Expand-BootstrapArgs $ActivationArgsTemplate $VersionRoot $Version); $StatusArgs = @(Expand-BootstrapArgs $StatusArgsTemplate $VersionRoot $Version)",
|
|
380
|
-
"
|
|
381
|
-
" $HealthText = (
|
|
437
|
+
" Invoke-BoundedExe $StableExe $ActivationArgs 240 | Out-Null; if ($LASTEXITCODE -ne 0) { throw 'Activation failed' }",
|
|
438
|
+
" $HealthText = ((Invoke-BoundedExe $StableExe $StatusArgs 120) | Out-String).Trim(); if ($LASTEXITCODE -ne 0) { throw 'Health command failed' }; $Health = $HealthText | ConvertFrom-Json",
|
|
382
439
|
" Assert-Health $Health $Version",
|
|
383
|
-
" $StableBin = Split-Path $StableExe -Parent; $UserPath = [Environment]::GetEnvironmentVariable('Path','User');
|
|
440
|
+
" $StableBin = Split-Path $StableExe -Parent; $InstallRootResolved = [IO.Path]::GetFullPath($InstallRoot).TrimEnd([IO.Path]::DirectorySeparatorChar); $UserPath = [Environment]::GetEnvironmentVariable('Path','User'); $Kept = @($UserPath -split ';' | Where-Object { $Entry = $_.Trim(); $Entry -ne '' -and -not ($Entry.TrimEnd([IO.Path]::DirectorySeparatorChar).StartsWith(($InstallRootResolved + [IO.Path]::DirectorySeparatorChar), [StringComparison]::OrdinalIgnoreCase) -and -not $Entry.TrimEnd([IO.Path]::DirectorySeparatorChar).Equals($StableBin.TrimEnd([IO.Path]::DirectorySeparatorChar), [StringComparison]::OrdinalIgnoreCase)) }); $NewPath = ((@($StableBin) + $Kept) -join ';'); if ($NewPath -ne $UserPath) { [Environment]::SetEnvironmentVariable('Path', $NewPath, 'User') }",
|
|
384
441
|
// Journal cleanup must succeed before discarding the prior pointer. If
|
|
385
442
|
// cleanup fails, catch still has a rollback anchor to restore.
|
|
386
443
|
" Remove-Item -Force $Journal; Remove-StableJunction $PreviousLink; Write-Output (" + psQuote(product + " ") + " + $Version + ' installed')",
|
|
387
444
|
"} catch {",
|
|
388
|
-
"
|
|
389
|
-
" if ($
|
|
390
|
-
" if (Test-Path $
|
|
391
|
-
|
|
445
|
+
" $InstallError = $_; $RollbackErrors = New-Object 'System.Collections.Generic.List[string]'",
|
|
446
|
+
" try { if ($Switched -or $PriorDetached) { Remove-StableJunction $Current }; if ($PriorDetached -and (Test-Path $PreviousLink)) { Rename-Item -LiteralPath $PreviousLink -NewName (Split-Path -Leaf $Current) } } catch { $RollbackErrors.Add($_.Exception.Message) }",
|
|
447
|
+
" try { if ($PriorTarget -and (Test-Path $Current) -and $PriorCliCapable -and ([string](Get-Item -LiteralPath $Current).Target -eq [string]$PriorTarget)) { $PriorExe = Join-Path $Current " + psQuote(executablePath.replaceAll("/", "\\")) + "; $PriorActivationArgs = @(Expand-BootstrapArgs $ActivationArgsTemplate ([string]$PriorTarget) $PriorVersion); $PriorStatusArgs = @(Expand-BootstrapArgs $StatusArgsTemplate ([string]$PriorTarget) $PriorVersion); Invoke-BoundedExe $PriorExe $PriorActivationArgs 240 | Out-Null; if ($LASTEXITCODE -ne 0) { throw 'Rollback activation failed' }; $PriorHealthText = ((Invoke-BoundedExe $PriorExe $PriorStatusArgs 120) | Out-String).Trim(); if ($LASTEXITCODE -ne 0) { throw 'Rollback health failed' }; $PriorHealth = $PriorHealthText | ConvertFrom-Json; try { Assert-Health $PriorHealth $PriorVersion } catch { throw ('Prior health was not restored: ' + $_.Exception.Message) } } } catch { $RollbackErrors.Add($_.Exception.Message) }",
|
|
448
|
+
String.raw` try {
|
|
449
|
+
$CandidatePath = [IO.Path]::GetFullPath($VersionRoot).TrimEnd([IO.Path]::DirectorySeparatorChar)
|
|
450
|
+
$VersionsPrefix = [IO.Path]::GetFullPath($VersionsRoot).TrimEnd([IO.Path]::DirectorySeparatorChar) + [IO.Path]::DirectorySeparatorChar
|
|
451
|
+
$ActiveTarget = if (Test-Path -LiteralPath $Current) { [string](Get-Item -LiteralPath $Current).Target } else { $null }
|
|
452
|
+
if ($ActiveTarget -and -not [IO.Path]::IsPathRooted($ActiveTarget)) { $ActiveTarget = Join-Path (Split-Path -Parent $Current) $ActiveTarget }
|
|
453
|
+
if (-not $CandidatePath.StartsWith($VersionsPrefix, [StringComparison]::OrdinalIgnoreCase)) { throw 'Failed version cleanup escapes versions root' }
|
|
454
|
+
if ($ActiveTarget -and [IO.Path]::GetFullPath($ActiveTarget).TrimEnd([IO.Path]::DirectorySeparatorChar).Equals($CandidatePath, [StringComparison]::OrdinalIgnoreCase)) { throw 'Failed version is still active; preserved for recovery' }
|
|
455
|
+
if (Test-Path -LiteralPath $CandidatePath) { Remove-Item -LiteralPath $CandidatePath -Recurse -Force }
|
|
456
|
+
} catch { $RollbackErrors.Add($_.Exception.Message) }
|
|
457
|
+
if ($RollbackErrors.Count) {
|
|
458
|
+
$InstallError.Exception.Data['RollbackErrors'] = $RollbackErrors.ToArray()
|
|
459
|
+
Write-Warning ('Rollback/cleanup also failed: ' + ($RollbackErrors -join '; '))
|
|
460
|
+
}
|
|
461
|
+
throw $InstallError
|
|
462
|
+
} finally { try { if (Test-Path -LiteralPath $Work) { Remove-Item -LiteralPath $Work -Recurse -Force } } catch { Write-Warning ('Installer work cleanup failed: ' + $_.Exception.Message) } }`,
|
|
392
463
|
];
|
|
393
464
|
const body = lines.join("\r\n") + "\r\n";
|
|
394
465
|
const sourceSha256 = createHash("sha256").update(body).digest("hex");
|
|
@@ -434,6 +505,7 @@ export function validatePowerShellBootstrap(script, expected = {}) {
|
|
|
434
505
|
statusArgs: contract.statusArgs,
|
|
435
506
|
healthAssertions: contract.healthAssertions,
|
|
436
507
|
preflightArgs: contract.preflightArgs,
|
|
508
|
+
priorExecutableMinVersion: contract.priorExecutableMinVersion ?? null,
|
|
437
509
|
allowLocalReleaseRoot: contract.allowLocalReleaseRoot === true,
|
|
438
510
|
});
|
|
439
511
|
if (regenerated !== script) errors.push("bootstrap source does not match canonical generated output");
|
package/github-release.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { copyFileSync, existsSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
|
-
import { spawnSync } from "
|
|
5
|
+
import { spawnSync } from "./background-process.mjs";
|
|
6
6
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
7
7
|
import { verifySealedRelease } from "./release-state.mjs";
|
|
8
8
|
import { addonManifestSha256, validateAddonConfig, validateAddonManifest } from "./addon-contract.mjs";
|
package/heavy-command.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
|
-
import { spawn, spawnSync } from "
|
|
5
|
+
import { spawn, spawnSync } from "./background-process.mjs";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import { randomUUID } from "node:crypto";
|
|
8
8
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { spawnSync } from "./background-process.mjs";
|
|
2
|
+
|
|
3
|
+
export function resolveMacosDeveloperIdIdentity({ env = process.env, run = spawnSync } = {}) {
|
|
4
|
+
const configured = env.APPLE_DEVELOPER_ID?.trim();
|
|
5
|
+
if (configured) return configured;
|
|
6
|
+
const result = run("security", ["find-identity", "-v", "-p", "codesigning"], { encoding: "utf8" });
|
|
7
|
+
if (result.status !== 0) throw new Error("could not inspect macOS signing identities in Keychain");
|
|
8
|
+
const identities = [...String(result.stdout ?? "").matchAll(/"([^"\n]*Developer ID Application:[^"]+)"/g)]
|
|
9
|
+
.map((match) => match[1]);
|
|
10
|
+
if (identities.length === 1) return identities[0];
|
|
11
|
+
if (!identities.length) throw new Error("no Developer ID Application signing identity found in Keychain");
|
|
12
|
+
throw new Error(`multiple Developer ID Application signing identities found in Keychain; set APPLE_DEVELOPER_ID explicitly (${identities.join(", ")})`);
|
|
13
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { spawnSync } from "./background-process.mjs";
|
|
4
|
+
|
|
5
|
+
export function managedRuntimeClient(env = process.env, platform = process.platform, exists = existsSync) {
|
|
6
|
+
const roots = [env.RIGHTKIT_MANAGED_ROOT, env.RIGHTKIT_EXTERNAL_ROOT,
|
|
7
|
+
platform === "win32" ? "D:\\.rightkit-managed" : platform === "darwin" ? "/Volumes/D/.rightkit-managed" : undefined]
|
|
8
|
+
.filter((root) => typeof root === "string" && root.length > 0);
|
|
9
|
+
for (const root of roots) {
|
|
10
|
+
const client = path.join(root, "runtime-current", "build-control-clients", "src", "rightkit.mjs");
|
|
11
|
+
if (exists(client)) return client;
|
|
12
|
+
}
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function managedHost(env) {
|
|
17
|
+
return Boolean(env.RIGHTKIT_BUILD_BROKER_SOCKET)
|
|
18
|
+
|| /(?:^|[;:])[^;:]*[/\\](?:\.rightkit-managed|rightkitmanagedagent)[/\\]agent-bin(?:[;:]|$)/i.test(String(env.PATH ?? ""));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function runCargoSync(args, options = {}) {
|
|
22
|
+
const env = options.env ?? process.env;
|
|
23
|
+
const client = managedRuntimeClient(env);
|
|
24
|
+
if (client) return spawnSync(process.execPath, [client, "cargo", ...args], { ...options, cwd: process.cwd() });
|
|
25
|
+
if (managedHost(env) && process.platform === "win32") {
|
|
26
|
+
return { status: null, stdout: "", stderr: "", error: new Error("installed RightKit runtime client is unavailable for managed Cargo metadata") };
|
|
27
|
+
}
|
|
28
|
+
return spawnSync("cargo", args, options);
|
|
29
|
+
}
|
package/mirror-root-artifact.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { copyFileSync, mkdirSync, realpathSync, renameSync, rmSync } from "node:fs";
|
|
3
|
-
import { execFileSync } from "
|
|
3
|
+
import { execFileSync } from "./background-process.mjs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
|
|
6
6
|
const args = parseArgs(process.argv.slice(2));
|
package/model-promote.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { createReadStream } from "node:fs";
|
|
|
4
4
|
import { mkdtemp, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
5
5
|
import os from "node:os";
|
|
6
6
|
import path from "node:path";
|
|
7
|
-
import { spawn, spawnSync } from "
|
|
7
|
+
import { spawn, spawnSync } from "./background-process.mjs";
|
|
8
8
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
9
9
|
|
|
10
10
|
import {
|
package/notarize-macos.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
|
-
import { spawnSync } from "
|
|
5
|
+
import { spawnSync } from "./background-process.mjs";
|
|
6
6
|
import { notarytoolAuthArgs } from "./notary-auth.mjs";
|
|
7
7
|
import { MACOS_PORTABLE_SIGNING_CONTRACT } from "./macos-signing-contract.mjs";
|
|
8
8
|
|
package/nsis-payload.mjs
CHANGED
|
@@ -16,7 +16,7 @@ import { createHash } from "node:crypto";
|
|
|
16
16
|
import { existsSync, mkdirSync, readFileSync, rmSync } from "node:fs";
|
|
17
17
|
import os from "node:os";
|
|
18
18
|
import path from "node:path";
|
|
19
|
-
import { spawnSync } from "
|
|
19
|
+
import { spawnSync } from "./background-process.mjs";
|
|
20
20
|
|
|
21
21
|
const SEVEN_ZIP_CANDIDATES = [
|
|
22
22
|
"C:\\Program Files\\7-Zip\\7z.exe",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rightkit/release",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.106",
|
|
4
4
|
"description": "Portable release CLI/SDK with fail-closed public GitHub Releases and private R2 + RightApps distribution.",
|
|
5
5
|
"license": "MIT OR Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -33,4 +33,4 @@
|
|
|
33
33
|
"doctor:all": "node --test right-suite-contract.test.mjs",
|
|
34
34
|
"verify:standalone": "node standalone-clone-verify.mjs"
|
|
35
35
|
}
|
|
36
|
-
}
|
|
36
|
+
}
|
package/preflight.mjs
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
import { existsSync, lstatSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
22
22
|
import path from "node:path";
|
|
23
|
-
import { spawnSync } from "
|
|
23
|
+
import { spawnSync } from "./background-process.mjs";
|
|
24
24
|
import { brokerManagedHost } from "./cargo-contract.mjs";
|
|
25
25
|
|
|
26
26
|
export const PREFLIGHT_OK = "ok";
|
package/process-liveness.mjs
CHANGED
package/progress-control.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { spawn } from "
|
|
1
|
+
import { spawn } from "./background-process.mjs";
|
|
2
2
|
|
|
3
3
|
const MANAGED_REQUEST_PATTERN = /\brightkit(?: managed-agent)?: request ([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\b/gi;
|
|
4
4
|
export const RELEASE_LIVENESS_OWNER_MARGIN_MS = 5 * 60 * 1000;
|
package/pub-upload.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { accessSync, statSync } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { spawnSync } from "
|
|
4
|
+
import { spawnSync } from "./background-process.mjs";
|
|
5
5
|
|
|
6
6
|
const ACCOUNT_ID = "03ae77ccd7a07bcbb2dcfde47fa7ba3a";
|
|
7
7
|
const BUCKETS = new Map([
|
package/publish-cargo.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { spawnSync } from "
|
|
4
|
+
import { spawnSync } from "./background-process.mjs";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
|
|
7
7
|
const FORBIDDEN_PACKAGE_PATH = /(^|\/)(?:\.env(?:\.[^/]*)?|target(?:\/.*)?|node_modules(?:\/.*)?|[^/]+\.(?:p8|p12|pem|key))$/i;
|
package/publish-swift.mjs
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import fs from "node:fs";
|
|
10
10
|
import path from "node:path";
|
|
11
11
|
import { createHash } from "node:crypto";
|
|
12
|
-
import { spawnSync } from "
|
|
12
|
+
import { spawnSync } from "./background-process.mjs";
|
|
13
13
|
import { scanCrateForSecrets } from "./publish-cargo.mjs";
|
|
14
14
|
|
|
15
15
|
const IDENTIFIER = /^[a-z0-9]+(?:[-_][a-z0-9]+)*$/i;
|
package/publish-update.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { createHash } from "node:crypto";
|
|
|
3
3
|
import { access, readFile, stat } from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
|
-
import { spawn } from "
|
|
6
|
+
import { spawn } from "./background-process.mjs";
|
|
7
7
|
import { pruneReleaseObjects } from "./prune-r2.mjs";
|
|
8
8
|
import { loadReleaseToken } from "./release-token.mjs";
|
|
9
9
|
import { registerRightAppsRelease } from "./rightapps-register.mjs";
|
package/registry-parity.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
|
-
import { execFileSync } from '
|
|
3
|
+
import { execFileSync } from './background-process.mjs';
|
|
4
4
|
import { mkdtemp, mkdir, readFile, readdir, rm, writeFile } from 'node:fs/promises';
|
|
5
5
|
import { tmpdir } from 'node:os';
|
|
6
6
|
import { basename, join, relative } from 'node:path';
|
package/release-invocation.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { spawnSync } from "
|
|
1
|
+
import { spawnSync } from "./background-process.mjs";
|
|
2
2
|
import { existsSync, readFileSync, realpathSync } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { resolveNativeCargoLayout } from "./native-cargo-layout.mjs";
|
package/release-state.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { spawnSync } from "
|
|
2
|
+
import { spawnSync } from "./background-process.mjs";
|
|
3
3
|
import { existsSync, readFileSync, readdirSync, watch } from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { readCachePolicy, resolveCacheLayout } from "./cache-policy.mjs";
|
|
@@ -182,6 +182,7 @@ export async function runBuildStateMachine({
|
|
|
182
182
|
await ops.build?.({ root, app, version, commit, platform, releaseId });
|
|
183
183
|
await ops.sign?.({ root, app, version, commit, platform, releaseId });
|
|
184
184
|
await ops.harden?.({ root, app, version, commit, platform, releaseId });
|
|
185
|
+
await ops.preSeal?.({ root, app, version, commit, platform, releaseId, sealedDir });
|
|
185
186
|
await ops.seal?.({ root, app, version, commit, platform, releaseId, sealedDir });
|
|
186
187
|
return { status: "sealed", resumed: false, releaseId, sealedDir };
|
|
187
188
|
}
|
package/release-token.mjs
CHANGED
package/release.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { createHash } from "node:crypto";
|
|
|
4
4
|
import { existsSync, mkdirSync, readFileSync, realpathSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
7
|
-
import { spawn, spawnSync } from "
|
|
7
|
+
import { spawn, spawnSync } from "./background-process.mjs";
|
|
8
8
|
import { validateRightKitCargoContract } from "./cargo-contract.mjs";
|
|
9
9
|
import { assertQaBackdoorContract } from "./qa-contract.mjs";
|
|
10
10
|
import { assertLegalReleaseContract } from "./legal-contract.mjs";
|
|
@@ -24,6 +24,7 @@ import { terminateProcessTree } from "./heavy-command.mjs";
|
|
|
24
24
|
import { materializeHardeningEvidence } from "./hardening-evidence.mjs";
|
|
25
25
|
import { MACOS_PORTABLE_SIGNING_CONTRACT } from "./macos-signing-contract.mjs";
|
|
26
26
|
import { runConfiguredChecks } from "./release-checks.mjs";
|
|
27
|
+
import { resolveMacosDeveloperIdIdentity } from "./macos-signing-identity.mjs";
|
|
27
28
|
|
|
28
29
|
const TOOL_ROOT = path.dirname(fileURLToPath(import.meta.url));
|
|
29
30
|
const HARDENING_SCAN = path.resolve(TOOL_ROOT, "hardeningscan.mjs");
|
|
@@ -56,6 +57,8 @@ const opts = {
|
|
|
56
57
|
skipChecks: false,
|
|
57
58
|
doctor: false,
|
|
58
59
|
tier: null,
|
|
60
|
+
resume: false,
|
|
61
|
+
resumeFrom: null,
|
|
59
62
|
};
|
|
60
63
|
|
|
61
64
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -70,6 +73,8 @@ for (let i = 0; i < args.length; i++) {
|
|
|
70
73
|
else if (arg === "--skip-checks") opts.skipChecks = true;
|
|
71
74
|
else if (arg === "--skip-hardening") fail("--skip-hardening is forbidden; use exact artifact/source-bound hardeningAllowances");
|
|
72
75
|
else if (arg === "--doctor") opts.doctor = true;
|
|
76
|
+
else if (arg === "--resume") opts.resume = true;
|
|
77
|
+
else if (arg === "--resume-from") { opts.resume = true; opts.resumeFrom = args[++i]; }
|
|
73
78
|
else if (arg === "--tier") opts.tier = args[++i];
|
|
74
79
|
else if (arg.startsWith("--tier=")) opts.tier = arg.slice("--tier=".length);
|
|
75
80
|
else if (arg === "--version") {
|
|
@@ -82,6 +87,8 @@ for (let i = 0; i < args.length; i++) {
|
|
|
82
87
|
|
|
83
88
|
if (opts.upload) fail("combined build+upload was removed; run right-release build, then right-release upload --release <id> --tier patch|update");
|
|
84
89
|
if (opts.tier && !TIERS.has(opts.tier)) usage(2, `invalid --tier: ${opts.tier} (expected patch|update)`);
|
|
90
|
+
const MAC_RESUME_PHASES = ["prepackage", "sign", "package", "post-sign", "harden"];
|
|
91
|
+
if (opts.resumeFrom && !MAC_RESUME_PHASES.includes(opts.resumeFrom)) usage(2, `invalid --resume-from: ${opts.resumeFrom}`);
|
|
85
92
|
const sccacheVersion = assertSharedSccachePrerequisite();
|
|
86
93
|
|
|
87
94
|
const requestedConfigPath = path.resolve(opts.config);
|
|
@@ -216,6 +223,8 @@ if (opts.upload && !target.publish && !target.upload) {
|
|
|
216
223
|
|
|
217
224
|
const started = Date.now();
|
|
218
225
|
console.log(`right-release ${VERSION}: ${config.app ?? path.basename(root)} ${opts.platform} ${mode} tier=${opts.tier ?? "neutral"}`);
|
|
226
|
+
const macResumeAt = opts.platform === "mac" && opts.resumeFrom ? MAC_RESUME_PHASES.indexOf(opts.resumeFrom) : 0;
|
|
227
|
+
const runMacPhase = (phase) => opts.platform !== "mac" || !opts.resumeFrom || MAC_RESUME_PHASES.indexOf(phase) >= macResumeAt;
|
|
219
228
|
|
|
220
229
|
if (opts.install) await runInstall(config.packageManager, workdir);
|
|
221
230
|
await runConfiguredChecks({
|
|
@@ -256,9 +265,9 @@ if (opts.platform === "win") {
|
|
|
256
265
|
}
|
|
257
266
|
} else if (opts.platform === "mac") {
|
|
258
267
|
const signingFiles = target.sign.prePackageFiles.map((p) => path.resolve(root, p));
|
|
259
|
-
await runCommand(target.prePackage, root);
|
|
268
|
+
if (runMacPhase("prepackage")) await runCommand(target.prePackage, root);
|
|
260
269
|
for (const file of signingFiles) await mustExist(file, `missing macOS Developer ID signing artifact: ${file}`);
|
|
261
|
-
await signMacos(signingFiles, root);
|
|
270
|
+
if (runMacPhase("sign")) await signMacos(signingFiles, root);
|
|
262
271
|
} else if (nativeAssembly) {
|
|
263
272
|
if (nativeAssembly.packageHook) await runCommand(nativeAssembly.packageHook, root);
|
|
264
273
|
if (nativeFinalizer) {
|
|
@@ -272,7 +281,7 @@ if (opts.platform === "win") {
|
|
|
272
281
|
}
|
|
273
282
|
}
|
|
274
283
|
|
|
275
|
-
await runCommand(command, root);
|
|
284
|
+
if (opts.platform !== "mac" || runMacPhase("package")) await runCommand(command, root);
|
|
276
285
|
|
|
277
286
|
for (const rel of target.artifacts ?? []) {
|
|
278
287
|
await mustExist(path.resolve(root, rel), `missing release artifact: ${rel}`);
|
|
@@ -294,7 +303,7 @@ if (opts.platform === "win" && target.signingContract === WINDOWS_NSIS_SIGNING_C
|
|
|
294
303
|
await run("node", [SIGN_UPDATER, ...(opts.dryRun ? ["--dry-run"] : []), ...updaterFiles.flatMap((file) => ["--file", file])], root);
|
|
295
304
|
}
|
|
296
305
|
|
|
297
|
-
if (target.postSign && opts.platform !== "win") {
|
|
306
|
+
if (target.postSign && opts.platform !== "win" && (opts.platform !== "mac" || runMacPhase("post-sign"))) {
|
|
298
307
|
await runCommand(target.postSign, root);
|
|
299
308
|
}
|
|
300
309
|
|
|
@@ -304,7 +313,7 @@ if (opts.platform === "mac") {
|
|
|
304
313
|
await notarizeMacos(archive, root);
|
|
305
314
|
}
|
|
306
315
|
|
|
307
|
-
{
|
|
316
|
+
if (opts.platform !== "mac" || runMacPhase("harden")) {
|
|
308
317
|
const scanTargets = target.hardening ?? target.artifacts ?? [];
|
|
309
318
|
if (scanTargets.length) {
|
|
310
319
|
let hardeningEvidence = null;
|
|
@@ -726,10 +735,7 @@ async function validateHostedWorkflows(root, appName, workflows, policy) {
|
|
|
726
735
|
// (Windows) and macos-sign (macOS), so publish is unreachable unless every
|
|
727
736
|
// signing/qualification stage actually verified in this exact run.
|
|
728
737
|
const legacyReleasePublishGate = /publish:\n needs: \[[^\n]*installed-qualification[^\n]*\]\n if: \$\{\{ startsWith\(github\.ref, 'refs\/tags\/v'\) && needs\.installed-qualification\.result == 'success'/.test(publishSource);
|
|
729
|
-
|
|
730
|
-
// when admission recorded `dry_run`. The clause only narrows the gate, so it
|
|
731
|
-
// is accepted as an optional trailing conjunct.
|
|
732
|
-
const admittedReleasePublishGate = /publish:\n needs: \[(?=[^\n]*\badmission\b)(?=[^\n]*\binstalled-qualification\b)(?=[^\n]*\bmacos-sign\b)[^\n]*\]\n if: \$\{\{ needs\.admission\.outputs\.publish == 'true' && needs\.installed-qualification\.result == 'success' && needs\.macos-sign\.result == 'success'(?: && needs\.admission\.outputs\.dry_run != 'true')? \}\}/.test(publishSource);
|
|
738
|
+
const admittedReleasePublishGate = /publish:\n needs: \[(?=[^\n]*\badmission\b)(?=[^\n]*\binstalled-qualification\b)(?=[^\n]*\bmacos-sign\b)[^\n]*\]\n if: \$\{\{ needs\.admission\.outputs\.publish == 'true' && needs\.installed-qualification\.result == 'success' && needs\.macos-sign\.result == 'success' \}\}/.test(publishSource);
|
|
733
739
|
const protectedReleasePublish = releaseChain
|
|
734
740
|
&& (legacyReleasePublishGate || admittedReleasePublishGate)
|
|
735
741
|
&& /\n permissions:\n contents: write\n/.test(publishSource);
|
|
@@ -826,7 +832,8 @@ function verifyMacosSigningReceipt(receipt, files) {
|
|
|
826
832
|
if (!existsSync(receipt)) fail(`missing macOS Developer ID signing receipt: ${receipt}`);
|
|
827
833
|
let evidence;
|
|
828
834
|
try { evidence = JSON.parse(readFileSync(receipt, "utf8")); } catch { fail(`invalid macOS Developer ID signing receipt: ${receipt}`); }
|
|
829
|
-
|
|
835
|
+
let expectedIdentity;
|
|
836
|
+
try { expectedIdentity = resolveMacosDeveloperIdIdentity(); } catch (error) { fail(error.message); }
|
|
830
837
|
const expected = files.map((file) => path.resolve(file)).sort();
|
|
831
838
|
const actual = Array.isArray(evidence.files) ? evidence.files.map((item) => path.resolve(item.file)).sort() : [];
|
|
832
839
|
const hashes = new Map(files.map((file) => [path.resolve(file), createHash("sha256").update(readFileSync(file)).digest("hex")]));
|