Skip to content

Build and Release Desktop App #28

Build and Release Desktop App

Build and Release Desktop App #28

Workflow file for this run

name: 构建并发布桌面应用
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
version:
description: '版本号 (例如: v0.1.0)'
required: true
default: 'v0.1.0'
# 注意:此工作流仅在打 tag 或手动触发时运行,不会在每次 push 时运行
permissions:
contents: write
packages: write
jobs:
# 构建多平台桌面应用(直接使用 dist 静态文件)
build-desktop:
name: 构建 ${{ matrix.platform }} 版本
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- os: ubuntu-latest
platform: Linux
artifact-name: CountBot-Linux-x64
- os: windows-latest
platform: Windows
artifact-name: CountBot-Windows-x64
- os: macos-latest
platform: macOS
artifact-name: CountBot-macOS-x64
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 验证前端静态文件
run: |
if [ -d "frontend/dist" ] && [ -f "frontend/dist/index.html" ]; then
echo "✓ 前端静态文件存在"
ls -la frontend/dist/
else
echo "✗ 前端静态文件不存在"
exit 1
fi
shell: bash
- name: 设置 Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
- name: 安装 Python 依赖
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pyinstaller
- name: 设置图标路径
id: icon
shell: bash
run: |
if [ "$RUNNER_OS" == "macOS" ]; then
echo "path=resources/countbot.icns" >> $GITHUB_OUTPUT
elif [ "$RUNNER_OS" == "Windows" ]; then
echo "path=resources/countbot.ico" >> $GITHUB_OUTPUT
else
echo "path=resources/countbot.png" >> $GITHUB_OUTPUT
fi
- name: 构建可执行文件 (Linux/macOS)
if: runner.os != 'Windows'
run: |
pyinstaller --clean --noconfirm \
--name=CountBot \
--onedir \
--windowed \
--icon="${{ steps.icon.outputs.path }}" \
--add-data="frontend/dist:frontend/dist" \
--add-data="workspace/skills:workspace/skills" \
--add-data="memory:memory" \
--add-data="resources:resources" \
--collect-data litellm \
--collect-data dateparser \
--collect-data tiktoken_ext \
--collect-data croniter \
--collect-data trafilatura \
--collect-data lxml \
--hidden-import=tiktoken_ext.openai_public \
--hidden-import=tiktoken_ext \
--hidden-import=greenlet \
--hidden-import=greenlet._greenlet \
--hidden-import=backend \
--hidden-import=backend.app \
--hidden-import=backend.database \
--hidden-import=backend.utils.paths \
--hidden-import=backend.utils.logger \
--hidden-import=backend.api.auth \
--hidden-import=backend.api.chat \
--hidden-import=backend.api.channels \
--hidden-import=backend.api.settings \
--hidden-import=backend.api.tools \
--hidden-import=backend.api.skills \
--hidden-import=backend.api.memory \
--hidden-import=backend.api.tasks \
--hidden-import=backend.api.system \
--hidden-import=backend.api.audio \
--hidden-import=backend.api.queue \
--hidden-import=backend.api.cron \
--hidden-import=backend.api.personalities \
--hidden-import=backend.models.message \
--hidden-import=backend.models.session \
--hidden-import=backend.models.setting \
--hidden-import=backend.models.task \
--hidden-import=backend.models.cron_job \
--hidden-import=backend.models.tool_conversation \
--hidden-import=backend.models.personality \
--hidden-import=uvicorn \
--hidden-import=uvicorn.logging \
--hidden-import=uvicorn.loops \
--hidden-import=uvicorn.loops.auto \
--hidden-import=uvicorn.protocols \
--hidden-import=uvicorn.protocols.http \
--hidden-import=uvicorn.protocols.http.auto \
--hidden-import=uvicorn.protocols.websockets \
--hidden-import=uvicorn.protocols.websockets.auto \
--hidden-import=uvicorn.lifespan \
--hidden-import=uvicorn.lifespan.on \
--hidden-import=fastapi \
--hidden-import=sqlalchemy.ext.asyncio \
--hidden-import=aiosqlite \
--exclude-module=playwright \
--exclude-module=pandas \
--exclude-module=numpy \
--exclude-module=PyQt5 \
--exclude-module=PyQt6 \
--exclude-module=PySide2 \
--exclude-module=PySide6 \
--exclude-module=tkinter \
--exclude-module=matplotlib \
--exclude-module=scipy \
--exclude-module=PIL.ImageQt \
--exclude-module=transformers \
--exclude-module=torch \
--exclude-module=tensorflow \
--exclude-module=psycopg2 \
--exclude-module=asyncpg \
start_desktop.py
- name: 构建可执行文件 (Windows)
if: runner.os == 'Windows'
run: |
pyinstaller --clean --noconfirm `
--name=CountBot `
--onedir `
--windowed `
--icon="${{ steps.icon.outputs.path }}" `
--add-data="frontend/dist;frontend/dist" `
--add-data="workspace/skills;workspace/skills" `
--add-data="memory;memory" `
--add-data="resources;resources" `
--collect-data litellm `
--collect-data dateparser `
--collect-data tiktoken_ext `
--collect-data croniter `
--collect-data trafilatura `
--collect-data lxml `
--hidden-import=tiktoken_ext.openai_public `
--hidden-import=tiktoken_ext `
--hidden-import=greenlet `
--hidden-import=greenlet._greenlet `
--hidden-import=backend `
--hidden-import=backend.app `
--hidden-import=backend.database `
--hidden-import=backend.utils.paths `
--hidden-import=backend.utils.logger `
--hidden-import=backend.api.auth `
--hidden-import=backend.api.chat `
--hidden-import=backend.api.channels `
--hidden-import=backend.api.settings `
--hidden-import=backend.api.tools `
--hidden-import=backend.api.skills `
--hidden-import=backend.api.memory `
--hidden-import=backend.api.tasks `
--hidden-import=backend.api.system `
--hidden-import=backend.api.audio `
--hidden-import=backend.api.queue `
--hidden-import=backend.api.cron `
--hidden-import=backend.api.personalities `
--hidden-import=backend.models.message `
--hidden-import=backend.models.session `
--hidden-import=backend.models.setting `
--hidden-import=backend.models.task `
--hidden-import=backend.models.cron_job `
--hidden-import=backend.models.tool_conversation `
--hidden-import=backend.models.personality `
--hidden-import=uvicorn `
--hidden-import=uvicorn.logging `
--hidden-import=uvicorn.loops `
--hidden-import=uvicorn.loops.auto `
--hidden-import=uvicorn.protocols `
--hidden-import=uvicorn.protocols.http `
--hidden-import=uvicorn.protocols.http.auto `
--hidden-import=uvicorn.protocols.websockets `
--hidden-import=uvicorn.protocols.websockets.auto `
--hidden-import=uvicorn.lifespan `
--hidden-import=uvicorn.lifespan.on `
--hidden-import=fastapi `
--hidden-import=sqlalchemy.ext.asyncio `
--hidden-import=aiosqlite `
--hidden-import=webview `
--hidden-import=webview.platforms.edgechromium `
--hidden-import=webview.platforms.winforms `
--exclude-module=playwright `
--exclude-module=pandas `
--exclude-module=numpy `
--exclude-module=PyQt5 `
--exclude-module=PyQt6 `
--exclude-module=PySide2 `
--exclude-module=PySide6 `
--exclude-module=tkinter `
--exclude-module=matplotlib `
--exclude-module=scipy `
--exclude-module=PIL.ImageQt `
--exclude-module=transformers `
--exclude-module=torch `
--exclude-module=tensorflow `
--exclude-module=psycopg2 `
--exclude-module=asyncpg `
start_desktop.py
- name: 打包发布文件 (macOS)
if: runner.os == 'macOS'
run: |
cd dist
tar -czf ${{ matrix.artifact-name }}.tar.gz CountBot.app/
ls -lh *.tar.gz
- name: 打包发布文件 (Linux)
if: runner.os == 'Linux'
run: |
cd dist
tar -czf ${{ matrix.artifact-name }}.tar.gz CountBot/
ls -lh *.tar.gz
- name: 打包发布文件 (Windows)
if: runner.os == 'Windows'
run: |
cd dist
Compress-Archive -Path CountBot -DestinationPath ${{ matrix.artifact-name }}.zip
Get-ChildItem *.zip
- name: 上传构建产物
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact-name }}
path: |
dist/*.tar.gz
dist/*.zip
retention-days: 7
# 创建 GitHub Release
create-release:
name: 创建 GitHub Release
needs: build-desktop
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch'
steps:
- name: 检出代码
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: 设置版本号
id: version
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
echo "version=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT
echo "is_manual=true" >> $GITHUB_OUTPUT
else
echo "version=${{ github.ref_name }}" >> $GITHUB_OUTPUT
echo "is_manual=false" >> $GITHUB_OUTPUT
fi
- name: 下载所有构建产物
uses: actions/download-artifact@v4
with:
path: artifacts/
- name: 显示构建产物
run: |
echo "构建产物列表:"
find artifacts -type f
echo ""
echo "文件详情:"
find artifacts -type f -exec ls -lh {} \;
- name: 创建 Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.version.outputs.version }}
name: CountBot ${{ steps.version.outputs.version }}
files: |
artifacts/*/*.tar.gz
artifacts/*/*.zip
draft: false
prerelease: false
generate_release_notes: true
body: |
## CountBot ${{ steps.version.outputs.version }}
### ⚠️ 重要提示
**桌面版目前处于测试阶段,可能存在兼容性问题。**
如果您在使用桌面版时遇到问题,我们强烈建议:
1. **使用 Python 源码运行**(推荐):
```bash
git clone https://github.com/countbot-ai/CountBot.git
cd CountBot
pip install -r requirements.txt
python start_desktop.py
```
2. **反馈问题帮助我们改进**:
- 遇到错误请在 [Issues](https://github.com/countbot-ai/CountBot/issues) 反馈
- 提供错误信息、系统版本等详细信息
- 您的反馈将帮助我们完善桌面版,降低 AI Agent 的使用门槛
---
### 🚀 快速开始
#### Windows
```cmd
# 1. 解压 zip 文件
# 2. 双击运行 CountBot.exe
# 3. 如果遇到问题,请使用 Python 源码运行
```
**常见问题:**
- 缺少 Edge WebView2:[下载安装](https://go.microsoft.com/fwlink/p/?LinkId=2124703)
- 端口被占用:修改默认的8000锻炼
- 防火墙拦截:允许 CountBot 访问网络
#### macOS
```bash
# 解压
tar -xzf CountBot-macOS-x64.tar.gz
# 添加执行权限
chmod +x CountBot/CountBot
# 运行
./CountBot/CountBot
```
**常见问题:**
- "无法打开,因为无法验证开发者":
```bash
xattr -cr CountBot/CountBot
```
- 系统版本要求:macOS 10.13 或更高
#### Linux
```bash
# 解压
tar -xzf CountBot-Linux-x64.tar.gz
# 添加执行权限
chmod +x CountBot/CountBot
# 运行
./CountBot/CountBot
```
**常见问题:**
- 缺少 WebKit2GTK:
```bash
# Ubuntu/Debian
sudo apt install webkit2gtk-4.0
# Fedora
sudo dnf install webkit2gtk3
```
---
### 🐛 遇到问题?
1. **查看日志**:`data/logs/CountBot_*.log`
2. **提交 Issue**:[GitHub Issues](https://github.com/countbot-ai/CountBot/issues)
3. **使用源码运行**:更稳定,更容易调试
### 💡 为什么推荐源码运行?
- ✅ 兼容性更好,避免打包问题
- ✅ 错误信息更详细,便于排查
- ✅ 可以随时更新到最新版本
- ✅ 支持所有 Python 环境和依赖
---
### 📝 更新日志
详见下方自动生成的更新说明。
---
### 🙏 感谢您的支持
CountBot 致力于降低 AI Agent 的使用门槛。您的反馈和建议对我们至关重要!
- ⭐ 如果觉得有用,请给我们一个 Star
- 🐛 遇到问题,请提交 Issue
- 💬 有想法,欢迎参与讨论
- 🤝 想贡献,查看 [CONTRIBUTING.md](https://github.com/countbot-ai/CountBot/blob/main/CONTRIBUTING.md)
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}