Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11), free to diverge. Tree copied sans upstream .git; upstream remote added for future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19. Preserves docs/ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
286 lines
12 KiB
YAML
286 lines
12 KiB
YAML
name: Windows Node Release
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
tag:
|
|
description: Existing OpenClaw release tag to receive Windows Hub installers, for example v2026.6.1
|
|
required: true
|
|
type: string
|
|
windows_node_tag:
|
|
description: Exact openclaw-windows-node release tag to promote, for example v0.6.3
|
|
required: true
|
|
type: string
|
|
expected_installer_digests:
|
|
description: Compact JSON map of installer asset names to pinned source sha256 digests
|
|
required: true
|
|
type: string
|
|
|
|
permissions:
|
|
contents: write
|
|
|
|
concurrency:
|
|
group: windows-node-release-${{ inputs.tag }}
|
|
cancel-in-progress: false
|
|
|
|
jobs:
|
|
promote_signed_windows_installers:
|
|
name: Promote signed Windows installers
|
|
runs-on: windows-latest
|
|
timeout-minutes: 30
|
|
steps:
|
|
- name: Validate inputs
|
|
shell: pwsh
|
|
env:
|
|
RELEASE_TAG: ${{ inputs.tag }}
|
|
WINDOWS_NODE_TAG: ${{ inputs.windows_node_tag }}
|
|
EXPECTED_INSTALLER_DIGESTS: ${{ inputs.expected_installer_digests }}
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
if ($env:RELEASE_TAG -notmatch '^v[0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*((-(alpha|beta)\.[1-9][0-9]*)|(-[1-9][0-9]*))?$') {
|
|
throw "Invalid OpenClaw release tag: $env:RELEASE_TAG"
|
|
}
|
|
$stableRelease = -not (
|
|
$env:RELEASE_TAG.Contains("-alpha.") -or
|
|
$env:RELEASE_TAG.Contains("-beta.")
|
|
)
|
|
if ($env:WINDOWS_NODE_TAG -notmatch '^v[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?$') {
|
|
throw "windows_node_tag must be an explicit openclaw-windows-node release tag, not latest: $env:WINDOWS_NODE_TAG"
|
|
}
|
|
|
|
try {
|
|
$expectedDigests = $env:EXPECTED_INSTALLER_DIGESTS | ConvertFrom-Json -AsHashtable
|
|
} catch {
|
|
throw "expected_installer_digests must be a JSON object: $_"
|
|
}
|
|
# Add future signed installer names, such as MSIX x64/ARM64, here.
|
|
$requiredInstallerNames = @(
|
|
"OpenClawCompanion-Setup-x64.exe",
|
|
"OpenClawCompanion-Setup-arm64.exe"
|
|
)
|
|
$allowedTargetCompanionAssetNames = @(
|
|
$requiredInstallerNames
|
|
"OpenClawCompanion-SHA256SUMS.txt"
|
|
)
|
|
if ($expectedDigests.Count -ne $requiredInstallerNames.Count) {
|
|
throw "expected_installer_digests must contain exactly the current installer asset contract."
|
|
}
|
|
foreach ($name in $requiredInstallerNames) {
|
|
$digest = [string]$expectedDigests[$name]
|
|
if ($digest -notmatch '^sha256:[A-Fa-f0-9]{64}$') {
|
|
throw "expected_installer_digests is missing a valid pinned digest for $name."
|
|
}
|
|
}
|
|
|
|
$targetRelease = gh release view $env:RELEASE_TAG --repo $env:GITHUB_REPOSITORY --json tagName,isDraft,isPrerelease,assets,url | ConvertFrom-Json
|
|
if ($targetRelease.tagName -ne $env:RELEASE_TAG) {
|
|
throw "OpenClaw release tag mismatch: expected $env:RELEASE_TAG, got $($targetRelease.tagName)"
|
|
}
|
|
$unexpectedTargetCompanionAssets = @(
|
|
$targetRelease.assets |
|
|
Where-Object {
|
|
$_.name.StartsWith("OpenClawCompanion-") -and
|
|
$_.name -notin $allowedTargetCompanionAssetNames
|
|
} |
|
|
ForEach-Object name |
|
|
Sort-Object
|
|
)
|
|
if ($unexpectedTargetCompanionAssets.Count -ne 0) {
|
|
throw "Target OpenClaw release contains unexpected OpenClawCompanion assets before upload: $($unexpectedTargetCompanionAssets -join ', ')"
|
|
}
|
|
|
|
$sourceRelease = gh release view $env:WINDOWS_NODE_TAG --repo openclaw/openclaw-windows-node --json tagName,isDraft,isPrerelease,assets,url | ConvertFrom-Json
|
|
if ($sourceRelease.tagName -ne $env:WINDOWS_NODE_TAG) {
|
|
throw "Windows source release tag mismatch: expected $env:WINDOWS_NODE_TAG, got $($sourceRelease.tagName)"
|
|
}
|
|
if ($sourceRelease.isDraft) {
|
|
throw "Windows source release must be published: $($sourceRelease.url)"
|
|
}
|
|
if ($stableRelease -and $sourceRelease.isPrerelease) {
|
|
throw "Stable OpenClaw releases require a non-prerelease Windows source release: $($sourceRelease.url)"
|
|
}
|
|
foreach ($name in $requiredInstallerNames) {
|
|
$sourceAssets = @($sourceRelease.assets | Where-Object name -eq $name)
|
|
if ($sourceAssets.Count -ne 1) {
|
|
throw "Windows source release must contain exactly one required asset $name; found $($sourceAssets.Count)."
|
|
}
|
|
if ([string]$sourceAssets[0].digest -ne [string]$expectedDigests[$name]) {
|
|
throw "Windows source release asset digest does not match the pinned digest: $name"
|
|
}
|
|
}
|
|
|
|
- name: Download Windows Hub release installers
|
|
shell: pwsh
|
|
env:
|
|
WINDOWS_NODE_TAG: ${{ inputs.windows_node_tag }}
|
|
EXPECTED_INSTALLER_DIGESTS: ${{ inputs.expected_installer_digests }}
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
New-Item -ItemType Directory -Force -Path dist | Out-Null
|
|
# Add future signed installer patterns, such as MSIX x64/ARM64, here.
|
|
# Every matched installer is signature-checked, checksummed, and promoted.
|
|
$installerPatterns = @(
|
|
"OpenClawCompanion-Setup-x64.exe",
|
|
"OpenClawCompanion-Setup-arm64.exe"
|
|
)
|
|
$downloadArgs = @(
|
|
$env:WINDOWS_NODE_TAG,
|
|
"--repo", "openclaw/openclaw-windows-node",
|
|
"--dir", "dist"
|
|
)
|
|
foreach ($pattern in $installerPatterns) {
|
|
$downloadArgs += @("--pattern", $pattern)
|
|
}
|
|
gh release download @downloadArgs
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "Failed to download Windows release assets from $env:WINDOWS_NODE_TAG."
|
|
}
|
|
|
|
foreach ($pattern in $installerPatterns) {
|
|
$patternMatches = @(Get-ChildItem -LiteralPath dist -File | Where-Object Name -Like $pattern)
|
|
if ($patternMatches.Count -ne 1) {
|
|
throw "Expected exactly one Windows installer matching '$pattern', found $($patternMatches.Count)."
|
|
}
|
|
}
|
|
|
|
$expectedDigests = $env:EXPECTED_INSTALLER_DIGESTS | ConvertFrom-Json -AsHashtable
|
|
foreach ($file in Get-ChildItem -LiteralPath dist -File) {
|
|
$expectedHash = ([string]$expectedDigests[$file.Name]) -replace '^sha256:', ''
|
|
$actualHash = (Get-FileHash -Algorithm SHA256 -LiteralPath $file.FullName).Hash
|
|
if ($actualHash -ne $expectedHash) {
|
|
throw "Downloaded Windows source asset does not match pinned digest: $($file.Name)"
|
|
}
|
|
}
|
|
|
|
- name: Verify Authenticode signatures
|
|
shell: pwsh
|
|
run: |
|
|
$expectedSignerSubject = "CN=OpenClaw Foundation, O=OpenClaw Foundation, L=Mill Valley, S=California, C=US"
|
|
Get-ChildItem -LiteralPath dist -File | ForEach-Object {
|
|
$signature = Get-AuthenticodeSignature -LiteralPath $_.FullName
|
|
if ($signature.Status -ne "Valid") {
|
|
throw "$($_.Name) Authenticode signature was $($signature.Status)."
|
|
}
|
|
if (-not $signature.SignerCertificate) {
|
|
throw "$($_.Name) has no signer certificate."
|
|
}
|
|
if ($signature.SignerCertificate.Subject -ne $expectedSignerSubject) {
|
|
throw "$($_.Name) has unexpected signer subject $($signature.SignerCertificate.Subject)."
|
|
}
|
|
[pscustomobject]@{
|
|
File = $_.Name
|
|
Signer = $signature.SignerCertificate.Subject
|
|
Thumbprint = $signature.SignerCertificate.Thumbprint
|
|
} | Format-List
|
|
}
|
|
|
|
- name: Write SHA-256 manifest
|
|
shell: pwsh
|
|
run: |
|
|
Get-ChildItem -LiteralPath dist -File |
|
|
Sort-Object Name |
|
|
ForEach-Object {
|
|
$hash = Get-FileHash -Algorithm SHA256 -LiteralPath $_.FullName
|
|
"$($hash.Hash.ToLowerInvariant()) $($_.Name)"
|
|
} | Set-Content -Encoding utf8NoBOM -Path dist/OpenClawCompanion-SHA256SUMS.txt
|
|
|
|
- name: Upload to OpenClaw release
|
|
shell: pwsh
|
|
env:
|
|
RELEASE_TAG: ${{ inputs.tag }}
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
$releaseAssets = @(Get-ChildItem -LiteralPath dist -File | Sort-Object Name | ForEach-Object FullName)
|
|
gh release upload $env:RELEASE_TAG @releaseAssets --repo $env:GITHUB_REPOSITORY --clobber
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "Failed to upload Windows release assets to $env:RELEASE_TAG."
|
|
}
|
|
|
|
- name: Verify promoted release asset contract
|
|
shell: pwsh
|
|
env:
|
|
RELEASE_TAG: ${{ inputs.tag }}
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
New-Item -ItemType Directory -Force -Path verified | Out-Null
|
|
$expectedAssets = @(Get-ChildItem -LiteralPath dist -File | Sort-Object Name)
|
|
$expectedCompanionAssetNames = @($expectedAssets | ForEach-Object Name | Sort-Object)
|
|
$targetRelease = gh release view $env:RELEASE_TAG --repo $env:GITHUB_REPOSITORY --json assets | ConvertFrom-Json
|
|
$actualCompanionAssetNames = @(
|
|
$targetRelease.assets |
|
|
Where-Object { $_.name.StartsWith("OpenClawCompanion-") } |
|
|
ForEach-Object name |
|
|
Sort-Object
|
|
)
|
|
$assetContractDiff = @(
|
|
Compare-Object `
|
|
-ReferenceObject $expectedCompanionAssetNames `
|
|
-DifferenceObject $actualCompanionAssetNames
|
|
)
|
|
if (
|
|
$actualCompanionAssetNames.Count -ne $expectedCompanionAssetNames.Count -or
|
|
$assetContractDiff.Count -ne 0
|
|
) {
|
|
throw "Promoted OpenClawCompanion asset names do not exactly match the current contract."
|
|
}
|
|
|
|
foreach ($asset in $expectedAssets) {
|
|
gh release download $env:RELEASE_TAG `
|
|
--repo $env:GITHUB_REPOSITORY `
|
|
--pattern $asset.Name `
|
|
--dir verified
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "Failed to download promoted Windows release asset $($asset.Name)."
|
|
}
|
|
}
|
|
|
|
$manifestPath = "verified/OpenClawCompanion-SHA256SUMS.txt"
|
|
$manifestEntries = @(Get-Content -LiteralPath $manifestPath | ForEach-Object {
|
|
if ($_ -notmatch '^([A-Fa-f0-9]{64}) ([^\\/]+)$') {
|
|
throw "Invalid Windows SHA-256 manifest entry: $_"
|
|
}
|
|
[PSCustomObject]@{
|
|
Hash = $Matches[1]
|
|
Name = $Matches[2]
|
|
}
|
|
})
|
|
$expectedInstallerNames = @(
|
|
$expectedAssets |
|
|
Where-Object Name -ne "OpenClawCompanion-SHA256SUMS.txt" |
|
|
ForEach-Object Name
|
|
)
|
|
$manifestInstallerNames = @($manifestEntries | ForEach-Object Name | Sort-Object)
|
|
$contractDiff = @(
|
|
Compare-Object `
|
|
-ReferenceObject $expectedInstallerNames `
|
|
-DifferenceObject $manifestInstallerNames
|
|
)
|
|
if ($contractDiff.Count -ne 0) {
|
|
throw "Promoted Windows SHA-256 manifest does not match the installer asset contract."
|
|
}
|
|
|
|
foreach ($entry in $manifestEntries) {
|
|
$hash = (Get-FileHash -Algorithm SHA256 -LiteralPath "verified/$($entry.Name)").Hash
|
|
if ($hash -ne $entry.Hash) {
|
|
throw "Promoted Windows release asset checksum mismatch: $($entry.Name)"
|
|
}
|
|
}
|
|
|
|
- name: Summary
|
|
shell: pwsh
|
|
env:
|
|
RELEASE_TAG: ${{ inputs.tag }}
|
|
WINDOWS_NODE_TAG: ${{ inputs.windows_node_tag }}
|
|
run: |
|
|
@"
|
|
## Windows Hub installers promoted
|
|
|
|
OpenClaw release: $env:RELEASE_TAG
|
|
Source release: openclaw/openclaw-windows-node@$env:WINDOWS_NODE_TAG
|
|
"@ >> $env:GITHUB_STEP_SUMMARY
|
|
Get-ChildItem -LiteralPath dist -File |
|
|
Sort-Object Name |
|
|
ForEach-Object {
|
|
"- https://github.com/openclaw/openclaw/releases/download/$env:RELEASE_TAG/$($_.Name)"
|
|
} >> $env:GITHUB_STEP_SUMMARY
|