diff options
| author | tanaka takashi <isazaka@gmail.com> | 2026-08-14 18:00:29 +0900 |
|---|---|---|
| committer | tanaka takashi <isazaka@gmail.com> | 2026-08-14 18:00:29 +0900 |
| commit | a8233adff75a4726015c7c96ce5e864d4506f6b2 (patch) | |
| tree | 8ce06e1bc5d605b9681259260c99a47921e65413 /scripts | |
| parent | e07183ee6d3e57da325213e898cdcd6687320f53 (diff) | |
Unify the public product name and repository identifier while preserving stable executable, store, AppContainer, namespace, pipe, and mutex identifiers.
Make Directory.Build.props VersionPrefix the application-version source of truth, use SDK-generated assembly metadata, and compare helper compatibility by parsed major/minor/build release identity.
Rebuild the fixed winagent-sandbox-mcp release staging directory from clean defaults, emit versioned ZIP/checksum assets, verify manifests and fresh extraction, and reject local configuration, user state, and test artifacts.
Document release/deployment separation and upgrade responsibilities, add the initial changelog, update operational script paths, and record the v0.1.0 verification results.
Diffstat (limited to 'scripts')
| -rw-r--r-- | scripts/Operational-E2E.ps1 | 2 | ||||
| -rw-r--r-- | scripts/Publish-Release.ps1 | 208 | ||||
| -rw-r--r-- | scripts/Restart-PublishedServer.ps1 | 2 | ||||
| -rw-r--r-- | scripts/Run-Final-ElevatedVerification.ps1 | 5 | ||||
| -rw-r--r-- | scripts/Verify-AdminIpcAttackMatrix.ps1 | 2 | ||||
| -rw-r--r-- | scripts/Verify-PublishedHttp.ps1 | 2 |
6 files changed, 142 insertions, 79 deletions
diff --git a/scripts/Operational-E2E.ps1 b/scripts/Operational-E2E.ps1 index cea1c75..2a32a8e 100644 --- a/scripts/Operational-E2E.ps1 +++ b/scripts/Operational-E2E.ps1 @@ -3,7 +3,7 @@ param( [string]$Transport = 'stdio', [ValidateSet('127.0.0.1', '::1')] [string]$HttpHost = '127.0.0.1', - [string]$ReleaseZip = 'build\Release\publish\agent-sandbox-mcp-1.0.0-win-x64.zip', + [string]$ReleaseZip = 'build\Release\publish\winagent-sandbox-mcp-v0.1.0-win-x64.zip', [string]$PythonPath ) diff --git a/scripts/Publish-Release.ps1 b/scripts/Publish-Release.ps1 index 3ab7bda..b34986a 100644 --- a/scripts/Publish-Release.ps1 +++ b/scripts/Publish-Release.ps1 @@ -1,5 +1,4 @@ param( - [string]$Version = '1.0.0', [string]$OutputRoot = 'build\Release\publish', [switch]$SkipRuntimeCheck ) @@ -11,96 +10,159 @@ $buildRootPath = [IO.Path]::GetFullPath((Join-Path $repository 'build')) if (-not $outputRootPath.StartsWith($buildRootPath + [IO.Path]::DirectorySeparatorChar, [StringComparison]::OrdinalIgnoreCase)) { throw 'Release output must remain inside the repository build directory.' } -$bundleName = "agent-sandbox-mcp-$Version-win-x64" + +$appProject = Join-Path $repository 'src\Agent.App\Agent.App.csproj' +$adminProject = Join-Path $repository 'src\Agent.Admin\Agent.Admin.Host\Agent.Admin.Host.csproj' +function Get-ApplicationVersion([string]$project) { + $value = (& dotnet msbuild $project -nologo -getProperty:Version | Out-String).Trim() + if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($value)) { + throw "Could not resolve the Application Version for $project." + } + return $value +} + +$version = Get-ApplicationVersion $appProject +$adminVersion = Get-ApplicationVersion $adminProject +if ($version -ne $adminVersion) { throw "Main/admin Application Version mismatch: $version / $adminVersion" } +if ($version -notmatch '^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$') { + throw "Application Version is not SemVer: $version" +} +$releaseVersion = [Version]($version -replace '[-+].*$', '') + +$bundleName = 'winagent-sandbox-mcp' +$assetBase = "$bundleName-v$version-win-x64" $bundle = Join-Path $outputRootPath $bundleName -$zip = Join-Path $outputRootPath "$bundleName.zip" +$zip = Join-Path $outputRootPath "$assetBase.zip" $checksum = "$zip.sha256" -$stagingRoot = Join-Path $outputRootPath (".staging-" + [Guid]::NewGuid().ToString('N')) +$stagingRoot = Join-Path $outputRootPath ('.staging-' + [Guid]::NewGuid().ToString('N')) $stagingBundle = Join-Path $stagingRoot $bundleName +$forbiddenNames = @('agent.local.yaml', 'local', 'logs', 'state', 'applied-configurations', 'TestResults', 'artifacts', '.staging') + +function Assert-ReleaseIdentity([string]$path, [string]$assemblyPath, [string]$label) { + $info = [Diagnostics.FileVersionInfo]::GetVersionInfo($path) + $fileVersion = [Version]$info.FileVersion + if ($fileVersion.Major -ne $releaseVersion.Major -or + $fileVersion.Minor -ne $releaseVersion.Minor -or + $fileVersion.Build -ne $releaseVersion.Build) { + throw "$label FileVersion '$($info.FileVersion)' does not match Application Version '$version'." + } + $assemblyVersion = [Reflection.AssemblyName]::GetAssemblyName($assemblyPath).Version + if ($assemblyVersion.Major -ne $releaseVersion.Major -or + $assemblyVersion.Minor -ne $releaseVersion.Minor -or + $assemblyVersion.Build -ne $releaseVersion.Build) { + throw "$label AssemblyVersion '$assemblyVersion' does not match Application Version '$version'." + } + if ($info.ProductVersion -notmatch ('^' + [Regex]::Escape($version) + '(?:\+[0-9A-Za-z.-]+)?$')) { + throw "$label InformationalVersion '$($info.ProductVersion)' is not compatible with '$version'." + } + [PSCustomObject]@{ + Label = $label + ApplicationVersion = $version + AssemblyVersion = $assemblyVersion.ToString() + FileVersion = $info.FileVersion + InformationalVersion = $info.ProductVersion + } +} + +function Assert-NoUserState([string]$root) { + $forbidden = Get-ChildItem -LiteralPath $root -Force -Recurse | + Where-Object Name -in $forbiddenNames | + Select-Object -First 1 + if ($forbidden) { + $relative = [IO.Path]::GetRelativePath($root, $forbidden.FullName) + throw "Release artifact must not contain user state, local assets, or test artifacts: $relative" + } +} New-Item -ItemType Directory -Force -Path $outputRootPath | Out-Null if (Test-Path -LiteralPath $zip) { Remove-Item -LiteralPath $zip -Force } if (Test-Path -LiteralPath $checksum) { Remove-Item -LiteralPath $checksum -Force } New-Item -ItemType Directory -Path $stagingBundle | Out-Null -$appProject = Join-Path $repository 'src\Agent.App\Agent.App.csproj' -$adminProject = Join-Path $repository 'src\Agent.Admin\Agent.Admin.Host\Agent.Admin.Host.csproj' -dotnet restore $appProject -r win-x64 --locked-mode -if ($LASTEXITCODE -ne 0) { throw "win-x64 locked restore failed with exit code $LASTEXITCODE" } -dotnet restore $adminProject -r win-x64 --locked-mode -if ($LASTEXITCODE -ne 0) { throw "win-x64 admin locked restore failed with exit code $LASTEXITCODE" } -dotnet publish $appProject -c Release -r win-x64 --self-contained true --no-restore --output $stagingBundle -p:Version=$Version -if ($LASTEXITCODE -ne 0) { throw "dotnet publish failed with exit code $LASTEXITCODE" } -$adminBundle = Join-Path $stagingBundle 'admin' -dotnet publish $adminProject -c Release -r win-x64 --self-contained true --no-restore --output $adminBundle -p:Version=$Version -if ($LASTEXITCODE -ne 0) { throw "admin dotnet publish failed with exit code $LASTEXITCODE" } +try { + dotnet restore $appProject -r win-x64 --locked-mode + if ($LASTEXITCODE -ne 0) { throw "win-x64 locked restore failed with exit code $LASTEXITCODE" } + dotnet restore $adminProject -r win-x64 --locked-mode + if ($LASTEXITCODE -ne 0) { throw "win-x64 admin locked restore failed with exit code $LASTEXITCODE" } + dotnet publish $appProject -c Release -r win-x64 --self-contained true --no-restore --output $stagingBundle + if ($LASTEXITCODE -ne 0) { throw "dotnet publish failed with exit code $LASTEXITCODE" } + $adminBundle = Join-Path $stagingBundle 'admin' + dotnet publish $adminProject -c Release -r win-x64 --self-contained true --no-restore --output $adminBundle + if ($LASTEXITCODE -ne 0) { throw "admin dotnet publish failed with exit code $LASTEXITCODE" } -$required = @( - 'agent-mcp.exe', 'admin\agent-mcp-admin.exe', 'agent.yaml', 'config\locales\en.json', 'config\locales\ja.json', - 'prompts\user-system.md', 'skills', 'README.md' -) -foreach ($relative in $required) { - if (-not (Test-Path -LiteralPath (Join-Path $stagingBundle $relative))) { throw "Release Bundle is missing $relative" } -} -foreach ($relative in @('agent.local.yaml', 'config\agent.local.example.yaml')) { - if (Test-Path -LiteralPath (Join-Path $stagingBundle $relative)) { throw "Release Bundle must not contain local override input: $relative" } -} + $required = @( + 'agent-mcp.exe', 'admin\agent-mcp-admin.exe', 'agent.yaml', 'config\locales\en.json', 'config\locales\ja.json', + 'prompts\user-system.md', 'skills', 'README.md', 'CHANGELOG.md' + ) + foreach ($relative in $required) { + if (-not (Test-Path -LiteralPath (Join-Path $stagingBundle $relative))) { throw "Release Bundle is missing $relative" } + } + Assert-NoUserState $stagingBundle -$manifestPath = Join-Path $stagingBundle 'publish-manifest.sha256' -Get-ChildItem -LiteralPath $stagingBundle -File -Recurse | - Where-Object FullName -ne $manifestPath | - Sort-Object FullName | - ForEach-Object { - $relative = [IO.Path]::GetRelativePath($stagingBundle, $_.FullName).Replace('\', '/') - $hash = (Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256).Hash.ToLowerInvariant() - "$hash $relative" - } | Set-Content -LiteralPath $manifestPath -Encoding utf8NoBOM + $mainVersionInfo = Assert-ReleaseIdentity (Join-Path $stagingBundle 'agent-mcp.exe') (Join-Path $stagingBundle 'agent-mcp.dll') 'main' + $adminVersionInfo = Assert-ReleaseIdentity (Join-Path $stagingBundle 'admin\agent-mcp-admin.exe') (Join-Path $stagingBundle 'admin\agent-mcp-admin.dll') 'admin' -Compress-Archive -LiteralPath $stagingBundle -DestinationPath $zip -CompressionLevel Optimal -$zipHash = (Get-FileHash -LiteralPath $zip -Algorithm SHA256).Hash.ToLowerInvariant() -"$zipHash $([IO.Path]::GetFileName($zip))" | Set-Content -LiteralPath $checksum -Encoding ascii + $manifestPath = Join-Path $stagingBundle 'publish-manifest.sha256' + Get-ChildItem -LiteralPath $stagingBundle -File -Recurse | + Where-Object FullName -ne $manifestPath | + Sort-Object FullName | + ForEach-Object { + $relative = [IO.Path]::GetRelativePath($stagingBundle, $_.FullName).Replace('\', '/') + $hash = (Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256).Hash.ToLowerInvariant() + "$hash $relative" + } | Set-Content -LiteralPath $manifestPath -Encoding utf8NoBOM -$freshRoot = Join-Path ([IO.Path]::GetTempPath()) ("agent-mcp-release-check-" + [Guid]::NewGuid().ToString('N')) -New-Item -ItemType Directory -Path $freshRoot | Out-Null -try { - Expand-Archive -LiteralPath $zip -DestinationPath $freshRoot - $freshBundle = Join-Path $freshRoot $bundleName - $sourceReferences = Get-ChildItem -LiteralPath $freshBundle -File -Recurse | - Where-Object Extension -in @('.yaml', '.json', '.md', '.config') | - Select-String -SimpleMatch -Pattern $repository - if ($sourceReferences) { throw 'Release Bundle contains a source-tree reference.' } - if (-not $SkipRuntimeCheck) { - Push-Location $freshBundle - try { - & .\agent-mcp.exe show .\agent.yaml - if ($LASTEXITCODE -ne 0) { throw "Bundle configuration check failed with exit code $LASTEXITCODE." } + Compress-Archive -LiteralPath $stagingBundle -DestinationPath $zip -CompressionLevel Optimal + $zipHash = (Get-FileHash -LiteralPath $zip -Algorithm SHA256).Hash.ToLowerInvariant() + "$zipHash $([IO.Path]::GetFileName($zip))" | Set-Content -LiteralPath $checksum -Encoding ascii + + $freshRoot = Join-Path ([IO.Path]::GetTempPath()) ('winagent-sandbox-mcp-release-check-' + [Guid]::NewGuid().ToString('N')) + New-Item -ItemType Directory -Path $freshRoot | Out-Null + try { + Expand-Archive -LiteralPath $zip -DestinationPath $freshRoot + $freshItems = @(Get-ChildItem -LiteralPath $freshRoot -Force) + if ($freshItems.Count -ne 1 -or -not $freshItems[0].PSIsContainer -or $freshItems[0].Name -ne $bundleName) { + throw "ZIP must contain exactly one unversioned root directory named '$bundleName'." + } + $freshBundle = Join-Path $freshRoot $bundleName + Assert-NoUserState $freshBundle + $sourceReferences = Get-ChildItem -LiteralPath $freshBundle -File -Recurse | + Where-Object Extension -in @('.yaml', '.json', '.md', '.config') | + Select-String -SimpleMatch -Pattern $repository + if ($sourceReferences) { throw 'Release Bundle contains a source-tree reference.' } + foreach ($line in Get-Content -LiteralPath (Join-Path $freshBundle 'publish-manifest.sha256')) { + if ($line -notmatch '^([0-9a-f]{64}) (.+)$') { throw "Invalid manifest entry: $line" } + $actual = (Get-FileHash -LiteralPath (Join-Path $freshBundle $Matches[2]) -Algorithm SHA256).Hash.ToLowerInvariant() + if ($actual -ne $Matches[1]) { throw "Manifest hash mismatch: $($Matches[2])" } + } + if (-not $SkipRuntimeCheck) { + Push-Location $freshBundle + try { + & .\agent-mcp.exe show .\agent.yaml + if ($LASTEXITCODE -ne 0) { throw "Bundle configuration check failed with exit code $LASTEXITCODE." } + } + finally { Pop-Location } } - finally { Pop-Location } } -} -finally { - if (Test-Path -LiteralPath $freshRoot) { Remove-Item -LiteralPath $freshRoot -Recurse -Force } -} + finally { + if (Test-Path -LiteralPath $freshRoot) { Remove-Item -LiteralPath $freshRoot -Recurse -Force } + } -# The ZIP is reproducible defaults only. The deployment directory keeps explicitly local, -# publish-excluded inputs while all versioned defaults and binaries are replaced. -try { - if (Test-Path -LiteralPath $bundle) { - $resolvedBundle = (Resolve-Path -LiteralPath $bundle).Path - if (-not $resolvedBundle.StartsWith($outputRootPath + [IO.Path]::DirectorySeparatorChar, [StringComparison]::OrdinalIgnoreCase)) { - throw 'Existing deployment Bundle escaped the validated output root.' - } - Get-ChildItem -LiteralPath $resolvedBundle -Force | - Where-Object Name -notin @('agent.local.yaml', 'local', 'logs', 'state') | - Remove-Item -Recurse -Force + $bundleFullPath = [IO.Path]::GetFullPath($bundle) + if (-not $bundleFullPath.StartsWith($outputRootPath + [IO.Path]::DirectorySeparatorChar, [StringComparison]::OrdinalIgnoreCase) -or + [IO.Path]::GetFileName($bundleFullPath) -ne $bundleName) { + throw 'Fixed Release Bundle target escaped the validated output root.' } - else { New-Item -ItemType Directory -Path $bundle | Out-Null } - Copy-Item -Path (Join-Path $stagingBundle '*') -Destination $bundle -Recurse -Force + if (Test-Path -LiteralPath $bundleFullPath) { Remove-Item -LiteralPath $bundleFullPath -Recurse -Force } + Move-Item -LiteralPath $stagingBundle -Destination $bundleFullPath + Assert-NoUserState $bundleFullPath + + $mainVersionInfo + $adminVersionInfo + Write-Output "Bundle: $bundle" + Write-Output "ZIP: $zip" + Write-Output "SHA-256: $zipHash" } finally { if (Test-Path -LiteralPath $stagingRoot) { Remove-Item -LiteralPath $stagingRoot -Recurse -Force } } - -Write-Output "Bundle: $bundle" -Write-Output "ZIP: $zip" -Write-Output "SHA-256: $checksum" diff --git a/scripts/Restart-PublishedServer.ps1 b/scripts/Restart-PublishedServer.ps1 index d4386f4..1909adf 100644 --- a/scripts/Restart-PublishedServer.ps1 +++ b/scripts/Restart-PublishedServer.ps1 @@ -1,5 +1,5 @@ param( - [string]$BundlePath = 'build\Release\publish\agent-sandbox-mcp-1.0.0-win-x64', + [string]$BundlePath = 'build\Release\publish\winagent-sandbox-mcp', [switch]$StopOnly ) diff --git a/scripts/Run-Final-ElevatedVerification.ps1 b/scripts/Run-Final-ElevatedVerification.ps1 index 1162da0..286d459 100644 --- a/scripts/Run-Final-ElevatedVerification.ps1 +++ b/scripts/Run-Final-ElevatedVerification.ps1 @@ -33,8 +33,9 @@ function Invoke-VerificationStep { "START $Name $(Get-Date -Format o)" | Tee-Object -FilePath $path $exitCode = 0 try { + $global:LASTEXITCODE = 0 & $Action *>&1 | Tee-Object -FilePath $path -Append - if ($LASTEXITCODE) { $exitCode = $LASTEXITCODE } + if ($global:LASTEXITCODE) { $exitCode = $global:LASTEXITCODE } } catch { $_ | Out-String | Tee-Object -FilePath $path -Append @@ -44,7 +45,7 @@ function Invoke-VerificationStep { if ($exitCode -ne 0) { $script:failed = $true } } -$bundle = Join-Path $repository 'build\Release\publish\agent-sandbox-mcp-1.0.0-win-x64' +$bundle = Join-Path $repository 'build\Release\publish\winagent-sandbox-mcp' $windowsTests = Join-Path $repository 'build\Release\Agent.Windows.Tests\net10.0-windows10.0.26100.0\Agent.Windows.Tests.exe' $coreTests = Join-Path $repository 'build\Release\Agent.Core.Tests\net10.0-windows10.0.26100.0\Agent.Core.Tests.exe' $plannerTests = Join-Path $repository 'build\Release\Agent.Planner.Tests\net10.0\Agent.Planner.Tests.exe' diff --git a/scripts/Verify-AdminIpcAttackMatrix.ps1 b/scripts/Verify-AdminIpcAttackMatrix.ps1 index c0703f0..0b3957a 100644 --- a/scripts/Verify-AdminIpcAttackMatrix.ps1 +++ b/scripts/Verify-AdminIpcAttackMatrix.ps1 @@ -1,6 +1,6 @@ param( [string]$Configuration = 'Release', - [string]$BundlePath = 'build\Release\publish\agent-sandbox-mcp-1.0.0-win-x64' + [string]$BundlePath = 'build\Release\publish\winagent-sandbox-mcp' ) $ErrorActionPreference = 'Stop' diff --git a/scripts/Verify-PublishedHttp.ps1 b/scripts/Verify-PublishedHttp.ps1 index 414e749..584cbd7 100644 --- a/scripts/Verify-PublishedHttp.ps1 +++ b/scripts/Verify-PublishedHttp.ps1 @@ -1,5 +1,5 @@ param( - [string]$BundlePath = 'build\Release\publish\agent-sandbox-mcp-1.0.0-win-x64', + [string]$BundlePath = 'build\Release\publish\winagent-sandbox-mcp', [string]$TestRoot = 'C:\tmp', [string]$EvidenceRoot = 'artifacts\evidence\published-http-cmd-verification' ) |
