mirror of
https://github.com/linshenkx/prompt-optimizer.git
synced 2026-05-07 22:18:23 +08:00
506 lines
18 KiB
YAML
506 lines
18 KiB
YAML
name: Release Desktop Apps
|
||
|
||
on:
|
||
push:
|
||
tags:
|
||
- 'v*.*.*' # 正式版本: v1.0.0, v2.1.3
|
||
- 'v*.*.*-*' # 预览版本: v1.0.0-beta.1, v1.0.0-rc.1
|
||
workflow_dispatch:
|
||
inputs:
|
||
version:
|
||
description: '当前所选 ref 上待发布的版本号(例如: v2.8.0)'
|
||
required: true
|
||
type: string
|
||
|
||
env:
|
||
NODE_VERSION: '22'
|
||
|
||
jobs:
|
||
test:
|
||
uses: ./.github/workflows/test.yml
|
||
|
||
validate-release-notes:
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- name: 检出代码
|
||
uses: actions/checkout@v6
|
||
with:
|
||
fetch-depth: 0
|
||
fetch-tags: true
|
||
|
||
- name: 设置 Node.js 环境
|
||
uses: actions/setup-node@v6
|
||
with:
|
||
node-version: ${{ env.NODE_VERSION }}
|
||
|
||
- name: 校验版本说明文件
|
||
run: |
|
||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||
VERSION="${{ inputs.version }}"
|
||
echo "📝 Using manual input version: $VERSION"
|
||
else
|
||
VERSION="${GITHUB_REF#refs/tags/}"
|
||
echo "🏷️ Using git tag version: $VERSION"
|
||
fi
|
||
|
||
echo "Validating release notes for $VERSION"
|
||
node scripts/release-notes.js check "$VERSION"
|
||
|
||
prepare-release:
|
||
needs: [test, validate-release-notes]
|
||
runs-on: ubuntu-latest
|
||
permissions:
|
||
contents: write
|
||
outputs:
|
||
version: ${{ steps.version.outputs.version }}
|
||
version_no_prefix: ${{ steps.version.outputs.version_no_prefix }}
|
||
is_prerelease: ${{ steps.version.outputs.is_prerelease }}
|
||
version_type: ${{ steps.version.outputs.version_type }}
|
||
steps:
|
||
- name: 检出代码
|
||
uses: actions/checkout@v6
|
||
with:
|
||
fetch-depth: 0
|
||
fetch-tags: true
|
||
|
||
- name: 设置 Node.js 环境
|
||
uses: actions/setup-node@v6
|
||
with:
|
||
node-version: ${{ env.NODE_VERSION }}
|
||
|
||
- name: 获取版本号和类型
|
||
id: version
|
||
run: |
|
||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||
VERSION="${{ inputs.version }}"
|
||
echo "📝 Using manual input version: $VERSION"
|
||
else
|
||
VERSION="${GITHUB_REF#refs/tags/}"
|
||
echo "🏷️ Using git tag version: $VERSION"
|
||
fi
|
||
|
||
VERSION_NO_PREFIX="${VERSION#v}"
|
||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||
echo "version_no_prefix=$VERSION_NO_PREFIX" >> "$GITHUB_OUTPUT"
|
||
echo "Version: $VERSION"
|
||
|
||
IS_PRERELEASE=false
|
||
VERSION_TYPE="release"
|
||
if [[ $VERSION =~ -(alpha|beta|rc) ]]; then
|
||
IS_PRERELEASE=true
|
||
VERSION_TYPE="prerelease"
|
||
echo "🧪 Detected prerelease version"
|
||
elif [[ $VERSION =~ -(hotfix|patch) ]]; then
|
||
IS_PRERELEASE=false
|
||
VERSION_TYPE="hotfix"
|
||
echo "🔧 Detected hotfix version"
|
||
else
|
||
IS_PRERELEASE=false
|
||
VERSION_TYPE="release"
|
||
echo "🚀 Detected stable release version"
|
||
fi
|
||
|
||
echo "is_prerelease=$IS_PRERELEASE" >> "$GITHUB_OUTPUT"
|
||
echo "version_type=$VERSION_TYPE" >> "$GITHUB_OUTPUT"
|
||
|
||
- name: 生成 Release 正文
|
||
run: |
|
||
CURRENT_TAG="${{ steps.version.outputs.version }}"
|
||
node scripts/release-notes.js render-body "$CURRENT_TAG" "${{ github.repository }}" > release_body.md
|
||
echo "Generated release body:"
|
||
cat release_body.md
|
||
|
||
- name: 创建或更新 Draft Release
|
||
env:
|
||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||
VERSION: ${{ steps.version.outputs.version }}
|
||
IS_PRERELEASE: ${{ steps.version.outputs.is_prerelease }}
|
||
run: |
|
||
if gh release view "$VERSION" --repo "${{ github.repository }}" >/dev/null 2>&1; then
|
||
echo "Release already exists, updating metadata in place"
|
||
gh release edit "$VERSION" \
|
||
--repo "${{ github.repository }}" \
|
||
--title "$VERSION" \
|
||
--notes-file release_body.md \
|
||
$([ "$IS_PRERELEASE" = "true" ] && echo "--prerelease")
|
||
else
|
||
echo "Creating draft release shell"
|
||
gh release create "$VERSION" \
|
||
--repo "${{ github.repository }}" \
|
||
--title "$VERSION" \
|
||
--notes-file release_body.md \
|
||
--target "${GITHUB_SHA}" \
|
||
--draft \
|
||
$([ "$IS_PRERELEASE" = "true" ] && echo "--prerelease")
|
||
fi
|
||
|
||
# 构建 Windows 版本
|
||
build-windows:
|
||
needs: [prepare-release]
|
||
runs-on: windows-latest
|
||
permissions:
|
||
contents: write
|
||
steps:
|
||
- name: 检出代码
|
||
uses: actions/checkout@v6
|
||
with:
|
||
fetch-depth: 0
|
||
fetch-tags: true
|
||
|
||
- name: 安装 pnpm
|
||
uses: pnpm/action-setup@v4
|
||
with:
|
||
run_install: false
|
||
|
||
- name: 设置 Node.js 环境
|
||
uses: actions/setup-node@v6
|
||
with:
|
||
node-version: ${{ env.NODE_VERSION }}
|
||
cache: 'pnpm'
|
||
|
||
- name: 动态配置 package.json
|
||
shell: pwsh
|
||
run: |
|
||
$pkgPath = "packages/desktop/package.json"
|
||
$pkgJson = Get-Content $pkgPath -Raw | ConvertFrom-Json
|
||
$tagVersion = "${{ needs.prepare-release.outputs.version_no_prefix }}"
|
||
|
||
Write-Host "✅ Set version to: $tagVersion"
|
||
|
||
$repoUrl = "https://github.com/${{ github.repository }}.git"
|
||
$pkgJson.version = $tagVersion
|
||
$pkgJson.repository.url = $repoUrl
|
||
|
||
$repoInfo = "${{ github.repository }}".split("/")
|
||
$repoOwner = $repoInfo[0]
|
||
$repoName = $repoInfo[1]
|
||
|
||
$pkgJson | ConvertTo-Json -Depth 10 | Set-Content $pkgPath -Encoding UTF8
|
||
|
||
Write-Host ""
|
||
Write-Host "📋 Configuration Summary:"
|
||
Write-Host " Version: $tagVersion"
|
||
Write-Host " Type: ${{ needs.prepare-release.outputs.version_type }}"
|
||
Write-Host " Repository: $repoUrl"
|
||
Write-Host " Owner: $repoOwner"
|
||
Write-Host " Repo: $repoName"
|
||
Write-Host " Private: false (fixed)"
|
||
Write-Host " Prerelease: ${{ needs.prepare-release.outputs.is_prerelease }}"
|
||
|
||
- name: 安装依赖
|
||
run: pnpm install
|
||
|
||
- name: 构建 Desktop 应用
|
||
run: pnpm build:desktop:ci
|
||
|
||
- name: 验证构建产物
|
||
shell: bash
|
||
run: |
|
||
echo "Checking build artifacts..."
|
||
ls -la packages/desktop/dist/ || echo "No dist directory found"
|
||
|
||
exe_files=$(find packages/desktop/dist -name "*.exe" 2>/dev/null | wc -l)
|
||
zip_files=$(find packages/desktop/dist -name "*.zip" 2>/dev/null | wc -l)
|
||
blockmap_files=$(find packages/desktop/dist -name "*.blockmap" 2>/dev/null | wc -l)
|
||
|
||
echo "Found $exe_files .exe files, $zip_files .zip files, and $blockmap_files .blockmap files"
|
||
|
||
if [ "$exe_files" -eq 0 ] || [ "$zip_files" -eq 0 ] || [ "$blockmap_files" -eq 0 ]; then
|
||
echo "Error: Windows build is missing required release artifacts (.exe, .zip, or .blockmap)"
|
||
echo "Directory contents:"
|
||
find packages/desktop/dist -type f 2>/dev/null || echo "Directory not found or empty"
|
||
exit 1
|
||
fi
|
||
|
||
echo "Build artifacts verification passed"
|
||
|
||
- name: 清理调试文件
|
||
shell: bash
|
||
run: find packages/desktop/dist -type f -name "builder-debug.yml" -delete
|
||
|
||
- name: 直接上传 Windows Release Assets
|
||
shell: bash
|
||
env:
|
||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||
VERSION: ${{ needs.prepare-release.outputs.version }}
|
||
run: |
|
||
gh release upload "$VERSION" \
|
||
packages/desktop/dist/PromptOptimizer-*.exe \
|
||
packages/desktop/dist/PromptOptimizer-*.zip \
|
||
packages/desktop/dist/*.blockmap \
|
||
packages/desktop/dist/latest*.yml \
|
||
--repo "${{ github.repository }}" \
|
||
--clobber
|
||
|
||
# 构建 macOS 版本
|
||
build-macos:
|
||
needs: [prepare-release]
|
||
runs-on: macos-latest
|
||
permissions:
|
||
contents: write
|
||
steps:
|
||
- name: 检出代码
|
||
uses: actions/checkout@v6
|
||
with:
|
||
fetch-depth: 0
|
||
fetch-tags: true
|
||
|
||
- name: 安装 pnpm
|
||
uses: pnpm/action-setup@v4
|
||
with:
|
||
run_install: false
|
||
|
||
- name: 设置 Node.js 环境
|
||
uses: actions/setup-node@v6
|
||
with:
|
||
node-version: ${{ env.NODE_VERSION }}
|
||
cache: 'pnpm'
|
||
|
||
- name: 动态配置 package.json
|
||
run: |
|
||
PKG_PATH="packages/desktop/package.json"
|
||
TAG_VERSION="${{ needs.prepare-release.outputs.version_no_prefix }}"
|
||
REPO_URL="https://github.com/${{ github.repository }}.git"
|
||
IFS='/' read -r REPO_OWNER REPO_NAME <<< "${{ github.repository }}"
|
||
|
||
jq --arg version "$TAG_VERSION" \
|
||
--arg repo_url "$REPO_URL" \
|
||
'.version = $version |
|
||
.repository.url = $repo_url' \
|
||
"$PKG_PATH" > "${PKG_PATH}.tmp" && mv "${PKG_PATH}.tmp" "$PKG_PATH"
|
||
|
||
echo "✅ package.json updated successfully"
|
||
echo ""
|
||
echo "📋 Configuration Summary:"
|
||
echo " Version: $TAG_VERSION"
|
||
echo " Type: ${{ needs.prepare-release.outputs.version_type }}"
|
||
echo " Repository: $REPO_URL"
|
||
echo " Owner: $REPO_OWNER"
|
||
echo " Repo: $REPO_NAME"
|
||
echo " Private: false (fixed)"
|
||
echo " Prerelease: ${{ needs.prepare-release.outputs.is_prerelease }}"
|
||
|
||
- name: 安装依赖
|
||
run: pnpm install
|
||
|
||
- name: 构建 Desktop 应用
|
||
run: pnpm build:desktop:ci
|
||
|
||
- name: 验证构建产物
|
||
run: |
|
||
echo "Checking build artifacts..."
|
||
ls -la packages/desktop/dist/ || echo "No dist directory found"
|
||
|
||
dmg_files=$(find packages/desktop/dist -name "*.dmg" 2>/dev/null | wc -l)
|
||
zip_files=$(find packages/desktop/dist -name "*.zip" 2>/dev/null | wc -l)
|
||
blockmap_files=$(find packages/desktop/dist -name "*.blockmap" 2>/dev/null | wc -l)
|
||
|
||
echo "Found $dmg_files .dmg files, $zip_files .zip files, and $blockmap_files .blockmap files"
|
||
|
||
if [ "$dmg_files" -eq 0 ] || [ "$zip_files" -eq 0 ] || [ "$blockmap_files" -eq 0 ]; then
|
||
echo "Error: macOS build is missing required release artifacts (.dmg, .zip, or .blockmap)"
|
||
echo "Directory contents:"
|
||
find packages/desktop/dist -type f 2>/dev/null || echo "Directory not found or empty"
|
||
exit 1
|
||
fi
|
||
|
||
echo "Build artifacts verification passed"
|
||
|
||
- name: 清理调试文件
|
||
run: find packages/desktop/dist -type f -name "builder-debug.yml" -delete
|
||
|
||
- name: 直接上传 macOS Release Assets
|
||
env:
|
||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||
VERSION: ${{ needs.prepare-release.outputs.version }}
|
||
run: |
|
||
gh release upload "$VERSION" \
|
||
packages/desktop/dist/*.dmg \
|
||
packages/desktop/dist/*.zip \
|
||
packages/desktop/dist/*.blockmap \
|
||
packages/desktop/dist/*.yml \
|
||
--repo "${{ github.repository }}" \
|
||
--clobber
|
||
|
||
# 构建 Linux 版本
|
||
build-linux:
|
||
needs: [prepare-release]
|
||
runs-on: ubuntu-latest
|
||
permissions:
|
||
contents: write
|
||
steps:
|
||
- name: 检出代码
|
||
uses: actions/checkout@v6
|
||
with:
|
||
fetch-depth: 0
|
||
fetch-tags: true
|
||
|
||
- name: 安装 pnpm
|
||
uses: pnpm/action-setup@v4
|
||
with:
|
||
run_install: false
|
||
|
||
- name: 设置 Node.js 环境
|
||
uses: actions/setup-node@v6
|
||
with:
|
||
node-version: ${{ env.NODE_VERSION }}
|
||
cache: 'pnpm'
|
||
|
||
- name: 动态配置 package.json
|
||
run: |
|
||
PKG_PATH="packages/desktop/package.json"
|
||
TAG_VERSION="${{ needs.prepare-release.outputs.version_no_prefix }}"
|
||
REPO_URL="https://github.com/${{ github.repository }}.git"
|
||
IFS='/' read -r REPO_OWNER REPO_NAME <<< "${{ github.repository }}"
|
||
|
||
jq --arg version "$TAG_VERSION" \
|
||
--arg repo_url "$REPO_URL" \
|
||
'.version = $version |
|
||
.repository.url = $repo_url' \
|
||
"$PKG_PATH" > "${PKG_PATH}.tmp" && mv "${PKG_PATH}.tmp" "$PKG_PATH"
|
||
|
||
echo "✅ package.json updated successfully"
|
||
echo ""
|
||
echo "📋 Configuration Summary:"
|
||
echo " Version: $TAG_VERSION"
|
||
echo " Type: ${{ needs.prepare-release.outputs.version_type }}"
|
||
echo " Repository: $REPO_URL"
|
||
echo " Owner: $REPO_OWNER"
|
||
echo " Repo: $REPO_NAME"
|
||
echo " Private: false (fixed)"
|
||
echo " Prerelease: ${{ needs.prepare-release.outputs.is_prerelease }}"
|
||
|
||
- name: 安装依赖
|
||
run: pnpm install
|
||
|
||
- name: 构建 Desktop 应用
|
||
run: pnpm build:desktop:ci
|
||
|
||
- name: 验证构建产物
|
||
run: |
|
||
echo "Checking build artifacts..."
|
||
ls -la packages/desktop/dist/ || echo "No dist directory found"
|
||
|
||
appimage_files=$(find packages/desktop/dist -name "*.AppImage" 2>/dev/null | wc -l)
|
||
zip_files=$(find packages/desktop/dist -name "*.zip" 2>/dev/null | wc -l)
|
||
metadata_files=$(find packages/desktop/dist -name "latest*.yml" 2>/dev/null | wc -l)
|
||
|
||
echo "Found $appimage_files .AppImage files, $zip_files .zip files, and $metadata_files latest*.yml files"
|
||
|
||
if [ "$appimage_files" -eq 0 ] && [ "$zip_files" -eq 0 ]; then
|
||
echo "Error: No build artifacts (.AppImage or .zip) found in packages/desktop/dist/"
|
||
echo "Directory contents:"
|
||
find packages/desktop/dist -type f 2>/dev/null || echo "Directory not found or empty"
|
||
exit 1
|
||
fi
|
||
|
||
if [ "$metadata_files" -eq 0 ]; then
|
||
echo "Error: Linux build is missing updater metadata (latest*.yml)"
|
||
echo "Directory contents:"
|
||
find packages/desktop/dist -type f 2>/dev/null || echo "Directory not found or empty"
|
||
exit 1
|
||
fi
|
||
|
||
echo "Build artifacts verification passed"
|
||
|
||
- name: 清理调试文件
|
||
run: find packages/desktop/dist -type f -name "builder-debug.yml" -delete
|
||
|
||
- name: 直接上传 Linux Release Assets
|
||
shell: bash
|
||
env:
|
||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||
VERSION: ${{ needs.prepare-release.outputs.version }}
|
||
run: |
|
||
shopt -s nullglob
|
||
|
||
files=(
|
||
packages/desktop/dist/*.AppImage
|
||
packages/desktop/dist/*.zip
|
||
packages/desktop/dist/latest*.yml
|
||
)
|
||
|
||
if [ "${#files[@]}" -eq 0 ]; then
|
||
echo "Error: No Linux release assets found to upload"
|
||
exit 1
|
||
fi
|
||
|
||
echo "Uploading Linux release assets:"
|
||
printf ' %s\n' "${files[@]}"
|
||
|
||
gh release upload "$VERSION" \
|
||
"${files[@]}" \
|
||
--repo "${{ github.repository }}" \
|
||
--clobber
|
||
|
||
publish-release:
|
||
needs: [prepare-release, build-windows, build-macos, build-linux]
|
||
runs-on: ubuntu-latest
|
||
permissions:
|
||
contents: write
|
||
steps:
|
||
- name: 发布 Draft Release
|
||
env:
|
||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||
VERSION: ${{ needs.prepare-release.outputs.version }}
|
||
IS_PRERELEASE: ${{ needs.prepare-release.outputs.is_prerelease }}
|
||
run: |
|
||
gh release edit "$VERSION" \
|
||
--repo "${{ github.repository }}" \
|
||
--draft=false \
|
||
--title "$VERSION" \
|
||
$([ "$IS_PRERELEASE" = "true" ] && echo "--prerelease")
|
||
|
||
- name: 验证 Release 元数据
|
||
env:
|
||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||
VERSION: ${{ needs.prepare-release.outputs.version }}
|
||
run: |
|
||
RELEASE_JSON=$(gh release view "$VERSION" \
|
||
--repo "${{ github.repository }}" \
|
||
--json name,body,url,assets,isDraft,isPrerelease)
|
||
|
||
echo "Release metadata:"
|
||
echo "$RELEASE_JSON"
|
||
|
||
EXPECTED_NAME="$VERSION"
|
||
RELEASE_NAME=$(echo "$RELEASE_JSON" | jq -r '.name')
|
||
RELEASE_BODY=$(echo "$RELEASE_JSON" | jq -r '.body')
|
||
RELEASE_IS_DRAFT=$(echo "$RELEASE_JSON" | jq -r '.isDraft')
|
||
RELEASE_IS_PRERELEASE=$(echo "$RELEASE_JSON" | jq -r '.isPrerelease')
|
||
RELEASE_ASSET_NAMES=$(echo "$RELEASE_JSON" | jq -r '.assets[].name')
|
||
WINDOWS_BLOCKMAP_COUNT=$(echo "$RELEASE_ASSET_NAMES" | grep -Ec '^PromptOptimizer-.*-win-.*\.exe\.blockmap$' || true)
|
||
MAC_ZIP_BLOCKMAP_COUNT=$(echo "$RELEASE_ASSET_NAMES" | grep -Ec '^PromptOptimizer-.*-mac-.*\.zip\.blockmap$' || true)
|
||
|
||
if [ "$RELEASE_NAME" != "$EXPECTED_NAME" ]; then
|
||
echo "Release name mismatch: expected '$EXPECTED_NAME', got '$RELEASE_NAME'"
|
||
exit 1
|
||
fi
|
||
|
||
if [ "$RELEASE_IS_DRAFT" != "false" ]; then
|
||
echo "Release should be published, but is still a draft"
|
||
exit 1
|
||
fi
|
||
|
||
if [ -z "$RELEASE_BODY" ] || [ "$RELEASE_BODY" = "null" ]; then
|
||
echo "Release body is empty"
|
||
exit 1
|
||
fi
|
||
|
||
if [ "${{ needs.prepare-release.outputs.is_prerelease }}" = "true" ] && [ "$RELEASE_IS_PRERELEASE" != "true" ]; then
|
||
echo "Expected prerelease metadata but release is not marked as prerelease"
|
||
exit 1
|
||
fi
|
||
|
||
if [ "$WINDOWS_BLOCKMAP_COUNT" -lt 1 ]; then
|
||
echo "Release is missing Windows differential-update blockmap assets"
|
||
echo "$RELEASE_ASSET_NAMES"
|
||
exit 1
|
||
fi
|
||
|
||
if [ "$MAC_ZIP_BLOCKMAP_COUNT" -lt 1 ]; then
|
||
echo "Release is missing macOS zip blockmap assets for differential updates"
|
||
echo "$RELEASE_ASSET_NAMES"
|
||
exit 1
|
||
fi
|