Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
10 changes: 7 additions & 3 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -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\<arch>\OpenClawCompanion-<arch>.msix` alongside an
`artifacts\msix\<arch>\OpenClaw-<arch>.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.

Expand Down Expand Up @@ -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-<arch>.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-<arch>.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
Expand Down
2 changes: 1 addition & 1 deletion build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
6 changes: 3 additions & 3 deletions docs/RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion scripts/Build-StoreMsix.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion scripts/Export-DevMsixArtifact.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
4 changes: 2 additions & 2 deletions scripts/Stage-StoreMsixReleaseAssets.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions scripts/test-ci-workflow-contract.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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."

Expand Down
18 changes: 9 additions & 9 deletions scripts/test-msix-alpha-release.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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]@{
Expand Down Expand Up @@ -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.'
}
Expand All @@ -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'
Expand Down Expand Up @@ -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'
Expand Down
57 changes: 34 additions & 23 deletions scripts/test-msix-ci-artifacts.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion tests/OpenClaw.Tray.Tests/MsixDevelopmentSigningTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions tests/OpenClaw.Tray.Tests/ReleaseSigningWorkflowTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
Loading