diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 0eac85e..8c6ca5a 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -7,7 +7,7 @@ on: - "v*" permissions: - contents: read + contents: write jobs: package: @@ -17,15 +17,26 @@ jobs: fail-fast: false matrix: include: - - platform: windows + - artifact: windows-x64 + platform: windows os: windows-latest - command: dist:win - - platform: linux + target: win + arch: x64 + - artifact: linux-x64 + platform: linux os: ubuntu-22.04 - command: dist:linux - - platform: macos + target: linux + arch: x64 + - artifact: macos-x64 + platform: macos os: macos-14 - command: dist:mac + target: mac + arch: x64 + - artifact: macos-arm64 + platform: macos + os: macos-14 + target: mac + arch: arm64 env: CSC_IDENTITY_AUTO_DISCOVERY: "false" @@ -44,12 +55,47 @@ jobs: run: npm ci - name: Build packages - run: npm run ${{ matrix.command }} + run: | + npm run build + npm run pack:${{ matrix.target }} -- --${{ matrix.arch }} + + - name: Write checksums + run: node scripts/write-checksums.cjs release/${{ matrix.platform }} ${{ matrix.artifact }} - name: Upload packages uses: actions/upload-artifact@v4 with: - name: ArborFlow-${{ matrix.platform }} - path: release/${{ matrix.platform }}/ + name: ArborFlow-${{ matrix.artifact }} + path: | + release/${{ matrix.platform }}/*.exe + release/${{ matrix.platform }}/*.blockmap + release/${{ matrix.platform }}/*.AppImage + release/${{ matrix.platform }}/*.deb + release/${{ matrix.platform }}/*.dmg + release/${{ matrix.platform }}/*.zip + release/${{ matrix.platform }}/SHA256SUMS-${{ matrix.artifact }}.txt if-no-files-found: error compression-level: 0 + + release: + name: Publish GitHub Release + if: startsWith(github.ref, 'refs/tags/v') + needs: package + runs-on: ubuntu-22.04 + + steps: + - name: Download package artifacts + uses: actions/download-artifact@v4 + with: + pattern: ArborFlow-* + path: release-assets + merge-multiple: true + + - name: Create release + env: + GH_TOKEN: ${{ github.token }} + run: | + gh release create "$GITHUB_REF_NAME" release-assets/* \ + --verify-tag \ + --generate-notes \ + --title "ArborFlow $GITHUB_REF_NAME" diff --git a/README.md b/README.md index b128d20..9d1c897 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,38 @@ # ArborFlow +[![Build release packages](https://github.com/RoboMaster-DLMU-CONE/Arborflow/actions/workflows/build-release.yml/badge.svg)](https://github.com/RoboMaster-DLMU-CONE/Arborflow/actions/workflows/build-release.yml) +[![GitHub release](https://img.shields.io/github/v/release/RoboMaster-DLMU-CONE/Arborflow)](https://github.com/RoboMaster-DLMU-CONE/Arborflow/releases/latest) +[![License](https://img.shields.io/github/license/RoboMaster-DLMU-CONE/Arborflow)](LICENSE) + ArborFlow 是一个面向 [BehaviorTree.CPP](https://github.com/BehaviorTree/BehaviorTree.CPP) 的跨平台行为树生成、编辑与运行状态可视化工具。 项目使用 Electron、React、TypeScript 和 React Flow 构建,可在 Windows、Linux 与 macOS 上运行。一个工程可以包含多棵行为树,并通过 `SubTree` 节点相互引用。 +## 下载与安装 + +安装包统一发布在 [GitHub Releases](https://github.com/RoboMaster-DLMU-CONE/Arborflow/releases/latest)。自动构建提供以下版本: + +| 平台 | 架构 | 发行格式 | +| --- | --- | --- | +| Windows | x64 | NSIS 安装包、Portable 便携版 | +| Linux | x64 | AppImage、deb | +| macOS | Intel x64、Apple Silicon arm64 | dmg、zip | + +Linux AppImage 首次运行前需要增加执行权限: + +```bash +chmod +x ArborFlow-*.AppImage +./ArborFlow-*.AppImage +``` + +deb 包可使用系统包管理器安装: + +```bash +sudo apt install ./arborflow_*.deb +``` + +当前自动构建未配置商业代码签名证书。Windows 可能显示 SmartScreen 提示;macOS 首次运行时可在 Finder 中右键应用并选择“打开”。每个 Release 都附带分平台的 `SHA256SUMS-*.txt` 校验文件。 + ## 主要功能 - 拖拽创建节点并通过端口连接父子关系 @@ -220,7 +249,7 @@ npm run dist:mac 输出按平台分别保存到 `release/windows/`、`release/linux/` 和 `release/macos/`。建议在目标操作系统上执行对应打包命令,特别是 macOS 签名、公证以及不同 CPU 架构的构建。 -仓库内的 `.github/workflows/build-release.yml` 会在 Windows、Ubuntu 和 macOS 原生运行器上并行打包。可在 GitHub Actions 中手动运行,也会在推送 `v*` 标签时自动执行并上传三个独立平台产物。 +仓库内的 `.github/workflows/build-release.yml` 会在 Windows、Ubuntu 和 macOS 原生运行器上并行打包。可在 GitHub Actions 中手动运行;推送 `v*` 标签时会自动创建 GitHub Release,并上传安装包及 SHA-256 校验文件。 可用的默认目标: diff --git a/package.json b/package.json index 8b3dbc4..c1bf765 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,14 @@ "main": "electron/main.cjs", "author": "ArborFlow Contributors", "license": "MIT", + "homepage": "https://github.com/RoboMaster-DLMU-CONE/Arborflow#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/RoboMaster-DLMU-CONE/Arborflow.git" + }, + "bugs": { + "url": "https://github.com/RoboMaster-DLMU-CONE/Arborflow/issues" + }, "scripts": { "dev": "concurrently -k \"vite --host 127.0.0.1\" \"wait-on http://127.0.0.1:5173 && electron .\"", "build": "tsc -b && vite build", diff --git a/scripts/write-checksums.cjs b/scripts/write-checksums.cjs new file mode 100644 index 0000000..e0a47c5 --- /dev/null +++ b/scripts/write-checksums.cjs @@ -0,0 +1,53 @@ +const { createHash } = require('node:crypto'); +const { createReadStream, promises: fs } = require('node:fs'); +const path = require('node:path'); + +const [directory, artifactName] = process.argv.slice(2); +if (!directory || !artifactName) { + console.error('Usage: node scripts/write-checksums.cjs '); + process.exit(1); +} + +const releaseExtensions = new Set([ + '.AppImage', + '.blockmap', + '.deb', + '.dmg', + '.exe', + '.zip', +]); + +function hashFile(filePath) { + return new Promise((resolve, reject) => { + const hash = createHash('sha256'); + const stream = createReadStream(filePath); + stream.on('data', (chunk) => hash.update(chunk)); + stream.on('error', reject); + stream.on('end', () => resolve(hash.digest('hex'))); + }); +} + +async function main() { + const entries = await fs.readdir(directory, { withFileTypes: true }); + const files = entries + .filter((entry) => entry.isFile() && releaseExtensions.has(path.extname(entry.name))) + .sort((left, right) => left.name.localeCompare(right.name)); + + if (files.length === 0) { + throw new Error(`No release packages found in ${directory}`); + } + + const lines = []; + for (const file of files) { + lines.push(`${await hashFile(path.join(directory, file.name))} ${file.name}`); + } + + const output = path.join(directory, `SHA256SUMS-${artifactName}.txt`); + await fs.writeFile(output, `${lines.join('\n')}\n`, 'ascii'); + console.log(`Wrote ${output}`); +} + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +});