Release #163
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |