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
57 changes: 49 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,9 @@ jobs:
shell: pwsh
run: ./scripts/test-msix-preview-source-version.ps1

- name: Validate Store MSIX alpha release assets
- name: Validate signed Dev MSIX release assets
shell: pwsh
run: ./scripts/test-msix-alpha-release.ps1
run: ./scripts/test-dev-msix-release.ps1

- name: Validate stable correction release ordering regressions
shell: pwsh
Expand Down Expand Up @@ -899,7 +899,7 @@ jobs:
name: MSIX artifacts (${{ matrix.architecture }})
needs: [change-classification, metadata, reserve-msix-version]
if: ${{ !cancelled() && needs.change-classification.result == 'success' && needs.metadata.result == 'success' && (needs.reserve-msix-version.result == 'success' || needs.reserve-msix-version.result == 'skipped') && (needs.change-classification.outputs.x64_release == 'true' || needs.change-classification.outputs.arm64_release == 'true') }}
# Unsigned Store packages may publish to alpha releases only. Dev packages stay workflow-only.
# Dev-signed packages publish on tag releases. Unsigned Store packages stay workflow-only.
runs-on: ${{ matrix.architecture == 'arm64' && 'windows-11-arm' || 'windows-latest' }}
env:
OPENCLAW_BUILD_VERSION: ${{ needs.metadata.outputs.semVer }}
Expand Down Expand Up @@ -1363,32 +1363,73 @@ jobs:
-Tag $env:RELEASE_TAG
-GitHubToken $env:GH_TOKEN

- name: Download Store MSIX release artifacts
- name: Download x64 signed Dev MSIX release artifact
uses: actions/download-artifact@v8
with:
pattern: openclaw-msix-store-unsigned-*
path: artifacts/msix-release
name: openclaw-msix-dev-x64
path: artifacts/msix-release/openclaw-msix-dev-x64

- name: Stage Store MSIX release assets
- name: Download ARM64 signed Dev MSIX release artifact
uses: actions/download-artifact@v8
with:
name: openclaw-msix-dev-arm64
path: artifacts/msix-release/openclaw-msix-dev-arm64

- name: Trust signed Dev MSIX certificates for validation
shell: pwsh
run: |
$storePath = 'Cert:\CurrentUser\TrustedPeople'
$thumbprintsPath = "$env:RUNNER_TEMP\openclaw-dev-msix-imported-certs.txt"
[IO.File]::WriteAllText($thumbprintsPath, '')
foreach ($architecture in @('x64', 'arm64')) {
$certificatePath = "artifacts\msix-release\openclaw-msix-dev-$architecture\OpenClaw-Dev.cer"
$certificate = [Security.Cryptography.X509Certificates.X509Certificate2]::new($certificatePath)
try {
$existing = @(Get-ChildItem -LiteralPath $storePath | Where-Object Thumbprint -EQ $certificate.Thumbprint)
if ($existing.Count -eq 0) {
Import-Certificate -FilePath $certificatePath -CertStoreLocation $storePath | Out-Null
Add-Content -LiteralPath $thumbprintsPath -Value $certificate.Thumbprint
}
}
finally { $certificate.Dispose() }
}

- name: Stage signed Dev MSIX release assets
id: msix_release
shell: pwsh
env:
RELEASE_VERSION: ${{ needs.metadata.outputs.semVer }}
MSIX_VERSION_INFO: ${{ needs.reserve-msix-version.outputs.versionInfo }}
DEV_MSIX_REVISION: ${{ github.run_number }}
run: |
if ([string]::IsNullOrWhiteSpace($env:MSIX_VERSION_INFO)) {
throw 'Missing official MSIX reservation.'
}
$env:MSIX_VERSION_INFO | Set-Content -LiteralPath "$env:RUNNER_TEMP\openclaw-msix-version.json" -Encoding utf8
$assets = .\scripts\Stage-StoreMsixReleaseAssets.ps1 `
$assets = .\scripts\Stage-DevMsixReleaseAssets.ps1 `
-ArtifactDirectory 'artifacts\msix-release' `
-OutputDirectory 'msix-release' `
-Version $env:RELEASE_VERSION `
-ExpectedRevision $env:DEV_MSIX_REVISION `
-ExpectedWorkflowRunId $env:GITHUB_RUN_ID `
-VersionInfoPath "$env:RUNNER_TEMP\openclaw-msix-version.json" `
-ExpectedSourceCommit $env:GITHUB_SHA
@('files<<MSIX_RELEASE_FILES'; $assets.Files; 'MSIX_RELEASE_FILES') >> $env:GITHUB_OUTPUT
@('notes<<MSIX_RELEASE_NOTES'; $assets.Notes; 'MSIX_RELEASE_NOTES') >> $env:GITHUB_OUTPUT

- name: Remove trusted Dev MSIX certificates
if: ${{ always() }}
shell: pwsh
run: |
$thumbprintsPath = "$env:RUNNER_TEMP\openclaw-dev-msix-imported-certs.txt"
if (Test-Path -LiteralPath $thumbprintsPath) {
foreach ($thumbprint in @(Get-Content -LiteralPath $thumbprintsPath)) {
Get-ChildItem -LiteralPath 'Cert:\CurrentUser\TrustedPeople' |
Where-Object Thumbprint -EQ $thumbprint |
Remove-Item -Force
}
}

- name: Create Release
uses: softprops/action-gh-release@v3
with:
Expand Down
16 changes: 8 additions & 8 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -442,12 +442,12 @@ Ordinary local builds that omit these options preserve their previous
unallocated version calculation. Store distribution remains gated by #1375;
this allocator does not submit packages to Partner Center.

Canonical `vX.Y.Z-alpha.N` releases also attach the **unsigned Store** MSIX
files and architecture-specific metadata, for manual upload to Partner Center.
They do not attach the Dev-signed packages or certificates. These public
pre-releases are not Latest and are not hidden from GitHub's Releases list.
Stable releases retain only the existing EXE/ZIP downloads and do not mention
MSIX submission assets in their generated download notes.
Every canonical stable, correction, and prerelease tag attaches
`OpenClaw-Dev-x64.zip` and `OpenClaw-Dev-arm64.zip`. Each archive contains the
signed Dev MSIX, matching public certificate, provenance metadata, and
installation instructions. Unsigned Store MSIX packages and the bundle remain
Actions artifacts only for manual upload to Partner Center. Public
prereleases are not Latest and are not hidden from GitHub's Releases list.

To request a new alpha from current `main`, manually run **Daily Alpha
Release**. Its existing checks choose the GitVersion alpha tag, skip a commit
Expand All @@ -457,8 +457,8 @@ tag. It does not release the feature branch selected in the UI. Running
only. See [manual alpha releases](docs/RELEASING.md#manual-alpha-releases).

Store distribution remains paused. This workflow neither submits to Partner
Center nor retrieves or publishes Store-signed packages. An alpha release
label does not change the Store package version or make the package installable.
Center nor retrieves or publishes Store-signed packages. The published Dev
packages require explicit trust of their included development certificate.

#### The Store package alongside an existing Inno install

Expand Down
62 changes: 33 additions & 29 deletions docs/RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ infrastructure changes. Those fail-closed pull requests run the x64 publish
smoke only; ARM64 portable publish remains required on `main` and tags.
When either release-build lane is selected, CI also builds both architectures
of Dev-signed and unsigned Store MSIX **workflow artifacts**. CI Gate requires
that MSIX job to succeed. Every tag release also attaches the unsigned Store
MSIX bundle, standalone packages, and metadata for manual Partner Center
submission. Dev-signed packages stay in Actions.
that MSIX job to succeed. Every tag release also attaches the signed Dev MSIX
packages with their matching public certificates, provenance metadata, and
installation instructions. Unsigned Store packages stay in Actions for manual
Partner Center submission.

## Inno-to-Store migration foundation

Expand Down Expand Up @@ -395,7 +396,7 @@ migration proof.
"Verify Release Binary Signing Policy", `
"OpenClaw.Tray.WinUI.exe", `
"build-msix:", `
"Stage Store MSIX release assets"
"Stage signed Dev MSIX release assets"
```

3. Create a new stable, stable correction, or prerelease tag from `origin/main`.
Expand Down Expand Up @@ -458,9 +459,9 @@ Stable, stable-correction, and alpha tags use the same signed CI release pipelin
numeric-suffix tag, including malformed ones such as `-0` and `-03`, is routed
through the validator rather than silently classified by GitVersion.
- `vX.Y.Z-alpha.N` creates a prerelease that stable updater checks do not offer.
It also includes unsigned x64/ARM64 Store submission MSIX files and their
metadata, not Dev-signed installers. The pre-release is visible on GitHub's
Releases page but is not promoted as Latest.
Like every canonical tag, it includes signed x64/ARM64 Dev MSIX ZIPs with
their matching public certificates and instructions. The pre-release is
visible on GitHub's Releases page but is not promoted as Latest.
The daily workflow evaluates the default branch at 2:00 PM Pacific, skips a
head already represented by a published release, and defers while an
unpublished non-alpha tag points at the head. After each successful alpha
Expand Down Expand Up @@ -533,22 +534,19 @@ Current release artifacts are:

Every stable, correction, and prerelease additionally contains:

- `OpenClaw.msixbundle` (recommended Partner Center submission input)
- `OpenClaw-x64.msix` and `OpenClaw-arm64.msix`
- `OpenClaw-x64.msix-metadata.json` and
`OpenClaw-arm64.msix-metadata.json`
- `OpenClaw-Dev-x64.zip`
- `OpenClaw-Dev-arm64.zip`

These are **unsigned Store submission inputs, not installers**. Upload the
bundle to Partner Center for one architecture-selecting submission. The
standalone packages remain available for inspection or fallback. Microsoft
signs accepted Store submissions. The release step checks both
architectures' clean source provenance, identity, version, and package hashes,
then proves that the bundle embeds those exact bytes. It fails rather than
publishing a partial or mismatched set.
Each archive contains the signed Dev-identity MSIX, its matching public
certificate, `msix-metadata.json`, and `INSTALL.txt`. These packages are
development-signed for sideloading, not Microsoft Store-signed. Testers must
extract the matching architecture archive and follow its instructions,
including explicitly trusting the included public certificate.

Dev-signed tester MSIX packages, public certificates, and instructions remain
Actions artifacts only. No production signing step is applied to the unsigned
Store packages.
Unsigned Store submission packages, the multi-architecture bundle, and their
metadata remain Actions artifacts only. Upload the unsigned bundle to Partner
Center for one architecture-selecting submission. No production signing step
is applied to those Store inputs.

Store distribution remains paused: automatic Partner Center submission,
Store-signed retrieval and publication, and official lifecycle acceptance
Expand Down Expand Up @@ -625,14 +623,16 @@ Preview candidates never reserve a number and can change between reruns;
they must not be treated as official Store submissions.

The release stager requires `-VersionInfoPath` for the exact reserved result. It
checks the app version, source commit, reserved allocation, package
version, and both architectures' metadata before copying any assets:
checks the app version, source commit, workflow run, reserved allocation,
package and certificate hashes, Dev identity, signature presence, version, and
both architectures' metadata before creating any release ZIPs:

```powershell
.\scripts\Stage-StoreMsixReleaseAssets.ps1 `
.\scripts\Stage-DevMsixReleaseAssets.ps1 `
-ArtifactDirectory 'artifacts\msix-release' `
-OutputDirectory 'msix-release' `
-Version $appVersion -ExpectedSourceCommit $sourceCommit `
-ExpectedRevision $runNumber -ExpectedWorkflowRunId $runId `
-VersionInfoPath $reservedVersionInfoPath
```

Expand Down Expand Up @@ -768,8 +768,9 @@ validation. Release tags cannot enter the `release` job until **CI Gate**
confirms classification, fast validation, tests, E2E, and release builds all
succeeded. The `build-msix` and `build-msix-bundle` jobs must also succeed
whenever release metadata is required. The release job downloads and attaches
its unsigned Store bundle, standalone packages, and metadata for every
canonical tag. Dev tester distribution stays workflow-only.
its signed x64 and ARM64 Dev packages, public certificates, metadata, and
instructions for every canonical tag. Unsigned Store distribution stays
workflow-only.

The release job should:

Expand All @@ -780,10 +781,10 @@ The release job should:
5. Create the portable x64 and ARM64 ZIPs.
6. Build Inno installers.
7. Sign installers.
8. Stage the validated unsigned Store MSIX
bundle, standalone packages, and metadata.
8. Stage the validated signed Dev MSIX packages and their public-only support
files into architecture-specific ZIPs.
9. Create a GitHub release whose prerelease flag matches the tag, with installer
and portable ZIP assets plus the Store submission assets.
and portable ZIP assets plus the signed Dev MSIX ZIPs.

## Post-release verification

Expand All @@ -801,6 +802,9 @@ Expected:
- Stable tags: `isPrerelease` is `false`.
- Alpha tags: `isPrerelease` is `true` and `isLatest` is `false`.
- Installer EXEs are signed.
- `OpenClaw-Dev-x64.zip` and `OpenClaw-Dev-arm64.zip` are present, and each
contains the signed MSIX, matching public certificate, metadata, and
`INSTALL.txt`.
- In ZIP payload:
- `OpenClaw.Tray.WinUI.exe` is OpenClaw-signed.
- All listed OpenClaw-owned DLLs are OpenClaw-signed.
Expand Down
9 changes: 5 additions & 4 deletions scripts/Export-DevMsixArtifact.ps1
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<#
.SYNOPSIS
Validates a signed Dev MSIX and stages a public-only CI tester download.
Validates a signed Dev MSIX and stages a public tester download.
.DESCRIPTION
Requires exactly one package from the current build. Verifies its Dev
identity, architecture, version, and trusted signature before exporting
Expand Down Expand Up @@ -124,7 +124,7 @@ $certificateHash = (Get-FileHash -LiteralPath $certificatePath -Algorithm SHA256

$allocationNote = if ($versionInfo) { $versionInfo.allocation } else { 'unallocated local build' }
@"
OpenClaw (Dev) CI tester package ($Architecture)
OpenClaw (Dev) signed tester package ($Architecture)

This is NOT a Microsoft Store-signed release. Install only from a workflow
and source revision you trust. Pull request builds can contain unreviewed code.
Expand Down Expand Up @@ -168,7 +168,8 @@ installed package still relies on it:
Remove-Item -LiteralPath 'Cert:\LocalMachine\TrustedPeople\$($certificate.Thumbprint)'

Store submission artifacts are separate unsigned CI downloads, not installers.
Unsigned Store packages may also appear on alpha GitHub pre-releases for
Partner Center submission. This signed Dev tester package stays workflow-only.
Tagged releases publish this signed Dev tester package with its matching public
certificate and instructions. Unsigned Store packages stay workflow-only for
Partner Center submission.
"@ | Set-Content -LiteralPath (Join-Path $OutputDirectory 'INSTALL.txt') -Encoding utf8
Write-Host "Staged signed Dev tester artifact: $OutputDirectory"
Loading
Loading