Files
qiming/qimingclaw/.github/workflows/release-electron.yml.bak

531 lines
24 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Electron 桌面客户端 — 正式版本发布
# 与 Tauri 客户端完全分开:使用独立 tag 与独立 GitHub Release不共用 v* tag。
#
# 触发:推送 tag electron-v* → 构建并创建 Release。OSS 同步请用 sync-oss.sh。
#
# 支持平台:
# - macOS arm64macos-latest
# - macOS x64macos-latest, cross-compile
# - Windows x64msi
# - Linux x64AppImage + deb
# - Linux arm64AppImage + deb
#
# 使用的 Secrets可与 Tauri 共用 Apple 相关,也可单独配置):
# - GH_PAT可选创建 Release、上传资产未配置时自动回退到 github.token需 Contents: Write
# - macOS 签名/公证(未配置时 Mac 构建为无签名包,用户打开会提示「已损坏」):
# - APPLE_TEAM_ID / APPLE_SIGNING_IDENTITY / APPLE_CERTIFICATE.p12 Base64/ APPLE_CERTIFICATE_PASSWORD
# - 公证APPLE_API_KEY.p8 Base64/ APPLE_API_KEY_ID / APPLE_ISSUER_ID
# - Windows本 workflow 仅构建产物,不在 CI 内签名;签名在本地 WindowsSimplySign手动完成
# 详见 crates/agent-electron-client/docs/windows-signing.md
#
# 产物目录crates/agent-electron-client/release/${version}/(由 package.json directories.output 决定)
name: Release Electron App
on:
push:
tags:
- "electron-v*"
workflow_dispatch:
inputs:
tag:
description: "Release tag to sync (e.g. electron-v0.9.0)"
required: true
type: string
channel:
description: "Update channel pointer to publish (stable|beta). beta will NOT overwrite latest/latest.json"
required: false
default: stable
type: choice
options:
- stable
- beta
permissions:
contents: write
jobs:
prepare:
name: Prepare release
if: github.event_name == 'push'
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Get version from tag
id: version
run: |
VERSION="${GITHUB_REF_NAME#electron-v}"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "Electron release version: $VERSION"
- name: Read release notes
id: notes
run: |
VERSION="${{ steps.version.outputs.version }}"
TAG="${{ github.ref_name }}"
NOTES_FILE="release-notes/${TAG}.md"
if [ -f "$NOTES_FILE" ]; then
echo "Using release notes from $NOTES_FILE"
cp "$NOTES_FILE" release_notes.txt
else
echo "QimingClaw ${VERSION}。请在 Assets 中下载对应平台安装包。" > release_notes.txt
fi
- name: Create GitHub Release
env:
GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
run: |
TAG="${{ github.ref_name }}"
if gh release view "$TAG" --repo "$GITHUB_REPOSITORY" 2>/dev/null; then
echo "Release $TAG already exists"
else
gh release create "$TAG" \
--title "QimingClaw (Electron) ${{ steps.version.outputs.version }}" \
--notes-file release_notes.txt \
--repo "$GITHUB_REPOSITORY"
echo "Created release $TAG"
fi
build-electron:
name: Build Electron (${{ matrix.platform }} ${{ matrix.arch }})
needs: prepare
if: github.event_name == 'push'
strategy:
fail-fast: false
matrix:
include:
- platform: macos-latest
arch: arm64
dist_cmd: dist:mac:arm64
- platform: macos-latest
arch: x64
dist_cmd: dist:mac:x64
- platform: windows-latest
arch: x64
dist_cmd: dist:win
- platform: ubuntu-24.04
arch: x64
dist_cmd: dist:linux:x64
- platform: ubuntu-24.04-arm
arch: arm64
dist_cmd: dist:linux:arm64
runs-on: ${{ matrix.platform }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set version in package.json
shell: bash
working-directory: crates/agent-electron-client
run: |
VERSION="${{ needs.prepare.outputs.version }}"
npm pkg set version="$VERSION"
echo "package.json version: $(npm pkg get version)"
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: lts/*
# 安装 pnpm用于 workspace 管理,版本由根 package.json packageManager 字段指定)
- name: Install pnpm
uses: pnpm/action-setup@v4
# Python 3.12+ 移除了 distutilsnode-gypbetter-sqlite3 等 native 模块)依赖它;安装 setuptools 以提供 distutils
- name: Install setuptools for node-gyp (Python 3.12+)
shell: bash
run: python3 -m pip install setuptools --break-system-packages 2>/dev/null || python3 -m pip install setuptools
# Linux 构建 deb/rpm 包需要 rpm 工具arm64 runner 需要系统 fpm内置 fpm 仅 x86
- name: Install Linux build tools
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y rpm ruby ruby-dev
sudo gem install fpm
echo "USE_SYSTEM_FPM=true" >> "$GITHUB_ENV"
# pnpm install 会建立 workspace 链接qiming-mcp-stdio-proxy 通过 prepare 脚本自动构建
- name: Install workspace dependencies
run: pnpm install --filter @qiming-ai/qimingclaw...
# 为 Electron 内嵌 Node 重编 better-sqlite3避免 Linux arm64 等平台运行时 "cannot open shared object file"
- name: Rebuild native modules for Electron
working-directory: crates/agent-electron-client
run: npm run electron-rebuild
# 缓存内置 Node.js所有平台、Git仅 Windows、uv所有平台和 qimingcode所有平台
- name: Cache bundled Node.js
uses: actions/cache@v4
with:
path: crates/agent-electron-client/resources/node
key: bundled-node-24-${{ matrix.platform }}-${{ matrix.arch }}-${{ hashFiles('crates/agent-electron-client/package.json') }}
- name: Cache bundled Git (Windows only)
if: matrix.platform == 'windows-latest'
uses: actions/cache@v4
with:
path: crates/agent-electron-client/resources/git
key: bundled-git-2.47.1-windows-${{ hashFiles('crates/agent-electron-client/package.json') }}
- name: Cache uv
uses: actions/cache@v4
with:
path: crates/agent-electron-client/resources/uv
key: bundled-uv-v2-${{ matrix.platform }}-${{ matrix.arch }}-${{ hashFiles('crates/agent-electron-client/scripts/prepare/prepare-uv.js') }}
- name: Cache qimingcode
uses: actions/cache@v4
with:
path: crates/agent-electron-client/resources/qimingcode
key: bundled-qimingcode-v1-${{ matrix.platform }}-${{ matrix.arch }}-${{ hashFiles('crates/agent-electron-client/scripts/prepare/prepare-qimingcode.js') }}
- name: Prepare bundled resources (uv, node, git, qimingcode)
working-directory: crates/agent-electron-client
env:
TARGET_ARCH: ${{ matrix.arch }}
run: npm run prepare:all
timeout-minutes: 15
# ---- macOS导入证书并配置公证可选 ----
- name: Import Apple certificate (macOS)
if: runner.os == 'macOS'
env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
run: |
if [ -z "${APPLE_CERTIFICATE:-}" ]; then
echo "APPLE_CERTIFICATE not set, signing will be skipped"
exit 0
fi
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
KEYCHAIN_PASSWORD=actions
echo -n "$APPLE_CERTIFICATE" | base64 --decode -o "$CERTIFICATE_PATH"
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security import "$CERTIFICATE_PATH" -k "$KEYCHAIN_PATH" -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign -T /usr/bin/security
security list-keychain -d user -s "$KEYCHAIN_PATH"
rm -f "$CERTIFICATE_PATH"
echo "CSC_KEYCHAIN=$KEYCHAIN_PATH" >> "$GITHUB_ENV"
- name: Prepare Apple notarization key (macOS)
if: runner.os == 'macOS'
env:
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }}
run: |
if [ -z "${APPLE_API_KEY:-}" ] || [ -z "${APPLE_API_KEY_ID:-}" ]; then
echo "Apple notarization secrets not set, notarization will be skipped"
exit 0
fi
if [ -z "${APPLE_ISSUER_ID:-}" ]; then
echo "APPLE_ISSUER_ID required for notarization"
exit 0
fi
mkdir -p "$RUNNER_TEMP/apple-api-key"
echo -n "$APPLE_API_KEY" | base64 --decode -o "$RUNNER_TEMP/apple-api-key/AuthKey.p8"
echo "APPLE_API_KEY=$RUNNER_TEMP/apple-api-key/AuthKey.p8" >> "$GITHUB_ENV"
echo "APPLE_API_KEY_ID=$APPLE_API_KEY_ID" >> "$GITHUB_ENV"
echo "APPLE_API_ISSUER=$APPLE_ISSUER_ID" >> "$GITHUB_ENV"
echo "APPLE_ISSUER_ID=$APPLE_ISSUER_ID" >> "$GITHUB_ENV"
# 若用户看到「已损坏」:请确认已配置 APPLE_CERTIFICATE / APPLE_SIGNING_IDENTITY 及公证用 APPLE_API_KEY 等,且构建日志中有签名与公证成功
# Windows 签名已废弃,改为本地手签方案
- name: Clean previous build artifacts
shell: bash
working-directory: crates/agent-electron-client
run: |
rm -rf release node_modules/.cache
echo "Cleaned release and cache directories"
- name: Build Electron app
shell: bash
working-directory: crates/agent-electron-client
env:
TARGET_ARCH: ${{ matrix.arch }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
CSC_NAME: ${{ secrets.APPLE_SIGNING_IDENTITY }}
run: |
if [ "${{ runner.os }}" = "macOS" ] && [ -z "${{ secrets.APPLE_CERTIFICATE }}" ]; then
echo "No Apple certificate, building unsigned Mac package for ${{ matrix.arch }}"
npm run dist:mac:unsigned:${{ matrix.arch }} -- --publish never
elif [ "${{ runner.os }}" = "Windows" ]; then
npm run ${{ matrix.dist_cmd }} -- --publish never
else
npm run ${{ matrix.dist_cmd }} -- --publish never
fi
- name: Upload artifacts to Release
shell: bash
env:
GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
run: |
TAG="${{ github.ref_name }}"
VERSION="${{ needs.prepare.outputs.version }}"
OUT_DIR="crates/agent-electron-client/release/${VERSION}"
if [ ! -d "$OUT_DIR" ]; then
echo "::error::Output directory not found: $OUT_DIR"
exit 1
fi
for f in "${OUT_DIR}"/*; do
[ -e "$f" ] || continue
[ -f "$f" ] || continue
echo "Uploading $(basename "$f") ..."
gh release upload "$TAG" "$f" --clobber --repo "$GITHUB_REPOSITORY"
done
# ==================== 同步到阿里云 OSS ====================
# 手动触发workflow_dispatch 输入 tag如 electron-v0.8.0
sync-to-oss:
name: Sync release assets to Alibaba Cloud OSS
if: github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
continue-on-error: true
env:
OSS_ENDPOINT: "https://oss-rg-china-mainland.aliyuncs.com"
OSS_REGION: "rg-china-mainland"
OSS_BUCKET: "oss://qiming-packages"
OSS_CDN_BASE: "https://qiming-packages.oss-rg-china-mainland.aliyuncs.com"
RELEASE_TAG: ${{ inputs.tag }}
RELEASE_CHANNEL: ${{ inputs.channel || 'stable' }}
steps:
- name: Install ossutil v2
run: |
curl -fSL -o /tmp/ossutil.zip \
"https://gosspublic.alicdn.com/ossutil/v2/2.2.0/ossutil-2.2.0-linux-amd64.zip"
unzip -q /tmp/ossutil.zip -d /tmp
sudo mv /tmp/ossutil-2.2.0-linux-amd64/ossutil /usr/local/bin/
ossutil version
- name: Download all release assets
env:
GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
run: |
TAG="${RELEASE_TAG}"
mkdir -p /tmp/release-assets
gh release download "$TAG" --repo "$GITHUB_REPOSITORY" --dir /tmp/release-assets
echo "==> Downloaded assets:"
ls -lh /tmp/release-assets/
# 以 latest.json 为唯一数据源:生成 latest.json并据此生成 electron-updater 所需的 yml与 macOS 一致Linux 也走 latest/latest.json
# latest-mac.yml、latest-linux.yml、latest-linux-arm64.yml、latest-linux-x64.yml、latest.yml写入 release-assets 后一并上传。
- name: Generate latest.json and platform yml from assets
env:
OSS_CDN_BASE: "https://qiming-packages.oss-rg-china-mainland.aliyuncs.com"
GH_TOKEN: ${{ secrets.GH_PAT || github.token }}
run: |
TAG="${RELEASE_TAG}"
VERSION="${TAG#electron-v}"
CHANNEL="${RELEASE_CHANNEL}"
OSS_URL_PREFIX="${OSS_CDN_BASE}/qimingclaw-electron/${TAG}"
ASSETS_DIR="/tmp/release-assets"
# semver MVP guard: only allow numeric x.y.z用 grep -E不依赖 ripgrep与 ubuntu-latest 默认环境一致)
if ! echo "$VERSION" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$'; then
echo "::error::Invalid version '${VERSION}'. MVP only supports numeric x.y.z"
exit 1
fi
if [ "$CHANNEL" != "stable" ] && [ "$CHANNEL" != "beta" ]; then
echo "::error::Invalid channel '${CHANNEL}', expected stable or beta"
exit 1
fi
# 删除 GitHub 自带的 yml仅保留由 latest.json 派生的 yml
rm -f "${ASSETS_DIR}"/*.yml
# 获取 release notes 与 pub_date
RELEASE_JSON=$(gh release view "$TAG" --repo "$GITHUB_REPOSITORY" --json body,createdAt 2>/dev/null || echo '{}')
NOTES=$(echo "$RELEASE_JSON" | jq -r '.body // "QimingClaw 功能优化和问题修复。建议更新到最新版本以获得更好的体验。"' | head -c 500)
PUB_DATE=$(echo "$RELEASE_JSON" | jq -r '.createdAt // empty')
if [ -z "$PUB_DATE" ]; then
PUB_DATE=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
fi
add_platform() {
local key="$1" file="$2"
local filepath="${ASSETS_DIR}/${file}"
if [ -f "$filepath" ]; then
local sig size
sig=$(sha512sum "$filepath" | awk '{print $1}' | xxd -r -p | base64 -w0)
if [ -z "$sig" ]; then
echo " Skipped: ${key} -> ${file} (signature generation failed)"
return 0
fi
size=$(stat -c%s "$filepath" 2>/dev/null || stat -f%z "$filepath")
PLATFORMS=$(echo "$PLATFORMS" | jq \
--arg k "$key" \
--arg u "${OSS_URL_PREFIX}/${file}" \
--arg s "$sig" \
--argjson sz "$size" \
'. + {($k): {"url": $u, "signature": $s, "size": $sz}}')
echo " Added: ${key} -> ${file} (${size} bytes)"
fi
}
PLATFORMS="{}"
echo "==> Scanning release assets for platforms..."
add_platform "darwin-aarch64" "QimingClaw-${VERSION}-arm64.dmg"
add_platform "darwin-aarch64-zip" "QimingClaw-${VERSION}-arm64-mac.zip"
add_platform "darwin-x86_64" "QimingClaw-${VERSION}.dmg"
add_platform "darwin-x86_64-zip" "QimingClaw-${VERSION}-mac.zip"
add_platform "windows-x86_64" "QimingClaw.Setup.${VERSION}.exe"
add_platform "windows-x86_64-nsis" "QimingClaw.Setup.${VERSION}.exe"
add_platform "windows-x86_64-msi" "QimingClaw.${VERSION}.msi"
add_platform "linux-x86_64" "QimingClaw-${VERSION}.AppImage"
add_platform "linux-x86_64-appimage" "QimingClaw-${VERSION}.AppImage"
add_platform "linux-x86_64-deb" "QimingClaw-${VERSION}-amd64.deb"
add_platform "linux-x86_64-rpm" "QimingClaw-${VERSION}-x86_64.rpm"
add_platform "linux-aarch64" "QimingClaw-${VERSION}-arm64.AppImage"
add_platform "linux-aarch64-appimage" "QimingClaw-${VERSION}-arm64.AppImage"
add_platform "linux-aarch64-deb" "QimingClaw-${VERSION}-arm64.deb"
add_platform "linux-aarch64-rpm" "QimingClaw-${VERSION}-aarch64.rpm"
if [ "$(echo "$PLATFORMS" | jq 'length')" = "0" ]; then
echo "::warning::No exact filename matches, falling back to fuzzy scan"
for file in "${ASSETS_DIR}"/*; do
filename=$(basename "$file")
case "$filename" in
*.dmg|*.zip|*.exe|*.msi|*.AppImage|*.deb|*.rpm)
key=""
case "$filename" in
*arm64*mac*|*arm64*.dmg) key="darwin-aarch64" ;;
*x64*mac*|*x64*.dmg) key="darwin-x86_64" ;;
*Setup*.exe|*setup*.exe) key="windows-x86_64" ;;
*.msi) key="windows-x86_64-msi" ;;
*arm64*.AppImage) key="linux-aarch64" ;;
*amd64*.AppImage|*x64*.AppImage|*.AppImage) key="linux-x86_64" ;;
*arm64*.deb) key="linux-aarch64-deb" ;;
*amd64*.deb|*x64*.deb) key="linux-x86_64-deb" ;;
*aarch64*.rpm) key="linux-aarch64-rpm" ;;
*x86_64*.rpm|*x64*.rpm) key="linux-x86_64-rpm" ;;
esac
[ -n "$key" ] && add_platform "$key" "$filename"
;;
esac
done
fi
LATEST_JSON=$(jq -n \
--arg version "$VERSION" \
--arg notes "$NOTES" \
--arg pub_date "$PUB_DATE" \
--argjson platforms "$PLATFORMS" \
'{version: $version, notes: $notes, pub_date: $pub_date, platforms: $platforms}')
echo "$LATEST_JSON" > /tmp/latest.json
echo "==> Generated latest.json"
echo "$LATEST_JSON" | jq .
# 从 latest.json 的 platforms 生成 electron-updater 所需的 ymlurl 使用相对路径feed 基地址为 OSS 版本化路径)
# 格式: version, files: [{ url, sha512, size }], path, sha512, releaseDate
gen_yml() {
local channel="$1"
local keys="$2"
local first_key first_file
first_key=$(echo "$PLATFORMS" | jq -r --argjson keys "$keys" 'limit(1; $keys[] as $k | select(.[$k] != null) | $k)')
[ -z "$first_key" ] || [ "$first_key" = "null" ] && return 0
first_file=$(echo "$PLATFORMS" | jq -r --arg k "$first_key" '.[$k].url' | sed 's|.*/||')
{
echo "version: $VERSION"
echo "path: $first_file"
echo "sha512: $(echo "$PLATFORMS" | jq -r --arg k "$first_key" '.[$k].signature')"
echo "releaseDate: $PUB_DATE"
echo "files:"
echo "$PLATFORMS" | jq -r --argjson keys "$keys" '
[ $keys[] as $k | select(.[$k] != null) | .[$k] |
" - url: " + (.url | split("/") | last) + "\n sha512: " + .signature + "\n size: " + (.size | tostring)
] | join("\n")
'
} > "${ASSETS_DIR}/${channel}.yml"
echo " Generated ${channel}.yml (primary: $first_file)"
}
# 与 macOS 一致:所有 yml 均由同一份 latest.json 的 platforms 派生Linux 也走 latest/latest.json 的更新链路
DARWIN_KEYS='["darwin-aarch64-zip","darwin-x86_64-zip","darwin-aarch64","darwin-x86_64"]'
LINUX_KEYS='["linux-x86_64-appimage","linux-aarch64-appimage","linux-x86_64","linux-aarch64","linux-x86_64-deb","linux-aarch64-deb","linux-x86_64-rpm","linux-aarch64-rpm"]'
LINUX_ARM64_KEYS='["linux-aarch64-appimage","linux-aarch64","linux-aarch64-deb","linux-aarch64-rpm"]'
LINUX_X64_KEYS='["linux-x86_64-appimage","linux-x86_64","linux-x86_64-deb","linux-x86_64-rpm"]'
WIN_KEYS='["windows-x86_64","windows-x86_64-nsis","windows-x86_64-msi"]'
gen_yml "latest-mac" "$DARWIN_KEYS"
gen_yml "latest-linux" "$LINUX_KEYS"
# electron-updater 在 Linux 上按架构请求 latest-linux-arm64.yml / latest-linux-x64.yml与 macOS 一样由 latest.json 派生
gen_yml "latest-linux-arm64" "$LINUX_ARM64_KEYS"
gen_yml "latest-linux-x64" "$LINUX_X64_KEYS"
gen_yml "latest" "$WIN_KEYS"
echo "==> Platform yml written to ${ASSETS_DIR}"
- name: Upload all assets to versioned path
env:
OSS_ACCESS_KEY_ID: ${{ secrets.OSS_ACCESS_KEY_ID }}
OSS_ACCESS_KEY_SECRET: ${{ secrets.OSS_ACCESS_KEY_SECRET }}
run: |
TAG="${RELEASE_TAG}"
OSS_PREFIX="qimingclaw-electron/${TAG}"
for file in /tmp/release-assets/*; do
filename=$(basename "$file")
echo "Uploading: ${filename}"
ossutil cp --force "$file" "${OSS_BUCKET}/${OSS_PREFIX}/${filename}" \
--endpoint "$OSS_ENDPOINT" \
--region "$OSS_REGION" \
--access-key-id "$OSS_ACCESS_KEY_ID" \
--access-key-secret "$OSS_ACCESS_KEY_SECRET"
done
# latest.json 已在上一段生成并写入 /tmp/latest.json此处仅上传
- name: Upload latest.json to OSS
env:
OSS_ACCESS_KEY_ID: ${{ secrets.OSS_ACCESS_KEY_ID }}
OSS_ACCESS_KEY_SECRET: ${{ secrets.OSS_ACCESS_KEY_SECRET }}
run: |
TAG="${RELEASE_TAG}"
CHANNEL="${RELEASE_CHANNEL}"
echo "==> Publishing latest.json for channel: ${CHANNEL}"
ossutil cp --force /tmp/latest.json "${OSS_BUCKET}/qimingclaw-electron/${TAG}/latest.json" \
--endpoint "$OSS_ENDPOINT" \
--region "$OSS_REGION" \
--access-key-id "$OSS_ACCESS_KEY_ID" \
--access-key-secret "$OSS_ACCESS_KEY_SECRET"
if [ "$CHANNEL" = "stable" ]; then
ossutil cp --force /tmp/latest.json "${OSS_BUCKET}/qimingclaw-electron/latest/latest.json" \
--endpoint "$OSS_ENDPOINT" \
--region "$OSS_REGION" \
--access-key-id "$OSS_ACCESS_KEY_ID" \
--access-key-secret "$OSS_ACCESS_KEY_SECRET"
echo "==> Uploaded latest.json to stable pointer: latest/latest.json"
else
ossutil cp --force /tmp/latest.json "${OSS_BUCKET}/qimingclaw-electron/beta/latest.json" \
--endpoint "$OSS_ENDPOINT" \
--region "$OSS_REGION" \
--access-key-id "$OSS_ACCESS_KEY_ID" \
--access-key-secret "$OSS_ACCESS_KEY_SECRET"
echo "==> Uploaded latest.json to beta pointer: beta/latest.json (stable pointer untouched)"
fi
- name: Verify OSS upload
run: |
CHANNEL="${RELEASE_CHANNEL}"
if [ "$CHANNEL" = "stable" ]; then
VERIFY_URL="${OSS_CDN_BASE}/qimingclaw-electron/latest/latest.json"
else
VERIFY_URL="${OSS_CDN_BASE}/qimingclaw-electron/beta/latest.json"
fi
HTTP_STATUS=$(curl -sS -o /tmp/oss-latest.json -w "%{http_code}" "$VERIFY_URL")
if [ "$HTTP_STATUS" = "200" ]; then
echo "==> latest.json (${CHANNEL}): OK (HTTP ${HTTP_STATUS})"
cat /tmp/oss-latest.json | jq .
else
echo "::error::latest.json for channel '${CHANNEL}' not found on OSS (HTTP ${HTTP_STATUS})"
exit 1
fi