diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5915ab931..7993ebe97 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -859,7 +859,7 @@ jobs: with: name: openclaw-msix-store-unsigned-${{ matrix.architecture }} path: | - artifacts/msix/${{ matrix.architecture }}/OpenClawCompanion-${{ matrix.architecture }}.msix + artifacts/msix/${{ matrix.architecture }}/OpenClaw-${{ matrix.architecture }}.msix artifacts/msix/${{ matrix.architecture }}/msix-metadata.json if-no-files-found: error @@ -891,7 +891,7 @@ jobs: with: name: openclaw-msix-dev-${{ matrix.architecture }} path: | - artifacts/msix-dev/${{ matrix.architecture }}/OpenClawCompanion-Dev-${{ matrix.architecture }}.msix + artifacts/msix-dev/${{ matrix.architecture }}/OpenClaw-Dev-${{ matrix.architecture }}.msix artifacts/msix-dev/${{ matrix.architecture }}/OpenClaw-Dev.cer artifacts/msix-dev/${{ matrix.architecture }}/msix-metadata.json artifacts/msix-dev/${{ matrix.architecture }}/INSTALL.txt diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 0c09c2b83..10e51513b 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -291,7 +291,7 @@ Partner Center rejects any submission whose revision is non-zero. `-Msix Store` forces `-Configuration Release`, refuses to combine with `-DevBuild`, and delegates to `scripts\Build-StoreMsix.ps1` once per architecture. Each run produces one unsigned self-contained package at -`artifacts\msix\\OpenClawCompanion-.msix` alongside an +`artifacts\msix\\OpenClaw-.msix` alongside an `msix-metadata.json` provenance sidecar recording the source commit, whether the tree was dirty, the package version, publisher, and the package SHA-256. @@ -330,8 +330,12 @@ Download the desired ZIP from the workflow run's **Artifacts**: | Artifact | Contents and purpose | |---|---| -| `openclaw-msix-dev-x64` / `openclaw-msix-dev-arm64` | Signed Dev `.msix`, public `OpenClaw-Dev.cer`, `msix-metadata.json`, and `INSTALL.txt` for opt-in tester installation. | -| `openclaw-msix-store-unsigned-x64` / `openclaw-msix-store-unsigned-arm64` | Unsigned Store `.msix` and the validated provenance sidecar from `Build-StoreMsix.ps1`. Submission inputs, not directly installable tester packages. | +| `openclaw-msix-dev-x64` / `openclaw-msix-dev-arm64` | Signed `OpenClaw-Dev-.msix`, public `OpenClaw-Dev.cer`, `msix-metadata.json`, and `INSTALL.txt` for opt-in tester installation. | +| `openclaw-msix-store-unsigned-x64` / `openclaw-msix-store-unsigned-arm64` | Unsigned `OpenClaw-.msix` and the validated provenance sidecar from `Build-StoreMsix.ps1`. Submission inputs, not directly installable tester packages. | + +These MSIX filenames use `OpenClaw`, not the previous `OpenClawCompanion` +prefix. Only the download filenames changed: package identities, versions, +and EXE installer filenames are unchanged. Existing downloads are not renamed. Each disposable runner uses `setup-dev-msix-cert.ps1` to generate and trust a non-exportable Dev certificate. Only its public `.cer` is included. The key and diff --git a/build.ps1 b/build.ps1 index 562ac40ba..9161ec2b2 100644 --- a/build.ps1 +++ b/build.ps1 @@ -597,7 +597,7 @@ if ($buildStoreMsix) { Write-Error "Store MSIX ($storeArchitecture) packaging failed: $($_.Exception.Message)" exit 1 } - $storePackages += Join-Path $repoRoot "artifacts\msix\$storeArchitecture\OpenClawCompanion-$storeArchitecture.msix" + $storePackages += Join-Path $repoRoot "artifacts\msix\$storeArchitecture\OpenClaw-$storeArchitecture.msix" } Write-Header "Store MSIX Packages" diff --git a/docs/RELEASING.md b/docs/RELEASING.md index e5953d87b..e5b81aa06 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -175,9 +175,9 @@ Current release artifacts are: Canonical alpha releases additionally contain: -- `OpenClawCompanion-x64.msix` and `OpenClawCompanion-arm64.msix` -- `OpenClawCompanion-x64.msix-metadata.json` and - `OpenClawCompanion-arm64.msix-metadata.json` +- `OpenClaw-x64.msix` and `OpenClaw-arm64.msix` +- `OpenClaw-x64.msix-metadata.json` and + `OpenClaw-arm64.msix-metadata.json` These are **unsigned Store submission inputs, not installers**. Download the MSIX files and upload them manually to Partner Center. Microsoft signs accepted diff --git a/scripts/Build-StoreMsix.ps1 b/scripts/Build-StoreMsix.ps1 index adbd8d06d..af0ba85f8 100644 --- a/scripts/Build-StoreMsix.ps1 +++ b/scripts/Build-StoreMsix.ps1 @@ -208,7 +208,7 @@ try { ) } - $msixName = "OpenClawCompanion-$Architecture.msix" + $msixName = "OpenClaw-$Architecture.msix" $msixPath = Join-Path $OutputDirectory $msixName Copy-Item -LiteralPath $builtPackages[0].FullName -Destination $msixPath -Force diff --git a/scripts/Export-DevMsixArtifact.ps1 b/scripts/Export-DevMsixArtifact.ps1 index 53811659b..6366dddcf 100644 --- a/scripts/Export-DevMsixArtifact.ps1 +++ b/scripts/Export-DevMsixArtifact.ps1 @@ -77,7 +77,7 @@ if ($LASTEXITCODE -ne 0 -or $sourceCommit -notmatch '^[0-9a-fA-F]{40}$') { $sourceTreeDirty = [bool](& git -C $repositoryRoot status --porcelain) if ($LASTEXITCODE -ne 0) { throw 'Unable to inspect the current source tree.' } -$packageName = "OpenClawCompanion-Dev-$Architecture.msix" +$packageName = "OpenClaw-Dev-$Architecture.msix" New-Item -ItemType Directory -Path $OutputDirectory -Force | Out-Null Copy-Item -LiteralPath $package.FullName -Destination (Join-Path $OutputDirectory $packageName) $certificatePath = Join-Path $OutputDirectory 'OpenClaw-Dev.cer' diff --git a/scripts/Stage-StoreMsixReleaseAssets.ps1 b/scripts/Stage-StoreMsixReleaseAssets.ps1 index 1e49c1f40..2e749bdbe 100644 --- a/scripts/Stage-StoreMsixReleaseAssets.ps1 +++ b/scripts/Stage-StoreMsixReleaseAssets.ps1 @@ -32,7 +32,7 @@ if ((Test-Path -LiteralPath $OutputDirectory) -and $expectedVersion = ($Version -replace '-alpha\.\d+$', '') + '.0' $packages = foreach ($architecture in @('x64', 'arm64')) { $directory = Join-Path $ArtifactDirectory "openclaw-msix-store-unsigned-$architecture" - $packageName = "OpenClawCompanion-$architecture.msix" + $packageName = "OpenClaw-$architecture.msix" $expectedFiles = @($packageName, 'msix-metadata.json') | Sort-Object $entries = @(Get-ChildItem -LiteralPath $directory -Force) if ($entries.Count -ne 2 -or @@ -80,7 +80,7 @@ $files = foreach ($package in $packages) { Notes = @" ### Unsigned Store submission packages (alpha only) -OpenClawCompanion-x64.msix and OpenClawCompanion-arm64.msix are unsigned +OpenClaw-x64.msix and OpenClaw-arm64.msix are unsigned Partner Center submission inputs, not installers. Their architecture-specific metadata files record the source commit, package version, and SHA-256. Upload the MSIX files manually to Partner Center; Microsoft signs accepted diff --git a/scripts/test-ci-workflow-contract.ps1 b/scripts/test-ci-workflow-contract.ps1 index fa62adac7..fc284b92d 100644 --- a/scripts/test-ci-workflow-contract.ps1 +++ b/scripts/test-ci-workflow-contract.ps1 @@ -668,6 +668,8 @@ foreach ($token in @( '-CertificateThumbprint $thumbprint', 'name: openclaw-msix-store-unsigned-${{ matrix.architecture }}', 'name: openclaw-msix-dev-${{ matrix.architecture }}', + 'artifacts/msix/${{ matrix.architecture }}/OpenClaw-${{ matrix.architecture }}.msix', + 'artifacts/msix-dev/${{ matrix.architecture }}/OpenClaw-Dev-${{ matrix.architecture }}.msix', 'msix-metadata.json', 'OpenClaw-Dev.cer', 'INSTALL.txt', @@ -726,8 +728,8 @@ Assert-Contains -Text $createRelease -Expected '${{ steps.msix_alpha.outputs.fil Assert-Contains -Text $createRelease -Expected '${{ steps.msix_alpha.outputs.notes }}' -Message "Only alpha release notes may mention MSIX downloads." Assert-Contains -Text $createRelease -Expected 'fail_on_unmatched_files: true' -Message "Missing release files must fail publication." Assert-Contains -Text $createRelease -Expected "make_latest: `${{ needs.metadata.outputs.isPrerelease == 'true' && 'false' || 'true' }}" -Message "Alpha releases must not become Latest." -Assert-NotContains -Text $createRelease -Unexpected 'OpenClawCompanion-x64.msix' -Message "MSIX must not be an unconditional stable release asset." -Assert-NotContains -Text $createRelease -Unexpected 'OpenClawCompanion-arm64.msix' -Message "MSIX must not be an unconditional stable release asset." +Assert-NotContains -Text $createRelease -Unexpected 'OpenClaw-x64.msix' -Message "MSIX must not be an unconditional stable release asset." +Assert-NotContains -Text $createRelease -Unexpected 'OpenClaw-arm64.msix' -Message "MSIX must not be an unconditional stable release asset." Assert-Contains -Text $workflow -Expected "./scripts/test-msix-ci-artifacts.ps1" -Message "Fast validation must exercise the Dev artifact contracts." Assert-Contains -Text $workflow -Expected "./scripts/test-msix-alpha-release.ps1" -Message "Fast validation must exercise alpha release staging." diff --git a/scripts/test-msix-alpha-release.ps1 b/scripts/test-msix-alpha-release.ps1 index 660aca423..9f8d042a0 100644 --- a/scripts/test-msix-alpha-release.ps1 +++ b/scripts/test-msix-alpha-release.ps1 @@ -36,7 +36,7 @@ function New-Fixture { foreach ($architecture in @('x64', 'arm64')) { $directory = Join-Path $inputPath "openclaw-msix-store-unsigned-$architecture" New-Item -ItemType Directory -Path $directory -Force | Out-Null - $packageName = "OpenClawCompanion-$architecture.msix" + $packageName = "OpenClaw-$architecture.msix" $packagePath = Join-Path $directory $packageName Set-Content -LiteralPath $packagePath -Value "Synthetic $architecture package fixture." [ordered]@{ @@ -69,18 +69,18 @@ try { $assets = & $stager @arguments $names = @($assets.Files | ForEach-Object { [IO.Path]::GetFileName($_) } | Sort-Object) $expected = @( - 'OpenClawCompanion-arm64.msix', - 'OpenClawCompanion-arm64.msix-metadata.json', - 'OpenClawCompanion-x64.msix', - 'OpenClawCompanion-x64.msix-metadata.json' + 'OpenClaw-arm64.msix', + 'OpenClaw-arm64.msix-metadata.json', + 'OpenClaw-x64.msix', + 'OpenClaw-x64.msix-metadata.json' ) if (@(Compare-Object $expected $names).Count -gt 0 -or @(Get-ChildItem -LiteralPath $arguments.OutputDirectory).Count -ne 4) { throw 'Release assets did not match the exact public Store allowlist.' } foreach ($architecture in @('x64', 'arm64')) { - $original = Join-Path $arguments.ArtifactDirectory "openclaw-msix-store-unsigned-$architecture\OpenClawCompanion-$architecture.msix" - $copy = Join-Path $arguments.OutputDirectory "OpenClawCompanion-$architecture.msix" + $original = Join-Path $arguments.ArtifactDirectory "openclaw-msix-store-unsigned-$architecture\OpenClaw-$architecture.msix" + $copy = Join-Path $arguments.OutputDirectory "OpenClaw-$architecture.msix" if ((Get-FileHash -LiteralPath $original).Hash -ne (Get-FileHash -LiteralPath $copy).Hash) { throw 'Staging changed the validated package bytes.' } @@ -90,7 +90,7 @@ try { throw 'Published metadata did not describe the released file.' } } - foreach ($warning in @('unsigned', 'not installers', '2026.7.2.0', 'same Store version', 'Dev-signed tester downloads remain in Actions')) { + foreach ($warning in @('OpenClaw-x64.msix', 'OpenClaw-arm64.msix', 'unsigned', 'not installers', '2026.7.2.0', 'same Store version', 'Dev-signed tester downloads remain in Actions')) { if (-not $assets.Notes.Contains($warning)) { throw "Release notes are missing '$warning'." } } Assert-Fails { & $stager @arguments } 'absent or empty' @@ -126,7 +126,7 @@ try { Set-Content -LiteralPath (Join-Path $arguments.ArtifactDirectory 'openclaw-msix-store-unsigned-x64\extra.pfx') 'not a real key' Assert-Fails { & $stager @arguments } 'exactly' $arguments = New-Fixture - Remove-Item -LiteralPath (Join-Path $arguments.ArtifactDirectory 'openclaw-msix-store-unsigned-arm64\OpenClawCompanion-arm64.msix') + Remove-Item -LiteralPath (Join-Path $arguments.ArtifactDirectory 'openclaw-msix-store-unsigned-arm64\OpenClaw-arm64.msix') Assert-Fails { & $stager @arguments } 'exactly' $arguments = New-Fixture $arguments.Version = '2026.7.3-alpha.4' diff --git a/scripts/test-msix-ci-artifacts.ps1 b/scripts/test-msix-ci-artifacts.ps1 index 36f805f49..c6234ebdd 100644 --- a/scripts/test-msix-ci-artifacts.ps1 +++ b/scripts/test-msix-ci-artifacts.ps1 @@ -83,31 +83,42 @@ function New-Arguments { try { Add-Type -AssemblyName System.IO.Compression.FileSystem - $arguments = New-Arguments - New-Package -Directory $arguments.PackageDirectory - # Unrelated build output must not be uploaded, even if it contains a key. - Set-Content (Join-Path $arguments.PackageDirectory 'do-not-publish.pfx') 'not a real key' - & $exporter @arguments - $files = @(Get-ChildItem -LiteralPath $arguments.OutputDirectory -File | Sort-Object Name | Select-Object -ExpandProperty Name) - if (($files -join ',') -ne 'INSTALL.txt,msix-metadata.json,OpenClaw-Dev.cer,OpenClawCompanion-Dev-x64.msix') { - throw "Unexpected Dev artifact contents: $($files -join ',')" - } - $metadata = Get-Content (Join-Path $arguments.OutputDirectory 'msix-metadata.json') -Raw | ConvertFrom-Json - $publicCertificate = [Security.Cryptography.X509Certificates.X509Certificate2]::new( - (Join-Path $arguments.OutputDirectory 'OpenClaw-Dev.cer')) - try { - if ($publicCertificate.HasPrivateKey -or $publicCertificate.Thumbprint -ne $certificate.Thumbprint) { - throw 'The exported certificate is not the expected public-only signer.' + foreach ($architecture in @('x64', 'arm64')) { + $arguments = New-Arguments + $arguments.Architecture = $architecture + New-Package -Directory $arguments.PackageDirectory -Architecture $architecture + # Unrelated build output must not be uploaded, even if it contains a key. + Set-Content (Join-Path $arguments.PackageDirectory 'do-not-publish.pfx') 'not a real key' + & $exporter @arguments + $packageName = "OpenClaw-Dev-$architecture.msix" + $expectedFiles = @('INSTALL.txt', 'msix-metadata.json', 'OpenClaw-Dev.cer', $packageName) + $files = @(Get-ChildItem -LiteralPath $arguments.OutputDirectory -File | Select-Object -ExpandProperty Name) + if (@(Compare-Object $expectedFiles $files).Count -gt 0) { + throw "Unexpected Dev artifact contents: $($files -join ',')" } + $metadata = Get-Content (Join-Path $arguments.OutputDirectory 'msix-metadata.json') -Raw | ConvertFrom-Json + $publicCertificate = [Security.Cryptography.X509Certificates.X509Certificate2]::new( + (Join-Path $arguments.OutputDirectory 'OpenClaw-Dev.cer')) + try { + if ($publicCertificate.HasPrivateKey -or $publicCertificate.Thumbprint -ne $certificate.Thumbprint) { + throw 'The exported certificate is not the expected public-only signer.' + } + } + finally { $publicCertificate.Dispose() } + $actualHash = (Get-FileHash (Join-Path $arguments.OutputDirectory $metadata.archive) -Algorithm SHA256).Hash + if (-not $metadata.signed -or $metadata.signing -ne 'development-only' -or + $metadata.archive -ne $packageName -or $metadata.architecture -ne $architecture -or + $metadata.identityName -ne 'OpenClawFoundation.OpenClaw.Dev' -or + $metadata.packageVersion -ne '2026.7.2.123' -or $metadata.sha256 -ne $actualHash -or + $metadata.certificateThumbprint -ne $certificate.Thumbprint -or $metadata.sourceCommit -notmatch '^[0-9a-f]{40}$') { + throw 'Dev package provenance did not match its inputs.' + } + $instructions = Get-Content (Join-Path $arguments.OutputDirectory 'INSTALL.txt') -Raw + if (-not $instructions.Contains("Add-AppxPackage -Path .\$packageName")) { + throw 'Dev installation instructions did not name the exported package.' + } + Assert-Fails { & $exporter @arguments } 'must be absent or empty' } - finally { $publicCertificate.Dispose() } - $actualHash = (Get-FileHash (Join-Path $arguments.OutputDirectory $metadata.archive) -Algorithm SHA256).Hash - if (-not $metadata.signed -or $metadata.signing -ne 'development-only' -or - $metadata.packageVersion -ne '2026.7.2.123' -or $metadata.sha256 -ne $actualHash -or - $metadata.certificateThumbprint -ne $certificate.Thumbprint -or $metadata.sourceCommit -notmatch '^[0-9a-f]{40}$') { - throw 'Dev package provenance did not match its inputs.' - } - Assert-Fails { & $exporter @arguments } 'must be absent or empty' $arguments = New-Arguments New-Item -ItemType Directory -Path $arguments.PackageDirectory | Out-Null diff --git a/tests/OpenClaw.Tray.Tests/MsixDevelopmentSigningTests.cs b/tests/OpenClaw.Tray.Tests/MsixDevelopmentSigningTests.cs index 2ec9421e3..6a9cd325d 100644 --- a/tests/OpenClaw.Tray.Tests/MsixDevelopmentSigningTests.cs +++ b/tests/OpenClaw.Tray.Tests/MsixDevelopmentSigningTests.cs @@ -94,7 +94,8 @@ public void BuildScript_StoreMsixPathIsUnsignedReleaseIdentityForBothArchitectur // Exactly one package per architecture, deterministically named, with provenance. Assert.Contains("$builtPackages.Count -ne 1", packagingScript); - Assert.Contains("\"OpenClawCompanion-$Architecture.msix\"", packagingScript); + Assert.Contains("\"OpenClaw-$Architecture.msix\"", packagingScript); + Assert.Contains(@"artifacts\msix\$storeArchitecture\OpenClaw-$storeArchitecture.msix", buildScript); Assert.Contains("msix-metadata.json", packagingScript); Assert.Contains("signed = $false", packagingScript); Assert.Contains("sourceTreeDirty = $sourceTreeDirty", packagingScript); diff --git a/tests/OpenClaw.Tray.Tests/ReleaseSigningWorkflowTests.cs b/tests/OpenClaw.Tray.Tests/ReleaseSigningWorkflowTests.cs index 6876d9b98..1c2242294 100644 --- a/tests/OpenClaw.Tray.Tests/ReleaseSigningWorkflowTests.cs +++ b/tests/OpenClaw.Tray.Tests/ReleaseSigningWorkflowTests.cs @@ -137,8 +137,8 @@ public void ReleaseWorkflow_PublishesOnlyUnsignedStoreMsixForAlphaTags() var releaseStep = ExtractReleaseStep(workflow); Assert.Contains("${{ steps.msix_alpha.outputs.files }}", releaseStep); Assert.Contains("${{ steps.msix_alpha.outputs.notes }}", releaseStep); - Assert.DoesNotContain("OpenClawCompanion-x64.msix", releaseStep); - Assert.DoesNotContain("OpenClawCompanion-arm64.msix", releaseStep); + Assert.DoesNotContain("OpenClaw-x64.msix", releaseStep); + Assert.DoesNotContain("OpenClaw-arm64.msix", releaseStep); Assert.DoesNotContain("openclaw-msix-dev-", releaseStep); }