feat: add GitHub Actions auto-release workflow

This commit is contained in:
Ethanfly 2026-01-05 13:52:21 +08:00
parent 13f9e5b71c
commit 468e05f7cc
4 changed files with 250 additions and 0 deletions

96
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,96 @@
name: Build and Release
on:
push:
tags:
- 'v*' # 当推送 v 开头的标签时触发,如 v1.0.0
jobs:
build:
name: Build on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-latest, macos-latest, ubuntu-latest]
include:
- os: windows-latest
artifact_name: EasyShell-Windows
build_cmd: npm run build && npx electron-builder --win
- os: macos-latest
artifact_name: EasyShell-Mac
build_cmd: npm run build && npx electron-builder --mac
- os: ubuntu-latest
artifact_name: EasyShell-Linux
build_cmd: npm run build && npx electron-builder --linux
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Generate icons
run: npm run icons
continue-on-error: true
- name: Build application
run: ${{ matrix.build_cmd }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact_name }}
path: |
dist/*.exe
dist/*.dmg
dist/*.AppImage
dist/*.deb
if-no-files-found: ignore
release:
name: Create Release
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Display structure of downloaded files
run: ls -R artifacts
- name: Get version from tag
id: get_version
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
- name: Create Release
uses: softprops/action-gh-release@v1
with:
name: EasyShell ${{ steps.get_version.outputs.VERSION }}
tag_name: ${{ steps.get_version.outputs.VERSION }}
draft: false
prerelease: false
generate_release_notes: true
files: |
artifacts/**/*.exe
artifacts/**/*.dmg
artifacts/**/*.AppImage
artifacts/**/*.deb
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -171,6 +171,49 @@ npm run dist
---
## 🏷️ 版本发布
项目已配置 GitHub Actions 自动化发布流程。
### 一键发布
```bash
# 发布补丁版本 (1.0.0 -> 1.0.1)
npm run release
# 发布次要版本 (1.0.0 -> 1.1.0)
npm run release:minor
# 发布主要版本 (1.0.0 -> 2.0.0)
npm run release:major
```
发布脚本会自动:
1. ✅ 更新 `package.json` 版本号
2. ✅ 更新界面显示的版本号
3. ✅ 提交更改
4. ✅ 创建 Git 标签
5. ✅ 推送到 GitHub
6. ✅ 触发 GitHub Actions 自动构建
### 自动构建
当推送 `v*` 格式的标签时(如 `v1.0.0`GitHub Actions 将自动:
| 平台 | 产物 |
|------|------|
| Windows | `EasyShell Setup x.x.x.exe` |
| macOS | `EasyShell-x.x.x.dmg` |
| Linux | `EasyShell-x.x.x.AppImage` |
构建完成后会自动创建 GitHub Release 并上传安装包。
### 查看构建状态
[![Build Status](https://github.com/ethanfly/easyshell/actions/workflows/release.yml/badge.svg)](https://github.com/ethanfly/easyshell/actions)
---
## 📱 移动端部署
### 1. 启动后端服务器

View File

@ -17,6 +17,9 @@
"version:patch": "node scripts/bump-version.js patch",
"version:minor": "node scripts/bump-version.js minor",
"version:major": "node scripts/bump-version.js major",
"release": "node scripts/release.js patch",
"release:minor": "node scripts/release.js minor",
"release:major": "node scripts/release.js major",
"icons": "node scripts/generate-icons.js",
"server": "cd server && npm start",
"server:dev": "cd server && npm run dev",

108
scripts/release.js Normal file
View File

@ -0,0 +1,108 @@
/**
* 发布脚本
* 自动更新版本号提交打标签并推送到 GitHub
*
* 用法:
* npm run release # patch 版本 (1.0.0 -> 1.0.1)
* npm run release:minor # minor 版本 (1.0.0 -> 1.1.0)
* npm run release:major # major 版本 (1.0.0 -> 2.0.0)
*/
const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');
const packageJsonPath = path.resolve(__dirname, '../package.json');
const titleBarPath = path.resolve(__dirname, '../src/components/TitleBar.js');
function exec(cmd, options = {}) {
console.log(`\n🔧 执行: ${cmd}`);
try {
execSync(cmd, { stdio: 'inherit', ...options });
} catch (error) {
console.error(`❌ 命令执行失败: ${cmd}`);
process.exit(1);
}
}
function bumpVersion(type = 'patch') {
// 读取 package.json
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
let [major, minor, patch] = packageJson.version.split('.').map(Number);
// 递增版本号
if (type === 'major') {
major++;
minor = 0;
patch = 0;
} else if (type === 'minor') {
minor++;
patch = 0;
} else {
patch++;
}
const newVersion = `${major}.${minor}.${patch}`;
packageJson.version = newVersion;
// 更新 package.json
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 4) + '\n');
console.log(`📦 版本号已更新: ${newVersion}`);
// 更新 TitleBar.js
if (fs.existsSync(titleBarPath)) {
let titleBarContent = fs.readFileSync(titleBarPath, 'utf8');
const newTitleBarContent = titleBarContent.replace(
/v\d+\.\d+\.\d+/g,
`v${newVersion}`
);
fs.writeFileSync(titleBarPath, newTitleBarContent, 'utf8');
console.log('📝 TitleBar.js 版本号已更新');
}
return newVersion;
}
function release() {
const versionType = process.argv[2] || 'patch';
console.log('🚀 开始发布流程...\n');
console.log(`📋 版本类型: ${versionType}`);
// 1. 检查是否有未提交的更改
try {
const status = execSync('git status --porcelain', { encoding: 'utf8' });
if (status.trim()) {
console.log('\n⚠ 检测到未提交的更改,将一起提交...');
}
} catch (e) {
console.error('❌ Git 状态检查失败');
process.exit(1);
}
// 2. 更新版本号
const newVersion = bumpVersion(versionType);
const tagName = `v${newVersion}`;
// 3. 添加所有更改
exec('git add .');
// 4. 提交更改
exec(`git commit -m "chore: release ${tagName}"`);
// 5. 创建标签
exec(`git tag -a ${tagName} -m "Release ${tagName}"`);
console.log(`\n🏷️ 标签已创建: ${tagName}`);
// 6. 推送到远程
console.log('\n📤 推送到远程仓库...');
exec('git push origin main');
exec(`git push origin ${tagName}`);
console.log(`\n✅ 发布完成!`);
console.log(` 版本: ${tagName}`);
console.log(` GitHub Actions 将自动开始构建...`);
console.log(`\n🔗 查看构建进度: https://github.com/ethanfly/easyshell/actions`);
}
release();