Skip to content

Release

Release #163

Workflow file for this run

name: Release
on:
push:
tags:
- v*
workflow_dispatch:
inputs:
version:
description: 'Release version (e.g., v2.0.0-beta.1)'
required: true
type: string
webui_tag:
description: '可选:捆绑的 WebUI 仓库 tag(默认取当前 main 上可达的最新 v* tag)'
required: false
default: ''
type: string
jobs:
build-webui-dist:
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
webui_console_version: ${{ steps.webui_ref.outputs.console_version }}
webui_git_ref: ${{ steps.webui_ref.outputs.resolved_ref }}
steps:
- name: Checkout WebUI repo
uses: actions/checkout@v6
with:
repository: PallasBot/Pallas-Bot-WebUI
ref: main
path: pallas-webui
fetch-depth: 0
fetch-tags: true
- name: Resolve WebUI release tag
id: webui_ref
working-directory: pallas-webui
env:
PIN_TAG: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.webui_tag || '' }}
run: |
set -euo pipefail
git fetch origin --tags --force
if [ -n "${PIN_TAG}" ]; then
git checkout "${PIN_TAG}"
echo "console_version=${PIN_TAG}" >> "$GITHUB_OUTPUT"
echo "resolved_ref=${PIN_TAG}" >> "$GITHUB_OUTPUT"
exit 0
fi
TAG=$(git tag -l 'v*' --merged HEAD --sort=-version:refname | head -n1)
if [ -z "${TAG}" ]; then
TAG=$(git tag -l 'v*' --sort=-version:refname | head -n1)
fi
if [ -n "${TAG}" ]; then
git checkout "${TAG}"
echo "console_version=${TAG}" >> "$GITHUB_OUTPUT"
echo "resolved_ref=${TAG}" >> "$GITHUB_OUTPUT"
else
echo "::warning::WebUI 仓库无 v* tag,使用 main;console 版本取自 package.json"
VER=$(node -p "require('./package.json').version")
echo "console_version=${VER}" >> "$GITHUB_OUTPUT"
echo "resolved_ref=main" >> "$GITHUB_OUTPUT"
fi
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: |
pallas-webui/package-lock.json
pallas-webui/react/package-lock.json
- name: Resolve WebUI build directory
id: webui_build
working-directory: pallas-webui
run: |
set -euo pipefail
# 现行:React 在仓库根;兼容旧 tag:仅 react/ 子目录有前端 package
if [[ -f react/package.json && ! -f src/main.tsx ]]; then
echo "dir=react" >> "$GITHUB_OUTPUT"
else
echo "dir=." >> "$GITHUB_OUTPUT"
fi
- name: Install dependencies
run: npm ci
working-directory: pallas-webui/${{ steps.webui_build.outputs.dir }}
# console-version.json:version = 上游 WebUI 发版 tag(或手动 PIN / 无 tag 时 package.json)。
- name: Build WebUI (React)
working-directory: pallas-webui/${{ steps.webui_build.outputs.dir }}
env:
CONSOLE_VERSION: ${{ steps.webui_ref.outputs.console_version }}
run: |
set -euo pipefail
if [[ "${{ steps.webui_build.outputs.dir }}" == "react" ]]; then
export GIT_COMMIT="$(git -C .. rev-parse HEAD)"
else
export GIT_COMMIT="$(git rev-parse HEAD)"
fi
export BUILD_TIME="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
npm run build:ci
# zip 根为 public-react/:解压到 data/pb_webui → public-react/index.html(默认 webui_public_path)。
- name: Pack dist artifact
run: |
mkdir -p webui-zip-root/public-react
cp -a "pallas-webui/${{ steps.webui_build.outputs.dir }}/dist/." webui-zip-root/public-react/
(cd webui-zip-root && zip -r ../dist.zip public-react)
- name: Upload WebUI artifact
uses: actions/upload-artifact@v4
with:
name: webui-dist-zip
path: dist.zip
create-release:
runs-on: ubuntu-latest
needs: [build-webui-dist, build-tagged-image]
permissions:
contents: write
packages: write
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Get version
id: get_version
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
echo "version=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT
else
echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
fi
- name: Generate changelog
id: changelog
env:
VERSION: ${{ steps.get_version.outputs.version }}
REPOSITORY: ${{ github.repository }}
run: |
set -euo pipefail
chmod +x tools/generate_release_changelog.sh
PREVIOUS=""
if git rev-parse "${VERSION}^{commit}" >/dev/null 2>&1; then
PREVIOUS=$(git describe --tags --abbrev=0 "${VERSION}^" 2>/dev/null || true)
fi
if [ -z "${PREVIOUS}" ]; then
PREVIOUS=$(git tag -l 'v*' --sort=-version:refname | grep -Fxv "${VERSION}" | head -n1 || true)
fi
GIT_LOG=$(./tools/generate_release_changelog.sh "${PREVIOUS}" "${VERSION}" "${REPOSITORY}")
ANNOUNCE=$(python3 - "${VERSION}" <<'PY'
import re
import sys
version = sys.argv[1].lstrip("v")
text = open("CHANGELOG.md", encoding="utf-8").read()
pattern = re.compile(
rf"^##\s+\[{re.escape(version)}\][^\n]*\n(.*?)(?=^##\s+|\Z)",
re.MULTILINE | re.DOTALL,
)
match = pattern.search(text)
if not match:
sys.exit(0)
announce = re.search(
r"^###\s+更新公告[^\n]*\n(.*?)(?=^###\s+|\Z)",
match.group(1),
re.MULTILINE | re.DOTALL,
)
if announce:
print(announce.group(1).strip())
PY
)
if [ -n "${ANNOUNCE}" ]; then
CHANGELOG="${ANNOUNCE}
### 提交明细
${GIT_LOG}"
else
CHANGELOG="${GIT_LOG}"
fi
{
echo 'changelog<<EOF'
printf '%s\n' "${CHANGELOG}"
echo EOF
} >> "$GITHUB_OUTPUT"
- name: Download WebUI artifact
uses: actions/download-artifact@v4
with:
name: webui-dist-zip
path: .
- name: Setup Python for deployment smoke check
uses: actions/setup-python@v6
with:
python-version: '3.12'
- name: Build clean deployment bundle
env:
VERSION: ${{ steps.get_version.outputs.version }}
WEBUI_VERSION: ${{ needs.build-webui-dist.outputs.webui_console_version }}
run: |
set -euo pipefail
BUNDLE_NAME="pallas-bot-${VERSION}"
git clone --filter=blob:none --no-checkout --depth 1 \
"https://github.com/${GITHUB_REPOSITORY}.git" "${BUNDLE_NAME}"
git -C "${BUNDLE_NAME}" fetch --depth 1 origin "${GITHUB_SHA}"
git -C "${BUNDLE_NAME}" sparse-checkout init --no-cone
cp .github/release-runtime-sparse-checkout "${BUNDLE_NAME}/.git/info/sparse-checkout"
git -C "${BUNDLE_NAME}" checkout --detach "${GITHUB_SHA}"
git -C "${BUNDLE_NAME}" tag -f "${VERSION}" "${GITHUB_SHA}"
mkdir -p "${BUNDLE_NAME}/data/pb_webui"
unzip -q dist.zip -d "${BUNDLE_NAME}/data/pb_webui"
test -f "${BUNDLE_NAME}/data/pb_webui/public-react/index.html"
test -f "${BUNDLE_NAME}/data/pb_webui/public-react/console-version.json"
python - "${BUNDLE_NAME}/data/pb_webui/public-react/console-version.json" "${WEBUI_VERSION}" <<'PY'
import json
import sys
payload = json.loads(open(sys.argv[1], encoding="utf-8").read())
if payload.get("version") != sys.argv[2]:
raise SystemExit(f"WebUI version mismatch: {payload.get('version')!r} != {sys.argv[2]!r}")
PY
test -z "$(git -C "${BUNDLE_NAME}" status --porcelain)"
test "$(git -C "${BUNDLE_NAME}" rev-parse HEAD)" = "${GITHUB_SHA}"
test "$(git -C "${BUNDLE_NAME}" remote get-url origin)" = \
"https://github.com/${GITHUB_REPOSITORY}.git"
PYTHONPYCACHEPREFIX="${RUNNER_TEMP}/pallas-release-pyc" \
python -m compileall -q "${BUNDLE_NAME}/pallas" "${BUNDLE_NAME}/packages"
tar -czf "pallas-bot-${VERSION}.tar.gz" "${BUNDLE_NAME}"
- name: Create Release
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2
with:
tag_name: ${{ steps.get_version.outputs.version }}
name: Release ${{ steps.get_version.outputs.version }}
body: |
## 更新内容
${{ steps.changelog.outputs.changelog }}
## Docker
```bash
docker pull ${{ secrets.DOCKERHUB_USERNAME }}/pallas-bot:${{ steps.get_version.outputs.version }}
```
## WebUI
附件 **`dist.zip`**:控制台前端静态资源(来自 [Pallas-Bot-WebUI](https://github.com/PallasBot/Pallas-Bot-WebUI) `${{ needs.build-webui-dist.outputs.webui_git_ref }}`,console `${{ needs.build-webui-dist.outputs.webui_console_version }}`)。
## 非 Docker 部署包
下载 **`pallas-bot-${{ steps.get_version.outputs.version }}.tar.gz`** 后解压。部署包已包含本次 WebUI、浅层 Git 元数据和运行/运维所需文件,不需要执行 `git init`;安装依赖后即可配置并启动:
```bash
tar -xzf pallas-bot-${{ steps.get_version.outputs.version }}.tar.gz
cd pallas-bot-${{ steps.get_version.outputs.version }}
uv sync --extra perf
cp config/pallas.example.toml config/pallas.toml
uv run pallas
```
Bot 与 WebUI 更新保持独立;部署包可继续使用控制台或 `uv run pallas update bot --restart` 跟踪后续 Release。
| 方式 | 说明 |
| --- | --- |
| 内置 | 将本附件保存为 `resource/webui/dist.zip`;本地尚无前端资源时,启动会优先自动解压。Docker 镜像已内置该文件 |
| 自动 | 内置包不存在时,启动会从本仓库 Release 拉取并解压 |
| 手动 | 将本附件解压到仓库根下的 `data/pb_webui/`(得到 `public-react/`)|
| 控制台 | 「更新」页可检查并应用同名 `dist.zip` |
```bash
unzip -d data/pb_webui dist.zip
```
默认前端栈为 **React**(`pallas_webui_frontend=react`)。若需 Vue,设置 `pallas_webui_frontend=vue` 并使用含 `public/` 的产物。
访问:`http://<host>:<port>/pallas/` · 说明见 [WebUI 部署](https://github.com/${{ github.repository }}/blob/${{ steps.get_version.outputs.version }}/docs/maintainer/install/webui.md) / [开发者文档](https://github.com/${{ github.repository }}/blob/${{ steps.get_version.outputs.version }}/docs/developer/webui.md)。
files: |
dist.zip
pallas-bot-${{ steps.get_version.outputs.version }}.tar.gz
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
build-tagged-image:
runs-on: ubuntu-latest
needs: build-webui-dist
permissions:
contents: read
packages: write
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Download WebUI dist for image
uses: actions/download-artifact@v4
with:
name: webui-dist-zip
path: resource/webui
- name: Set up QEMU
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4
- name: Login to DockerHub
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Get version
id: get_version
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
echo "version=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT
else
echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
fi
- name: Build and push tagged image
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7
with:
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64
push: true
build-args: |
PALLAS_BOT_VERSION=${{ steps.get_version.outputs.version }}
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/pallas-bot:${{ steps.get_version.outputs.version }}
${{ secrets.DOCKERHUB_USERNAME }}/pallas-bot:latest