Skip to content

发布流程 (Release Pipeline) #21

发布流程 (Release Pipeline)

发布流程 (Release Pipeline) #21

Workflow file for this run

name: 发布流程 (Release Pipeline)
on:
# 允许手动触发:用于发布新版本(包含自动版本升级、打标签、构建和发布)
workflow_dispatch:
inputs:
version_type:
description: '版本类型 (patch=修复, minor=功能, major=重大变更)'
required: true
default: patch
type: choice
options:
- patch
- minor
- major
- custom
custom_version:
description: '自定义版本号 (格式: x.y.z, 仅当类型选为 custom 时有效)'
required: false
type: string
# 允许 Tag 触发:用于重新构建或手动打 Tag 后的发布
push:
tags: ['v*']
jobs:
# -----------------------------------------------------------------------------
# 阶段 1: 准备发布 (版本管理 & 标签)
# -----------------------------------------------------------------------------
prepare:
name: 准备发布
runs-on: ubuntu-latest
permissions:
contents: write # 需要写入权限来推送 Tag
outputs:
version: ${{ steps.determine_version.outputs.version }}
tag_name: ${{ steps.determine_version.outputs.tag_name }}
should_build: ${{ steps.check_trigger.outputs.should_build }}
steps:
- name: 检出代码
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: 配置 Git 用户
if: github.event_name == 'workflow_dispatch'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: 确定版本号
id: determine_version
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
# --- 手动触发逻辑:计算新版本 ---
# 1. 获取当前版本
if [ -f "Cargo.toml" ]; then
CURRENT_VERSION=$(grep '^version = ' Cargo.toml | head -1 | cut -d'"' -f2)
else
CURRENT_VERSION="0.0.0"
fi
echo "Current version: $CURRENT_VERSION"
# 2. 计算新版本
TYPE="${{ inputs.version_type }}"
CUSTOM="${{ inputs.custom_version }}"
if [ "$TYPE" = "custom" ]; then
if [ -z "$CUSTOM" ]; then
echo "Error: Custom version is required when type is 'custom'"
exit 1
fi
NEW_VERSION="$CUSTOM"
else
IFS='.' read -r major minor patch <<< "$CURRENT_VERSION"
case $TYPE in
"major") major=$((major + 1)); minor=0; patch=0 ;;
"minor") minor=$((minor + 1)); patch=0 ;;
"patch") patch=$((patch + 1)) ;;
esac
NEW_VERSION="$major.$minor.$patch"
fi
echo "New version: $NEW_VERSION"
echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT
echo "tag_name=v$NEW_VERSION" >> $GITHUB_OUTPUT
echo "is_new_release=true" >> $GITHUB_OUTPUT
else
# --- Tag 触发逻辑:使用现有 Tag ---
TAG_NAME="${{ github.ref_name }}"
VERSION=${TAG_NAME#v}
echo "Triggered by tag: $TAG_NAME"
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT
echo "is_new_release=false" >> $GITHUB_OUTPUT
fi
- name: 更新版本文件并推送 (仅手动触发)
if: github.event_name == 'workflow_dispatch'
run: |
NEW_VERSION="${{ steps.determine_version.outputs.version }}"
TAG_NAME="${{ steps.determine_version.outputs.tag_name }}"
CURRENT_DATE=$(date +"%Y-%m-%d")
echo "Updating files to version $NEW_VERSION..."
# 更新 Cargo.toml
if [ -f "Cargo.toml" ]; then
sed -i.bak "s/^version = \"[^\"]*\"/version = \"$NEW_VERSION\"/" Cargo.toml && rm Cargo.toml.bak
fi
# 更新 package.json
if [ -f "package.json" ]; then
sed -i.bak "s/\"version\"[[:space:]]*:[[:space:]]*\"[^\"]*\"/\"version\": \"$NEW_VERSION\"/" package.json && rm package.json.bak
fi
# 更新 tauri.conf.json
if [ -f "tauri.conf.json" ]; then
sed -i.bak "s/\"version\"[[:space:]]*:[[:space:]]*\"[^\"]*\"/\"version\": \"$NEW_VERSION\"/" tauri.conf.json && rm tauri.conf.json.bak
fi
# 更新 version.json
if [ -f "version.json" ]; then
sed -i.bak "s/\"version\"[[:space:]]*:[[:space:]]*\"[^\"]*\"/\"version\": \"$NEW_VERSION\"/" version.json && rm version.json.bak
sed -i.bak "s/\"build_date\"[[:space:]]*:[[:space:]]*\"[^\"]*\"/\"build_date\": \"$CURRENT_DATE\"/" version.json && rm version.json.bak
fi
# 提交并推送
git add .
git commit -m "release: Release $NEW_VERSION"
git tag -a "$TAG_NAME" -m "Release $NEW_VERSION"
echo "Pushing changes and tag..."
git push origin main
git push origin "$TAG_NAME"
# -----------------------------------------------------------------------------
# 阶段 2: 多平台构建 (Build)
# -----------------------------------------------------------------------------
build:
name: 构建 CLI (${{ matrix.name }})
needs: prepare
runs-on: ${{ matrix.platform }}
strategy:
fail-fast: false
matrix:
include:
- platform: macos-latest
args: --target aarch64-apple-darwin
name: macos-aarch64
- platform: macos-latest
args: --target x86_64-apple-darwin
name: macos-x86_64
- platform: ubuntu-22.04
args: ''
name: linux-x86_64
- platform: windows-latest
args: ''
name: windows-x86_64
steps:
- name: 检出代码
uses: actions/checkout@v4
with:
# 关键:检出特定的 Tag,确保所有构建机器使用相同的代码状态
ref: ${{ needs.prepare.outputs.tag_name }}
- name: 安装 Linux 系统依赖
if: matrix.platform == 'ubuntu-22.04'
run: |
sudo apt-get update
sudo apt-get install -y \
libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf pkg-config \
libglib2.0-dev libgtk-3-dev libgdk-pixbuf2.0-dev libpango1.0-dev libatk1.0-dev \
libcairo-gobject2 libjavascriptcoregtk-4.1-dev libasound2-dev libpulse-dev libjack-dev
- name: 安装 Node.js
uses: actions/setup-node@v4
with:
node-version: lts/*
- name: 安装 pnpm
uses: pnpm/action-setup@v4
- name: 安装 Rust stable
uses: dtolnay/rust-toolchain@stable
with:
# macOS 需要同时安装 x86_64 和 aarch64 target
targets: ${{ matrix.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
- name: 配置 Rust 缓存
uses: swatinem/rust-cache@v2
- name: 安装前端依赖
run: pnpm install
- name: 安装 Tauri CLI
run: cargo install tauri-cli --version "^2.0" --locked
- name: 构建二进制文件
shell: bash
run: |
echo "Building for ${{ matrix.name }}..."
if [[ "${{ matrix.platform }}" == "macos-latest" ]]; then
cargo tauri build ${{ matrix.args }} --no-bundle
else
cargo tauri build --no-bundle
fi
- name: 打包产物
shell: bash
run: |
mkdir -p cli-package
TAG_NAME="${{ needs.prepare.outputs.tag_name }}"
# 确定产物路径
if [[ "${{ matrix.platform }}" == "macos-latest" ]]; then
if [[ "${{ matrix.args }}" == *"aarch64"* ]]; then
TARGET_DIR="target/aarch64-apple-darwin/release"
else
TARGET_DIR="target/x86_64-apple-darwin/release"
fi
else
TARGET_DIR="target/release"
fi
# 复制并压缩
if [[ "${{ matrix.platform }}" == "windows-latest" ]]; then
cp "$TARGET_DIR/等一下.exe" cli-package/
cp "$TARGET_DIR/三术.exe" cli-package/
cd cli-package
7z a ../sanshu-cli-${TAG_NAME}-${{ matrix.name }}.zip *
else
cp "$TARGET_DIR/等一下" cli-package/
cp "$TARGET_DIR/三术" cli-package/
cd cli-package
tar -czf ../sanshu-cli-${TAG_NAME}-${{ matrix.name }}.tar.gz *
fi
- name: 上传构建产物
uses: actions/upload-artifact@v4
with:
name: sanshu-cli-${{ matrix.name }}
path: |
sanshu-cli-*.tar.gz
sanshu-cli-*.zip
if-no-files-found: error
# -----------------------------------------------------------------------------
# 阶段 3: 发布 (Publish)
# -----------------------------------------------------------------------------
release:
name: 发布 Release
needs: [prepare, build]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: 检出代码
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ needs.prepare.outputs.tag_name }}
- name: 下载所有构建产物
uses: actions/download-artifact@v4
with:
path: artifacts
- name: 安装 git-cliff (用于生成 Changelog)
uses: taiki-e/install-action@git-cliff
- name: 生成 Changelog
id: changelog
run: |
TAG_NAME="${{ needs.prepare.outputs.tag_name }}"
# 尝试获取上一个 Tag
PREVIOUS_TAG=$(git tag --sort=-version:refname | grep -E '^v[0-9]+' | grep -A 1 "^$TAG_NAME$" | tail -n 1 || echo "")
echo "Generating changelog for $TAG_NAME (Previous: $PREVIOUS_TAG)"
if [ -z "$PREVIOUS_TAG" ] || [ "$PREVIOUS_TAG" == "$TAG_NAME" ]; then
git-cliff --tag "$TAG_NAME" --output changelog.md
else
git-cliff "$PREVIOUS_TAG..$TAG_NAME" --output changelog.md
fi
# 生成发布标题 (提取主要特性)
VERSION=${TAG_NAME#v}
if [ -n "$PREVIOUS_TAG" ]; then
MAIN_FEATURE=$(git log --oneline "$PREVIOUS_TAG..$TAG_NAME" | grep -E "^[a-f0-9]+ (feat|fix)" | head -1 | sed 's/^[a-f0-9]* //' | sed 's/^feat: /✨ /' | sed 's/^fix: /🐞 /')
fi
if [ -z "$MAIN_FEATURE" ]; then
echo "release_title=$VERSION 📦 版本更新" >> $GITHUB_OUTPUT
else
echo "release_title=$VERSION $MAIN_FEATURE" >> $GITHUB_OUTPUT
fi
- name: 创建 GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ needs.prepare.outputs.tag_name }}
name: ${{ steps.changelog.outputs.release_title }}
body_path: changelog.md
draft: false
prerelease: false
files: |
artifacts/*/sanshu-cli-*.tar.gz
artifacts/*/sanshu-cli-*.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: 更新 README 中的版本信息
run: |
TAG_NAME="${{ needs.prepare.outputs.tag_name }}"
VERSION_NUMBER="${{ needs.prepare.outputs.version }}"
DATE=$(date +%Y-%m-%d)
echo "📝 Updating README with version: $VERSION_NUMBER (tag: $TAG_NAME)"
# 使用不冲突分隔符并匹配可选 v 前缀,避免 sed 解析错误
sed -i -E "s#^> \\*\\*📋 版本信息\\*\\*:当前最新版本 v?[0-9.]+ \\| 上次更新:.*#> **📋 版本信息**:当前最新版本 ${VERSION_NUMBER} | 上次更新:${DATE}#g" README.md
# 下载链接中同时更新 tag 路径和文件名里的版本号
sed -i -E "s#v[0-9.]+/sanshu-cli-v[0-9.]+-#${TAG_NAME}/sanshu-cli-${TAG_NAME}-#g" README.md
# 显示更改
echo "=== README changes ==="
git diff README.md || true
- name: 提交 README 更新
run: |
TAG_NAME="${{ needs.prepare.outputs.tag_name }}"
VERSION_NUMBER="${{ needs.prepare.outputs.version }}"
if git diff --quiet README.md; then
echo "No changes to README, skipping commit"
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add README.md
git commit -m "docs: update README to version ${VERSION_NUMBER}
- Update download links to ${TAG_NAME}
- Update version info and last updated date
- Auto-generated by release workflow"
git push origin HEAD:main
echo "✅ README updated and pushed successfully"
- name: 触发 Homebrew 更新
uses: actions/github-script@v7
continue-on-error: true
with:
script: |
console.log('Triggering Homebrew update...');
try {
await github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: 'update-homebrew.yml',
ref: 'main',
inputs: { tag_name: '${{ needs.prepare.outputs.tag_name }}' }
});
console.log('✅ Homebrew update triggered');
} catch (error) {
console.error('⚠️ Could not trigger Homebrew update:', error);
}