fix(gateway): MiniMax env strip, Feishu log dedupe; release v0.4.4 #47
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| on: | |
| push: | |
| tags: | |
| - 'v*' | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: '要发布的 Git 标签(须已存在),例如 v0.2.18。必填:工作流会检出该标签再构建,勿留空(留空会误用当前分支导致安装包混拼 / Control UI 黑屏)。' | |
| required: true | |
| type: string | |
| concurrency: | |
| group: release-${{ github.workflow }}-${{ github.ref_name || github.run_id }} | |
| cancel-in-progress: false | |
| env: | |
| NODE_VERSION_CI: '22.16.0' | |
| # check-openclaw-versions: validate pin vs bundle-manifest only (no npm registry query / latest warn). | |
| OPENCLAW_SKIP_NPM_LATEST_CHECK: '1' | |
| # workflow_dispatch: must checkout the release tag, not the branch that triggered the run (avoids shell/OpenClaw pin vs artifact mismatch → Control UI black screen). | |
| CHECKOUT_REF: >- | |
| ${{ | |
| github.event_name == 'workflow_dispatch' && | |
| github.event.inputs.tag != '' && | |
| (startsWith(github.event.inputs.tag, 'refs/') && github.event.inputs.tag || format('refs/tags/{0}', github.event.inputs.tag)) || | |
| github.ref | |
| }} | |
| jobs: | |
| release_gate: | |
| name: Release context check | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Validate manual dispatch | |
| if: github.event_name == 'workflow_dispatch' | |
| run: | | |
| if [ -z "${{ github.event.inputs.tag }}" ]; then | |
| echo "::error::手动发布时必须在输入框填写 tag(与 Git 标签一致,例如 v0.2.18)。否则会按当前分支打包,极易与 Release 标签错配并导致安装包内 OpenClaw / Control UI 混拼。" | |
| exit 1 | |
| fi | |
| build_control_ui: | |
| name: Build OpenClaw Control UI (Linux) | |
| needs: release_gate | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ env.CHECKOUT_REF }} | |
| fetch-depth: 1 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 9 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION_CI }} | |
| cache: pnpm | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build Control UI for bundle | |
| env: | |
| NODE_OPTIONS: '--max-old-space-size=4096' | |
| run: pnpm exec tsx scripts/ci-build-openclaw-control-ui.ts | |
| - name: Upload Control UI dist | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: openclaw-control-ui-dist | |
| path: build/_ci_openclaw_control_ui_root/dist/control-ui | |
| retention-days: 2 | |
| if-no-files-found: error | |
| verify: | |
| name: Verify (lint, type-check, build) | |
| needs: release_gate | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| env: | |
| ELECTRON_CACHE: ~/.cache/electron | |
| ELECTRON_BUILDER_CACHE: ~/.cache/electron-builder | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ env.CHECKOUT_REF }} | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| submodules: false | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 9 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION_CI }} | |
| cache: pnpm | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Lint | |
| run: pnpm run lint | |
| - name: Type check | |
| run: pnpm run type-check | |
| - name: Build | |
| run: pnpm run build | |
| # Validates package.json pin vs bundle-manifest; pin need not equal npm openclaw@latest. | |
| - name: Check OpenClaw version consistency (pin vs bundle-manifest) | |
| run: pnpm run check-openclaw-versions | |
| package_win: | |
| name: Package Windows Installer | |
| needs: | |
| - verify | |
| - build_control_ui | |
| runs-on: windows-latest | |
| permissions: | |
| contents: read | |
| outputs: | |
| artifact-id: ${{ steps.upload-installer.outputs.artifact-id }} | |
| # SignPath 模式:vars.USE_SIGNPATH=true 时构建未签名包;否则用 CSC_LINK 签名 | |
| env: | |
| CSC_LINK: ${{ vars.USE_SIGNPATH == 'true' && '' || secrets.CSC_LINK }} | |
| CSC_KEY_PASSWORD: ${{ vars.USE_SIGNPATH == 'true' && '' || secrets.CSC_KEY_PASSWORD }} | |
| ELECTRON_CACHE: ~\AppData\Local\electron\Cache | |
| ELECTRON_BUILDER_CACHE: ~\AppData\Local\electron-builder\Cache | |
| # Whole job: any script that touches OpenClaw must not leave npm control-ui before Linux artifact merge | |
| OPENCLAW_SKIP_CONTROL_UI_BUILD: '1' | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ env.CHECKOUT_REF }} | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| submodules: false | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 9 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION_CI }} | |
| cache: pnpm | |
| - name: Cache Electron | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ${{ env.ELECTRON_CACHE }} | |
| ${{ env.ELECTRON_BUILDER_CACHE }} | |
| key: ${{ runner.os }}-electron-${{ hashFiles('pnpm-lock.yaml') }} | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build Electron app | |
| run: pnpm run build | |
| - name: Prepare apple-touch-icon for generate-resources | |
| run: | | |
| if (Test-Path resources/apple-touch-icon.png) { | |
| Copy-Item resources/apple-touch-icon.png apple-touch-icon.png -Force | |
| } | |
| # OpenClaw: version from package.json `openclawBundleVersion` (override: OPENCLAW_DESKTOP_BUNDLE_VERSION). | |
| - name: Download Node.js and OpenClaw (skip Vite on Windows) | |
| env: | |
| NODE_OPTIONS: "--max-old-space-size=4096" | |
| npm_config_fetch_retries: "5" | |
| npm_config_fetch_retry_mintimeout: "20000" | |
| run: | | |
| pnpm run download-node | |
| pnpm run download-openclaw | |
| - name: Assert OpenClaw npm bundle matches desktop pin | |
| run: pnpm exec tsx scripts/assert-openclaw-pin-aligns.ts | |
| # download-artifact merges into the path: leaves orphan assets/*.js from npm or self-hosted runners. | |
| # download-openclaw strips dist/control-ui when SKIP_CONTROL_UI_BUILD=1; this step is a second line of defense. | |
| - name: Ensure empty dist/control-ui before merge | |
| shell: pwsh | |
| run: | | |
| $p = Join-Path "build/openclaw/dist" "control-ui" | |
| if (Test-Path -LiteralPath $p) { | |
| Remove-Item -LiteralPath $p -Recurse -Force -ErrorAction Stop | |
| } | |
| # run-id: bind to this workflow run only (never pick artifact from another run / fork confusion) | |
| - name: Merge OpenClaw Control UI (Linux-built) | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: openclaw-control-ui-dist | |
| path: build/openclaw/dist/control-ui | |
| run-id: ${{ github.run_id }} | |
| - name: Assert merged Control UI layout | |
| shell: pwsh | |
| run: | | |
| $idx = Join-Path "build/openclaw/dist/control-ui" "index.html" | |
| if (-not (Test-Path -LiteralPath $idx)) { | |
| Write-Error "Control UI artifact merge failed: missing $idx (check upload/download paths)." | |
| exit 1 | |
| } | |
| # Same checks as verify-bundle / verify-packaged-win (esbuild parse) — catch HTML-as-JS or broken merge before prepare-bundle. | |
| - name: Verify merged Control UI bundle | |
| env: | |
| NODE_OPTIONS: "--max-old-space-size=4096" | |
| run: pnpm exec tsx scripts/verify-merged-control-ui.ts build/openclaw/dist/control-ui | |
| - name: Prepare bundle | |
| env: | |
| NODE_OPTIONS: "--max-old-space-size=4096" | |
| run: pnpm run prepare-bundle | |
| # Validates on-disk bundles vs package.json `openclawBundleVersion` + bundle-manifest (not npm latest). | |
| - name: Verify resource versions (Node, OpenClaw, project pin) | |
| run: pnpm run verify-resource-versions | |
| - name: Package Windows installer | |
| run: pnpm run package:win | |
| - name: Copy bundle manifest to dist | |
| run: Copy-Item resources/bundle-manifest.json dist/ -Force | |
| - name: Upload installer artifact | |
| id: upload-installer | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: openclaw-desktop-build | |
| path: | | |
| dist/OpenClaw-Setup-*.exe | |
| dist/bundle-manifest.json | |
| sign_path: | |
| name: Sign with SignPath Foundation | |
| needs: | |
| - package_win | |
| # 设置仓库变量 USE_SIGNPATH=true 时启用;否则使用 CSC_LINK 开发者签名 | |
| if: vars.USE_SIGNPATH == 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| actions: read | |
| contents: read | |
| steps: | |
| - name: Submit to SignPath for signing | |
| id: signpath | |
| uses: signpath/github-action-submit-signing-request@v2 | |
| with: | |
| api-token: ${{ secrets.SIGNPATH_API_TOKEN }} | |
| organization-id: ${{ secrets.SIGNPATH_ORGANIZATION_ID }} | |
| project-slug: ${{ secrets.SIGNPATH_PROJECT_SLUG }} | |
| signing-policy-slug: ${{ secrets.SIGNPATH_SIGNING_POLICY_SLUG }} | |
| artifact-configuration-slug: openclaw-desktop | |
| github-artifact-id: ${{ needs.package_win.outputs.artifact-id }} | |
| wait-for-completion: true | |
| output-artifact-directory: dist-signed-raw | |
| - name: Extract signed exe from zip | |
| run: | | |
| cd dist-signed-raw | |
| shopt -s nullglob | |
| for z in *.zip; do | |
| unzip -o "$z" | |
| rm -f "$z" | |
| done | |
| - name: Download build artifact for bundle manifest | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: openclaw-desktop-build | |
| path: manifest-temp | |
| - name: Copy bundle manifest into signed artifact | |
| run: | | |
| if [ -f manifest-temp/dist/bundle-manifest.json ]; then | |
| cp manifest-temp/dist/bundle-manifest.json dist-signed-raw/ | |
| fi | |
| - name: Upload signed installer | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: openclaw-desktop-setup | |
| path: dist-signed-raw/ | |
| publish_release: | |
| name: Publish GitHub Release | |
| needs: | |
| - package_win | |
| - sign_path | |
| # sign_path is skipped when USE_SIGNPATH≠true; without `skipped`, this job would never run. | |
| if: ${{ always() && needs.package_win.result == 'success' && (needs.sign_path.result == 'success' || needs.sign_path.result == 'skipped') }} | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ env.CHECKOUT_REF }} | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| submodules: false | |
| - name: Download signed installer | |
| if: needs.sign_path.result == 'success' | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: openclaw-desktop-setup | |
| path: dist | |
| - name: Download build artifact (unsigned) | |
| if: needs.sign_path.result == 'skipped' | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: openclaw-desktop-build | |
| path: dist | |
| - name: Generate SHA-256 checksums | |
| run: | | |
| cd dist | |
| for file in OpenClaw-Setup-*.exe; do | |
| [ -f "$file" ] || continue | |
| sha256sum "$file" > "$file.sha256" | |
| done | |
| - name: Generate latest.yml for electron-updater | |
| id: gen-yml | |
| run: | | |
| TAG="${{ inputs.tag || github.ref_name }}" | |
| VERSION="${TAG#v}" | |
| if [[ "$VERSION" == *"-beta"* ]] || [[ "$VERSION" == *"-alpha"* ]] || [[ "$VERSION" == *"-rc"* ]]; then | |
| CHANNEL=beta | |
| else | |
| CHANNEL=stable | |
| fi | |
| EXE=$(find dist -name "OpenClaw-Setup-*.exe" -type f | head -1) | |
| if [ -z "$EXE" ]; then | |
| echo "error: no OpenClaw-Setup-*.exe found in dist" | |
| exit 1 | |
| fi | |
| MANIFEST_ARG="" | |
| if [ -f dist/bundle-manifest.json ]; then | |
| MANIFEST_ARG="--manifest dist/bundle-manifest.json" | |
| fi | |
| node scripts/generate-latest-yml.mjs --exe "$EXE" --version "$VERSION" --channel "$CHANNEL" --output dist $MANIFEST_ARG | |
| if [ "$CHANNEL" = "beta" ]; then | |
| echo "yml_file=dist/latest-beta.yml" >> $GITHUB_OUTPUT | |
| else | |
| echo "yml_file=dist/latest.yml" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Publish GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ inputs.tag || github.ref_name }} | |
| name: ${{ inputs.tag || github.ref_name }} | |
| files: | | |
| dist/OpenClaw-Setup-*.exe | |
| dist/OpenClaw-Setup-*.exe.sha256 | |
| ${{ steps.gen-yml.outputs.yml_file }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |