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
148 changes: 96 additions & 52 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,14 @@ jobs:
shell: pwsh
run: ./scripts/test-ci-workflow-contract.ps1

- name: Validate MSIX CI artifacts
shell: pwsh
run: ./scripts/test-msix-ci-artifacts.ps1

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

- name: Validate stable correction release ordering regressions
shell: pwsh
run: ./scripts/test-stable-correction-release-validator.ps1
Expand Down Expand Up @@ -154,6 +162,7 @@ jobs:
majorMinorPatch: ${{ steps.release_version.outputs.majorMinorPatch }}
isPrerelease: ${{ steps.release_version.outputs.isPrerelease }}
isStableCorrection: ${{ steps.release_version.outputs.isStableCorrection }}
isMsixAlpha: ${{ steps.release_version.outputs.isMsixAlpha }}
steps:
- uses: actions/checkout@v7
with:
Expand Down Expand Up @@ -212,6 +221,8 @@ jobs:
"majorMinorPatch=$majorMinorPatch" >> $env:GITHUB_OUTPUT
"isPrerelease=$($isPrerelease.ToString().ToLowerInvariant())" >> $env:GITHUB_OUTPUT
"isStableCorrection=$($isStableCorrection.ToString().ToLowerInvariant())" >> $env:GITHUB_OUTPUT
$isMsixAlpha = $isPrerelease -and ($env:GITHUB_REF -cmatch '^refs/tags/v(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)-alpha\.(?:0|[1-9]\d*)$')
"isMsixAlpha=$($isMsixAlpha.ToString().ToLowerInvariant())" >> $env:GITHUB_OUTPUT

core-tests:
name: Core and CLI tests
Expand Down Expand Up @@ -808,37 +819,28 @@ jobs:
path: publish/

build-msix:
needs: [metadata]
if: false # MSIX distribution is paused; ship Inno setup and portable ZIP artifacts only.
runs-on: ${{ matrix.rid == 'win-arm64' && 'windows-11-arm' || 'windows-latest' }}
continue-on-error: true
name: MSIX artifacts (${{ matrix.architecture }})
needs: [change-classification, metadata]
if: ${{ !cancelled() && needs.change-classification.result == 'success' && needs.metadata.result == 'success' && (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.
runs-on: ${{ matrix.architecture == 'arm64' && 'windows-11-arm' || 'windows-latest' }}
env:
OPENCLAW_BUILD_VERSION: ${{ needs.metadata.outputs.semVer }}
DEV_MSIX_REVISION: ${{ github.run_number }}
strategy:
fail-fast: false
matrix:
rid: [win-x64, win-arm64]
include:
- rid: win-x64
platform: x64
- rid: win-arm64
platform: ARM64
architecture: [x64, arm64]

steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0

- name: Setup .NET 10 for VS MSBuild
- name: Setup .NET from global.json
uses: actions/setup-dotnet@v6
with:
dotnet-version: 10.0.100

- name: Pin .NET SDK for MSIX packaging
shell: pwsh
run: |
$globalJson = Get-Content global.json -Raw | ConvertFrom-Json
$globalJson.sdk.rollForward = "disable"
$globalJson | ConvertTo-Json -Depth 5 | Set-Content global.json
dotnet --version
global-json-file: global.json

- name: Cache NuGet packages
continue-on-error: true
Expand All @@ -848,48 +850,62 @@ jobs:
key: nuget-${{ runner.os }}-${{ hashFiles('**/*.csproj', '**/Directory.Packages.props') }}
restore-keys: nuget-${{ runner.os }}-

- name: Setup MSBuild
uses: microsoft/setup-msbuild@v3
- name: Build and validate unsigned Store MSIX
shell: pwsh
run: .\scripts\Build-StoreMsix.ps1 -Architecture ${{ matrix.architecture }}

- name: Upload unsigned Store submission artifact
uses: actions/upload-artifact@v7
with:
name: openclaw-msix-store-unsigned-${{ matrix.architecture }}
path: |
artifacts/msix/${{ matrix.architecture }}/OpenClawCompanion-${{ matrix.architecture }}.msix
artifacts/msix/${{ matrix.architecture }}/msix-metadata.json
if-no-files-found: error

- name: Restore
run: dotnet restore src/OpenClaw.Tray.WinUI -r ${{ matrix.rid }}
- name: Provision disposable Dev MSIX certificate
shell: pwsh
run: .\scripts\setup-dev-msix-cert.ps1

- name: Build MSIX Package
- name: Build signed Dev MSIX
shell: pwsh
run: >
msbuild src/OpenClaw.Tray.WinUI/OpenClaw.Tray.WinUI.csproj
/p:Configuration=Release
/p:RuntimeIdentifier=${{ matrix.rid }}
/p:Platform=${{ matrix.platform }}
/p:PackageMsix=true
/p:GenerateAppxPackageOnBuild=true
/p:AppxPackageSigningEnabled=false
/p:AppxBundle=Never
/p:UapAppxPackageBuildMode=SideloadOnly
/p:AppxPackageDir=AppPackages\

- name: Find MSIX Package
id: find-msix
.\build.ps1 -Project WinUI -Configuration Release -Msix Dev
-MsixRevision $env:DEV_MSIX_REVISION
-MsixOutputDirectory "$env:RUNNER_TEMP\openclaw-dev-appx"

- name: Validate and stage Dev tester artifact
shell: pwsh
run: |
$msix = Get-ChildItem -Path src/OpenClaw.Tray.WinUI/AppPackages -Recurse -Filter "*.msix" -ErrorAction SilentlyContinue | Select-Object -First 1
if (-not $msix) {
Write-Error "No MSIX package found in AppPackages directory"
exit 1
}
Write-Host "Found: $($msix.FullName)"
echo "msix_path=$($msix.FullName)" >> $env:GITHUB_OUTPUT
echo "msix_name=$($msix.Name)" >> $env:GITHUB_OUTPUT

- name: Upload MSIX Artifact
$thumbprint = (Get-Content "$env:LOCALAPPDATA\OpenClawDevelopment\MSIX\dev-msix-thumbprint.txt" -Raw).Trim()
.\scripts\Export-DevMsixArtifact.ps1 `
-Architecture ${{ matrix.architecture }} `
-PackageDirectory "$env:RUNNER_TEMP\openclaw-dev-appx" `
-ExpectedRevision $env:DEV_MSIX_REVISION `
-ExpectedVersion $env:OPENCLAW_BUILD_VERSION `
-CertificateThumbprint $thumbprint `
-OutputDirectory "artifacts\msix-dev\${{ matrix.architecture }}"

- name: Upload Dev tester artifact
uses: actions/upload-artifact@v7
with:
name: openclaw-msix-${{ matrix.rid }}
path: ${{ steps.find-msix.outputs.msix_path }}
name: openclaw-msix-dev-${{ matrix.architecture }}
path: |
artifacts/msix-dev/${{ matrix.architecture }}/OpenClawCompanion-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
if-no-files-found: error

- name: Remove disposable Dev MSIX certificate
if: ${{ always() }}
shell: pwsh
run: .\scripts\setup-dev-msix-cert.ps1 -Remove

ci-gate:
name: CI Gate
if: ${{ always() }}
needs: [change-classification, fast-validation, proof-pool-contracts, metadata, core-tests, tray-tests, ui-tests, setup-e2e, revocation-e2e, network-e2e, build-x64, build-arm64]
needs: [change-classification, fast-validation, proof-pool-contracts, metadata, core-tests, tray-tests, ui-tests, setup-e2e, revocation-e2e, network-e2e, build-x64, build-arm64, build-msix]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
Expand Down Expand Up @@ -919,6 +935,7 @@ jobs:
ARM64_RELEASE_REQUIRED: ${{ needs.change-classification.outputs.arm64_release }}
ARM64_RELEASE_RESULT: ${{ needs.build-arm64.result }}
METADATA_RESULT: ${{ needs.metadata.result }}
MSIX_RESULT: ${{ needs.build-msix.result }}
run: |
$validatedMode = ./scripts/Assert-CiGateResults.ps1 `
-ClassificationResult $env:CLASSIFICATION_RESULT `
Expand All @@ -942,7 +959,8 @@ jobs:
-X64ReleaseResult $env:X64_RELEASE_RESULT `
-Arm64ReleaseRequired $env:ARM64_RELEASE_REQUIRED `
-Arm64ReleaseResult $env:ARM64_RELEASE_RESULT `
-MetadataResult $env:METADATA_RESULT
-MetadataResult $env:METADATA_RESULT `
-MsixResult $env:MSIX_RESULT
"CI Gate passed $validatedMode validation." >> $env:GITHUB_STEP_SUMMARY

release:
Expand Down Expand Up @@ -1150,6 +1168,28 @@ jobs:
-Tag $env:RELEASE_TAG
-GitHubToken $env:GH_TOKEN

- name: Download alpha Store MSIX artifacts
if: needs.metadata.outputs.isMsixAlpha == 'true'
uses: actions/download-artifact@v8
with:
pattern: openclaw-msix-store-unsigned-*
path: artifacts/msix-alpha

- name: Stage alpha Store MSIX release assets
if: needs.metadata.outputs.isMsixAlpha == 'true'
id: msix_alpha
shell: pwsh
env:
RELEASE_VERSION: ${{ needs.metadata.outputs.semVer }}
run: |
$assets = .\scripts\Stage-StoreMsixReleaseAssets.ps1 `
-ArtifactDirectory 'artifacts\msix-alpha' `
-OutputDirectory 'msix-alpha-release' `
-Version $env:RELEASE_VERSION `
-ExpectedSourceCommit $env:GITHUB_SHA
@('files<<MSIX_ALPHA_FILES'; $assets.Files; 'MSIX_ALPHA_FILES') >> $env:GITHUB_OUTPUT
@('notes<<MSIX_ALPHA_NOTES'; $assets.Notes; 'MSIX_ALPHA_NOTES') >> $env:GITHUB_OUTPUT

- name: Create Release
uses: softprops/action-gh-release@v3
with:
Expand All @@ -1159,6 +1199,8 @@ jobs:
Output/OpenClawCompanion-Setup-arm64.exe
OpenClawTray-${{ needs.metadata.outputs.semVer }}-win-x64.zip
OpenClawTray-${{ needs.metadata.outputs.semVer }}-win-arm64.zip
${{ steps.msix_alpha.outputs.files }}
fail_on_unmatched_files: true
prerelease: ${{ needs.metadata.outputs.isPrerelease }}
make_latest: ${{ needs.metadata.outputs.isPrerelease == 'true' && 'false' || 'true' }}
body: |
Expand All @@ -1170,6 +1212,8 @@ jobs:
- **Portable x64**: `OpenClawTray-${{ needs.metadata.outputs.semVer }}-win-x64.zip`
- **Portable ARM64**: `OpenClawTray-${{ needs.metadata.outputs.semVer }}-win-arm64.zip`

${{ steps.msix_alpha.outputs.notes }}

### Features
- 🦞 System tray integration with gateway status
- 🔄 Auto-updates from GitHub Releases
Expand Down
11 changes: 11 additions & 0 deletions .github/workflows/daily-alpha-release.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: Daily Alpha Release

on:
workflow_dispatch:
schedule:
- cron: '0 21 * * *'
- cron: '0 22 * * *'
Expand All @@ -21,10 +22,17 @@ jobs:
id: pacific_schedule
shell: bash
env:
EVENT_NAME: ${{ github.event_name }}
SCHEDULE: ${{ github.event.schedule }}
run: |
set -euo pipefail

if [[ "$EVENT_NAME" == "workflow_dispatch" ]]; then
echo "run=true" >> "$GITHUB_OUTPUT"
echo "Manually checking the default branch for a new alpha release."
exit 0
fi

pacific_offset="$(TZ=America/Los_Angeles date +%z)"
case "$pacific_offset" in
-0700) expected_schedule='0 21 * * *' ;;
Expand Down Expand Up @@ -117,6 +125,9 @@ jobs:
if: steps.pacific_schedule.outputs.run == 'true' && steps.previous_release.outputs.changed == 'true'
id: gitversion
uses: gittools/actions/gitversion/execute@7417b1089e2c7de93510f1901d656ddf60bb024f # v4.7.0
with:
# Checkout already selected the full default branch. Ignore a manual dispatch's source ref.
disableNormalization: true

- name: Create or reuse alpha tag
if: steps.pacific_schedule.outputs.run == 'true' && steps.previous_release.outputs.changed == 'true'
Expand Down
64 changes: 63 additions & 1 deletion DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ is what lets a packaged smoke test run without disturbing a working install:
| Publisher | local development certificate | Partner Center |
| Protocol | `openclaw-dev` | `openclaw` |
| Signing | signed locally | unsigned; the Store signs |
| Version revision | installed revision + 1 | pinned to `0` |
| Version revision | installed revision + 1 locally; explicit CI run number | pinned to `0` |
| Architectures | host only | x64 and ARM64 |

The revision field is the clearest reason the modes cannot merge, because each
Expand Down Expand Up @@ -318,6 +318,68 @@ Generating the optional `.appxsym` symbol package additionally requires
`mspdbcmf.exe` from the Visual Studio **Desktop development with C++** workload;
without it the build logs a warning and skips symbols.

#### CI MSIX downloads

The **Build and Test** workflow builds both x64 and ARM64 MSIX variants whenever
the change classifier selects a release-build lane. This includes packaging,
build, and workflow PRs, pushes to `main`/`master`, tags, and manual workflow
dispatches. Ordinary targeted or documentation-only PRs intentionally skip them.
MSIX failures block **CI Gate** when selected; a skipped unselected job is valid.

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. |

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
runner trust are removed in an always-run cleanup step. No repository signing
secret or production release-signing environment is used. Each architecture
and later workflow run can have a different certificate; testers must trust
the matching signer explicitly. Only install packages from a workflow/source
you trust, especially when testing unreviewed PR code.

Extract the Dev artifact and follow `INSTALL.txt`: verify package/certificate
hashes, install the architecture-matched VCLibs dependency described above,
import the public certificate into `LocalMachine\TrustedPeople` from elevated
PowerShell, then install the package as the intended user. This uses the
existing **OpenClaw (Dev)** identity and can upgrade a locally installed Dev
package; it is not a new independent test identity.

CI passes `-MsixRevision $env:GITHUB_RUN_NUMBER` to the existing
`build.ps1 -Project WinUI -Configuration Release -Msix Dev` path, with a fresh
`-MsixOutputDirectory`. Explicit revisions must be 1-65535; overflow fails
instead of wrapping. Omitting these options preserves local build behavior.
The same run's reruns keep the same version, not a new upgrade. Version
ordering is not guaranteed across forks, branches, local builds, or decreasing
base versions. Do not uninstall/downgrade an existing Dev package just to
resolve a version conflict without considering its settings and data.

The Store version stays `X.Y.Z.0`. Prerelease and stable-correction suffixes
can therefore produce the same Store version; CI artifacts do not promise
unique Store submissions for every tag. Store submission version allocation
must be resolved before distribution is enabled in #1375.

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.

To request a new alpha from current `main`, manually run **Daily Alpha
Release**. Its existing checks choose the GitVersion alpha tag, skip a commit
that already has a published release, and dispatch **Build and Test** on the
tag. It does not release the feature branch selected in the UI. Running
**Build and Test** directly on a branch still produces workflow artifacts
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.

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

The Store package and the Inno installer produce the same application. Both can
Expand Down
Loading
Loading