build: reject dirty Rayforge source trees
This commit is contained in:
parent
a5a8e4086d
commit
7b817f6339
8 changed files with 54 additions and 6 deletions
|
|
@ -15,7 +15,8 @@ negative cases, cancellation/recovery and synthetic LightBurn imports.
|
|||
The repository now provides:
|
||||
|
||||
- an on-demand source/build entry point that never installs the Windows
|
||||
toolchain;
|
||||
toolchain, rejects modified upstream source and replaces only the validated
|
||||
addon staging directory;
|
||||
- a non-executing verifier for externally produced Windows artifacts;
|
||||
it requires an exact repository-to-manifest-to-ZIP addon file set;
|
||||
- a checksummed acceptance-kit generator containing only allowlisted public
|
||||
|
|
|
|||
|
|
@ -39,8 +39,12 @@ toolchain preparation remains a separate, explicitly invoked upstream action
|
|||
or should happen on a dedicated build runner.
|
||||
|
||||
Before staging, the script rejects transport imports in the addon. The build
|
||||
manifest records the exact upstream commit and hashes every addon source file,
|
||||
the portable archive and installer. It does not claim the GUI has been tested.
|
||||
entry point also rejects tracked Rayforge modifications and any unexpected
|
||||
untracked source. It removes and recreates only the validated addon staging
|
||||
directory, preventing stale files from a previous run from entering the
|
||||
bundle. The build manifest records the exact upstream commit and hashes every
|
||||
addon source file, the portable archive and installer. It does not claim the
|
||||
GUI has been tested.
|
||||
|
||||
Copy the ZIP, installer and JSON manifest together, then verify an externally
|
||||
produced checkpoint without launching either binary:
|
||||
|
|
|
|||
|
|
@ -45,6 +45,12 @@ revision into the ignored `.build/upstreams/` cache only when explicitly
|
|||
invoked. Its default action prepares source only; it neither installs the
|
||||
Windows toolchain nor starts a build.
|
||||
|
||||
Source preparation also verifies the Git worktree state. Tracked changes and
|
||||
unexpected untracked source fail closed. The Windows checkpoint wrapper allows
|
||||
only its known addon staging subtree, validates that path, removes it before
|
||||
copying, and then rechecks the worktree before invoking upstream's build.
|
||||
Ignored upstream build outputs remain outside this source-integrity decision.
|
||||
|
||||
The repository scripts do not install Windows dependencies. Bundle creation
|
||||
requires the explicit `-BuildWindowsBundle` switch and an already reviewed,
|
||||
prepared runtime. This keeps a multi-gigabyte MSYS2/UCRT64 toolchain out of
|
||||
|
|
|
|||
|
|
@ -321,7 +321,9 @@ The transport-free portions are now consolidated by the
|
|||
[M1 no-device acceptance checkpoint](research/m1-no-device-acceptance.md).
|
||||
It reruns the public suite and leak guard and hashes the validated regression
|
||||
reports. The on-demand build entry point fetches pinned source and stages the
|
||||
addon but deliberately refuses to install a missing Windows toolchain.
|
||||
addon but deliberately refuses to install a missing Windows toolchain. It
|
||||
rejects modified upstream source and replaces the exact addon staging tree so
|
||||
stale files cannot survive between builds.
|
||||
An artifact produced elsewhere can be checked locally without execution: the
|
||||
verifier rejects unsafe paths or changed bytes and confirms the pin plus every
|
||||
packaged addon source before the manual GUI checklist may begin. It also
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ if (-not $ArtifactRoot) {
|
|||
}
|
||||
|
||||
& (Join-Path $PSScriptRoot "prepare_rayforge_build.ps1") `
|
||||
-CacheRoot $CacheRoot
|
||||
-CacheRoot $CacheRoot `
|
||||
-AllowStagedPirateRayAddon
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Pinned Rayforge source preparation failed"
|
||||
}
|
||||
|
|
@ -48,6 +49,17 @@ $addonDestination = Join-Path $checkout (
|
|||
"rayforge\builtin_addons\rayforge-addon-the-pirate-ray-jcz"
|
||||
)
|
||||
if ($PSCmdlet.ShouldProcess($addonDestination, "Stage no-device addon")) {
|
||||
$resolvedCheckout = [IO.Path]::GetFullPath($checkout).TrimEnd("\")
|
||||
$resolvedAddonDestination = [IO.Path]::GetFullPath($addonDestination)
|
||||
$expectedAddonDestination = Join-Path $resolvedCheckout (
|
||||
"rayforge\builtin_addons\rayforge-addon-the-pirate-ray-jcz"
|
||||
)
|
||||
if ($resolvedAddonDestination -ne $expectedAddonDestination) {
|
||||
throw "Refusing to replace an unexpected addon staging path"
|
||||
}
|
||||
if (Test-Path -LiteralPath $resolvedAddonDestination) {
|
||||
Remove-Item -LiteralPath $resolvedAddonDestination -Recurse -Force
|
||||
}
|
||||
New-Item -ItemType Directory -Force -Path $addonDestination | Out-Null
|
||||
Copy-Item -Path (Join-Path $addonSource "*") `
|
||||
-Destination $addonDestination -Recurse -Force
|
||||
|
|
@ -56,6 +68,7 @@ if ($PSCmdlet.ShouldProcess($addonDestination, "Stage no-device addon")) {
|
|||
$prepareArguments = @{
|
||||
CacheRoot = $CacheRoot
|
||||
BuildWindowsBundle = $true
|
||||
AllowStagedPirateRayAddon = $true
|
||||
}
|
||||
& (Join-Path $PSScriptRoot "prepare_rayforge_build.ps1") @prepareArguments
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
[CmdletBinding(SupportsShouldProcess)]
|
||||
param(
|
||||
[string]$CacheRoot,
|
||||
[switch]$BuildWindowsBundle
|
||||
[switch]$BuildWindowsBundle,
|
||||
[switch]$AllowStagedPirateRayAddon
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
|
@ -54,8 +55,23 @@ if ($verifiedCommit -ne $manifest.commit) {
|
|||
throw "Rayforge pin mismatch: expected $($manifest.commit), got $verifiedCommit"
|
||||
}
|
||||
|
||||
$statusLines = @(& git -C $checkout status --porcelain=v1 --untracked-files=all)
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Could not inspect the Rayforge checkout state"
|
||||
}
|
||||
$allowedAddonPrefix = "?? rayforge/builtin_addons/rayforge-addon-the-pirate-ray-jcz/"
|
||||
$unexpectedChanges = @(
|
||||
$statusLines | Where-Object {
|
||||
$_ -and (-not $AllowStagedPirateRayAddon -or -not $_.StartsWith($allowedAddonPrefix))
|
||||
}
|
||||
)
|
||||
if ($unexpectedChanges.Count -gt 0) {
|
||||
throw "Rayforge checkout contains unreviewed tracked or untracked changes"
|
||||
}
|
||||
|
||||
Write-Output "Rayforge source ready at $checkout"
|
||||
Write-Output "Verified commit: $verifiedCommit"
|
||||
Write-Output "Verified checkout state: clean except explicitly staged PirateRay addon"
|
||||
|
||||
if (-not $BuildWindowsBundle) {
|
||||
Write-Output "No dependencies were installed and no build was started."
|
||||
|
|
|
|||
|
|
@ -47,6 +47,9 @@ class M1AcceptancePipelineTests(unittest.TestCase):
|
|||
self.assertIn("schema_version = 2", source)
|
||||
self.assertIn("Rayforge-LICENSE.txt", source)
|
||||
self.assertIn("license_files", source)
|
||||
self.assertIn("GetFullPath", source)
|
||||
self.assertIn("Remove-Item -LiteralPath $resolvedAddonDestination", source)
|
||||
self.assertIn("AllowStagedPirateRayAddon", source)
|
||||
|
||||
def test_acceptance_has_no_hardware_commands(self):
|
||||
source = ACCEPTANCE.read_text(encoding="utf-8").lower()
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@ class UpstreamBuildReferenceTests(unittest.TestCase):
|
|||
self.assertNotIn("InstallWindowsDependencies", script)
|
||||
self.assertIn("BuildWindowsBundle", script)
|
||||
self.assertIn("No dependencies were installed", script)
|
||||
self.assertIn("status --porcelain=v1 --untracked-files=all", script)
|
||||
self.assertIn("unreviewed tracked or untracked changes", script)
|
||||
self.assertIn("AllowStagedPirateRayAddon", script)
|
||||
|
||||
def test_prepare_script_has_no_hardware_path(self):
|
||||
script = PREPARE.read_text(encoding="utf-8").lower()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue