Skip to content

style(format): oxfmt 全量格式化 + 排除 vendored 技能,修复 format:check 门 #2

style(format): oxfmt 全量格式化 + 排除 vendored 技能,修复 format:check 门

style(format): oxfmt 全量格式化 + 排除 vendored 技能,修复 format:check 门 #2

name: Build and Release
permissions:
contents: write
issues: read
pull-requests: read
actions: write
on:
push:
branches: [dev]
tags:
- '*'
env:
BUN_INSTALL_REGISTRY: 'https://registry.npmjs.org/'
jobs:
# 独立 code-quality job,供桌面构建和 web-cli 打包共用,避免重复跑
code-quality:
name: Code Quality
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/dev' || (startsWith(github.ref, 'refs/tags/') && !contains(github.ref, '-dev-'))
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Setup bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Run postinstall
run: bun run postinstall || true
- name: Run ESLint
run: bun run lint
- name: Check Prettier formatting
run: bun run format:check
- name: TypeScript type check
run: bunx tsc --noEmit
- name: Run unit tests
run: bunx vitest run
build-pipeline:
name: Build Pipeline
uses: ./.github/workflows/_build-reusable.yml
needs: code-quality
# dev 分支或正式 tag 触发构建(排除 -dev- tag,避免重复构建)
if: needs.code-quality.result == 'success' && (github.ref == 'refs/heads/dev' || (startsWith(github.ref, 'refs/tags/') && !contains(github.ref, '-dev-')))
with:
skip_code_quality: true
matrix: >-
{"include":[
{"platform":"macos-arm64","os":"macos-14","command":"node scripts/build-with-builder.js arm64 --mac --arm64","artifact-name":"macos-build-arm64","arch":"arm64"},
{"platform":"macos-x64","os":"macos-14","command":"node scripts/build-with-builder.js x64 --mac --x64","artifact-name":"macos-build-x64","arch":"x64"},
{"platform":"windows-x64","os":"windows-2022","command":"node scripts/build-with-builder.js x64 --win --x64","artifact-name":"windows-build-x64","arch":"x64"},
{"platform":"windows-arm64","os":"windows-11-arm","command":"node scripts/build-with-builder.js arm64 --win --arm64","artifact-name":"windows-build-arm64","arch":"arm64"},
{"platform":"linux-x64","os":"ubuntu-latest","command":"node scripts/build-with-builder.js x64 --linux --x64","artifact-name":"linux-build-x64","arch":"x64"},
{"platform":"linux-arm64","os":"ubuntu-24.04-arm","command":"node scripts/build-with-builder.js arm64 --linux --arm64","artifact-name":"linux-build-arm64","arch":"arm64"}
]}
secrets: inherit
pack-web-cli:
name: Pack Web CLI
uses: ./.github/workflows/pack-web-cli.yml
needs: code-quality
# 与桌面构建同频触发:dev 分支或正式 tag(排除 -dev- tag)
if: needs.code-quality.result == 'success' && (github.ref == 'refs/heads/dev' || (startsWith(github.ref, 'refs/tags/') && !contains(github.ref, '-dev-')))
with:
ref: ''
append_commit_hash: false
skip_code_quality: true
secrets: inherit
# 自动重试 workflow(当构建失败时)
auto-retry-workflow:
name: Auto Retry on Build Failure
runs-on: ubuntu-latest
needs: build-pipeline
# 关键:只在首次失败时触发,避免无限循环
if: |
failure() &&
github.run_attempt == 1 &&
(github.event_name == 'push' || github.event_name == 'schedule')
steps:
- name: Log retry information
run: |
echo "=========================================="
echo "🔄 Auto retry triggered (first failure)"
echo "=========================================="
echo "Build failed on first attempt, preparing auto retry..."
echo "Current attempt: ${{ github.run_attempt }}"
echo "Wait strategy: 5 minutes cooldown before retry"
echo "=========================================="
- name: Wait before retry (5 min cooldown)
run: |
echo "⏳ Waiting 5 minutes before retry..."
echo "Start: $(date)"
sleep 300
echo "End: $(date)"
echo "Triggering retry..."
- name: Trigger workflow rerun
run: |
echo "🔄 Triggering full workflow rerun (attempt 2)..."
# Use re-run API (not rerun-failed-jobs, to avoid loops)
response=$(curl -X POST \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
-w "\n%{http_code}" \
https://api.github.com/repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/rerun)
http_code=$(echo "$response" | tail -n1)
if [ "$http_code" = "201" ]; then
echo ""
echo "✅ Retry triggered successfully"
echo "This will be attempt 2"
echo "Details: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
else
echo ""
echo "❌ Retry trigger failed, HTTP status: $http_code"
echo "Response:"
echo "$response" | head -n-1
exit 1
fi
# 自动创建tag(仅 dev 分支推送时)
create-tag:
name: Create Tag from Branch
runs-on: ubuntu-latest
needs: [build-pipeline]
if: success() && github.ref == 'refs/heads/dev'
outputs:
tag_name: ${{ steps.create_tag.outputs.tag_name }}
is_dev: ${{ steps.create_tag.outputs.is_dev }}
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
# GH_TOKEN is a PAT with `repo` + `workflow` scopes — required because
# tag pushes whose reachable history contains .github/workflows/
# changes are rejected for the built-in GITHUB_TOKEN (lacks the
# `workflow` scope, which GITHUB_TOKEN can never grant).
token: ${{ secrets.GH_TOKEN }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Setup bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Create and push tag
id: create_tag
run: |
VERSION=$(node -p "require('./package.json').version")
COMMIT_SHORT=$(git rev-parse --short HEAD)
BRANCH_NAME=${GITHUB_REF#refs/heads/}
# 根据分支确定 tag 格式
if [ "$BRANCH_NAME" = "dev" ]; then
TAG_NAME="v${VERSION}-dev-${COMMIT_SHORT}"
IS_DEV="true"
echo "🔧 Development release: $TAG_NAME"
else
TAG_NAME="v$VERSION"
IS_DEV="false"
echo "🚀 Production release: $TAG_NAME"
fi
echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT
echo "is_dev=$IS_DEV" >> $GITHUB_OUTPUT
# 检查远程tag是否已存在
if git ls-remote --tags origin | grep -q "refs/tags/$TAG_NAME$"; then
if [ "$IS_DEV" = "false" ]; then
# Main 分支:需要递增版本并修改 package.json
echo "⚠️ Tag $TAG_NAME already exists, auto-incrementing version..."
if [[ $VERSION =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
MAJOR=${BASH_REMATCH[1]}
MINOR=${BASH_REMATCH[2]}
PATCH=${BASH_REMATCH[3]}
NEW_PATCH=$((PATCH + 1))
NEW_VERSION="${MAJOR}.${MINOR}.${NEW_PATCH}"
echo "📝 Updating package.json version to $NEW_VERSION"
# 更新 package.json 版本号
bun pm version $NEW_VERSION
# 配置git用户信息
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
# 提交版本更新
git add package.json bun.lock
git commit -m "chore: bump version to $NEW_VERSION"
git remote set-url origin https://x-access-token:${{ secrets.GH_TOKEN }}@github.com/${{ github.repository }}.git
git push origin $BRANCH_NAME
# 获取新的 commit ID
COMMIT_SHORT=$(git rev-parse --short HEAD)
TAG_NAME="v${NEW_VERSION}-${COMMIT_SHORT}"
echo "🚀 New tag with updated version: $TAG_NAME"
echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT
else
TAG_NAME="v${VERSION}-${COMMIT_SHORT}"
echo "⚠️ Fallback: creating tag with commit ID: $TAG_NAME"
echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT
fi
else
# Dev 分支 tag 已存在则失败(不应该发生)
echo "⚠️ Dev tag $TAG_NAME already exists, this shouldn't happen"
exit 1
fi
fi
# 配置git用户信息(如果前面没配置)
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
# 创建并推送tag
echo "Creating tag: $TAG_NAME"
git tag $TAG_NAME
git remote set-url origin https://x-access-token:${{ secrets.GH_TOKEN }}@github.com/${{ github.repository }}.git
git push origin $TAG_NAME
echo "✅ Successfully created and pushed tag: $TAG_NAME"
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
# 发布到 GitHub Releases
release:
name: Create Release
runs-on: ubuntu-latest
needs: [build-pipeline, create-tag, pack-web-cli]
# Tag 推送时 create-tag 会被跳过,所以需要检查两种情况(排除 -dev- tag 避免重复)
if: always() && needs.build-pipeline.result == 'success' && needs.pack-web-cli.result == 'success' && (needs.create-tag.result == 'success' || (startsWith(github.ref, 'refs/tags/') && !contains(github.ref, '-dev-')))
# dev 分支使用 dev-release 环境,正式 tag 使用 release 环境
environment: ${{ needs.create-tag.outputs.is_dev == 'true' && 'dev-release' || 'release' }}
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Get version for release
id: version
run: |
# 判断触发来源:正式 tag 推送 or dev 分支
if [[ "$GITHUB_REF" == refs/tags/* ]]; then
# 正式 Tag 推送触发(-dev- tag 已被排除,不会到达这里)
TAG_NAME="${GITHUB_REF#refs/tags/}"
IS_DEV="false"
echo "🚀 Creating stable release from tag: $TAG_NAME"
else
# dev 分支触发,使用 create-tag job 的输出
TAG_NAME="${{ needs.create-tag.outputs.tag_name }}"
IS_DEV="${{ needs.create-tag.outputs.is_dev }}"
echo "🔧 Creating development release: $TAG_NAME"
fi
echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT
echo "is_dev=$IS_DEV" >> $GITHUB_OUTPUT
- name: Download all build artifacts
uses: actions/download-artifact@v7
with:
path: build-artifacts
- name: Prepare release assets (normalize updater metadata)
shell: bash
run: bash scripts/prepare-release-assets.sh build-artifacts release-assets
- name: Create Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.version.outputs.tag_name }}
name: ${{ steps.version.outputs.is_dev == 'true' && format('Development Build {0}', steps.version.outputs.tag_name) || steps.version.outputs.tag_name }}
files: |
release-assets/**/*.exe
release-assets/**/*.msi
release-assets/**/*.dmg
release-assets/**/*.deb
release-assets/**/*.zip
release-assets/**/*.yml
release-assets/**/*.tar.gz
release-assets/**/*.sha256
release-assets/install-web.sh
generate_release_notes: true
draft: true
prerelease: ${{ steps.version.outputs.is_dev == 'true' || contains(steps.version.outputs.tag_name, 'beta') || contains(steps.version.outputs.tag_name, 'alpha') || contains(steps.version.outputs.tag_name, 'rc') }}
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}