diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..98edbea --- /dev/null +++ b/.github/workflows/release.yml @@ -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 }} diff --git a/README.md b/README.md index ec84c83..8902269 100644 --- a/README.md +++ b/README.md @@ -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. 启动后端服务器 diff --git a/package.json b/package.json index 522e8e1..5bc2cec 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/scripts/release.js b/scripts/release.js new file mode 100644 index 0000000..93ddb10 --- /dev/null +++ b/scripts/release.js @@ -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();