283 lines
13 KiB
YAML
283 lines
13 KiB
YAML
# 仅同步:将指定 tag 的 GitHub Release 资产同步到阿里云 OSS(不构建)
|
||
# 供 sync-oss.sh 通过 workflow_dispatch 触发;qimingclaw 仓库需复制本文件到 .github/workflows/ 后即可用。
|
||
#
|
||
# 所需 Secrets:GH_PAT(或 repo token)、OSS_ACCESS_KEY_ID、OSS_ACCESS_KEY_SECRET
|
||
|
||
name: Sync Electron Release to OSS
|
||
|
||
on:
|
||
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: read
|
||
|
||
jobs:
|
||
sync-to-oss:
|
||
name: Sync release assets to Alibaba Cloud OSS
|
||
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/
|
||
|
||
- 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}"
|
||
if [ "$CHANNEL" = "beta" ]; then
|
||
OSS_URL_PREFIX="${OSS_CDN_BASE}/qimingclaw-electron/beta-build/${TAG}"
|
||
fi
|
||
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
|
||
|
||
rm -f "${ASSETS_DIR}"/*.yml
|
||
|
||
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
|
||
|
||
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)"
|
||
}
|
||
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"
|
||
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}"
|
||
|
||
# 构建 yml 文件的完整 OSS URL(供客户端 electron-updater 直接使用)
|
||
# 注意:stable 通道 OSS_URL_PREFIX = qimingclaw-electron/electron-v{x}.y.z
|
||
# beta 通道 OSS_URL_PREFIX = qimingclaw-electron/beta-build/prerelease-v{x}.y.z
|
||
YML_URLS=$(jq -n \
|
||
--arg darwin "${OSS_URL_PREFIX}/latest-mac.yml" \
|
||
--arg linux "${OSS_URL_PREFIX}/latest-linux.yml" \
|
||
--arg win "${OSS_URL_PREFIX}/latest.yml" \
|
||
'{darwin: $darwin, linux: $linux, win: $win}')
|
||
|
||
LATEST_JSON=$(jq -n \
|
||
--arg version "$VERSION" \
|
||
--arg notes "$NOTES" \
|
||
--arg pub_date "$PUB_DATE" \
|
||
--argjson platforms "$PLATFORMS" \
|
||
--argjson yml "$YML_URLS" \
|
||
'{version: $version, notes: $notes, pub_date: $pub_date, platforms: $platforms, yml: $yml}')
|
||
echo "$LATEST_JSON" > /tmp/latest.json
|
||
echo "==> Generated latest.json"
|
||
echo "$LATEST_JSON" | jq .
|
||
|
||
- 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}"
|
||
CHANNEL="${RELEASE_CHANNEL}"
|
||
if [ "$CHANNEL" = "beta" ]; then
|
||
OSS_PREFIX="qimingclaw-electron/beta-build/${TAG}"
|
||
else
|
||
OSS_PREFIX="qimingclaw-electron/${TAG}"
|
||
fi
|
||
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
|
||
|
||
- 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}"
|
||
if [ "$CHANNEL" = "beta" ]; then
|
||
VERSIONED_JSON_PATH="${OSS_BUCKET}/qimingclaw-electron/beta-build/${TAG}/latest.json"
|
||
else
|
||
VERSIONED_JSON_PATH="${OSS_BUCKET}/qimingclaw-electron/${TAG}/latest.json"
|
||
fi
|
||
ossutil cp --force /tmp/latest.json "${VERSIONED_JSON_PATH}" \
|
||
--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
|