26 包 monorepo(TS 全源码): - snowcat-core: NT 内核 API 封装 + listener(含 9.9.37 六回调适配)+ Packet 发包 - snowcat-onebot: OneBot v11 全动作 + MCP Streamable HTTP 适配器 + 稳定线 - snowcat-webui-frontend/backend: 雪猫控制台(React+HeroUI) - snowcat-shell: 单文件 bundle 出口(vite inlineDynamicImports) - snowcat-native: napi2native / MoeHoo / ffmpeg / pty 运行时 - 56 项 SnowLuma 独门动作移植(qzone 全家桶/群相册/装扮/闪照/收藏) - 工具件: 解包器/启动器/便携制作器/doctor GitHub 关联已全量指向 DandanLLab/SnowCatQQ
425 lines
15 KiB
YAML
425 lines
15 KiB
YAML
# =============================================================================
|
|
# PR 构建工作流
|
|
# =============================================================================
|
|
# 安全模型:
|
|
# 1. pull_request_target / issue_comment 入口只读取元数据、检查权限、更新评论并触发构建。
|
|
# 2. 真正执行 PR 代码的构建流程只在 workflow_dispatch 中运行。
|
|
# 3. 构建流程不具备写仓库/写评论/读 secrets 权限,显式清空 GITHUB_TOKEN,不持久化 checkout 凭据。
|
|
# 4. 版本号在 Action 中提前生成,通过 NAPCAT_VERSION 传给 Vite 插件,插件无需 GitHub token。
|
|
# 5. 发布 Release、触发 Docker 等可信操作只消费构建 artifact,不执行 PR artifact 中的代码。
|
|
# =============================================================================
|
|
|
|
name: Build PR
|
|
|
|
on:
|
|
pull_request_target:
|
|
types: [opened, synchronize, reopened]
|
|
|
|
issue_comment:
|
|
types: [created]
|
|
|
|
workflow_dispatch:
|
|
inputs:
|
|
pr_number:
|
|
description: PR 编号
|
|
required: true
|
|
type: string
|
|
pr_sha:
|
|
description: PR HEAD SHA
|
|
required: true
|
|
type: string
|
|
pr_head_repo:
|
|
description: PR 源仓库
|
|
required: true
|
|
type: string
|
|
pr_head_ref:
|
|
description: PR 源分支
|
|
required: false
|
|
type: string
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
group: pr-build-${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event_name == 'pull_request_target' && github.event.pull_request.number || github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(github.event.comment.body, '/build') && github.event.issue.number || github.run_id }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
# ---------------------------------------------------------------------------
|
|
# 特权入口:只检查条件、写入“构建中”评论、触发无权限构建
|
|
# ---------------------------------------------------------------------------
|
|
check-build:
|
|
if: github.event_name != 'workflow_dispatch'
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
pull-requests: read
|
|
issues: write
|
|
outputs:
|
|
should_build: ${{ steps.check.outputs.should_build }}
|
|
pr_number: ${{ steps.check.outputs.pr_number }}
|
|
pr_sha: ${{ steps.check.outputs.pr_sha }}
|
|
pr_head_repo: ${{ steps.check.outputs.pr_head_repo }}
|
|
pr_head_ref: ${{ steps.check.outputs.pr_head_ref }}
|
|
steps:
|
|
- name: Checkout scripts
|
|
uses: actions/checkout@v5
|
|
with:
|
|
sparse-checkout: .github/scripts
|
|
sparse-checkout-cone-mode: false
|
|
persist-credentials: false
|
|
|
|
- name: Setup Node.js 24
|
|
uses: actions/setup-node@v6
|
|
with:
|
|
node-version: 24
|
|
|
|
- name: Check trigger condition
|
|
id: check
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: node --experimental-strip-types .github/scripts/pr-build-check.ts
|
|
|
|
update-comment-building:
|
|
needs: check-build
|
|
if: needs.check-build.outputs.should_build == 'true'
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
pull-requests: write
|
|
issues: write
|
|
steps:
|
|
- name: Checkout scripts
|
|
uses: actions/checkout@v5
|
|
with:
|
|
sparse-checkout: .github/scripts
|
|
sparse-checkout-cone-mode: false
|
|
persist-credentials: false
|
|
|
|
- name: Setup Node.js 24
|
|
uses: actions/setup-node@v6
|
|
with:
|
|
node-version: 24
|
|
|
|
- name: Update building comment
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
PR_NUMBER: ${{ needs.check-build.outputs.pr_number }}
|
|
PR_SHA: ${{ needs.check-build.outputs.pr_sha }}
|
|
run: node --experimental-strip-types .github/scripts/pr-build-building.ts
|
|
|
|
dispatch-build:
|
|
needs: [check-build, update-comment-building]
|
|
if: needs.check-build.outputs.should_build == 'true'
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
actions: write
|
|
steps:
|
|
- name: Checkout scripts
|
|
uses: actions/checkout@v5
|
|
with:
|
|
sparse-checkout: .github/scripts
|
|
sparse-checkout-cone-mode: false
|
|
persist-credentials: false
|
|
|
|
- name: Setup Node.js 24
|
|
uses: actions/setup-node@v6
|
|
with:
|
|
node-version: 24
|
|
|
|
- name: Dispatch untrusted build
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
PR_NUMBER: ${{ needs.check-build.outputs.pr_number }}
|
|
PR_SHA: ${{ needs.check-build.outputs.pr_sha }}
|
|
PR_HEAD_REPO: ${{ needs.check-build.outputs.pr_head_repo }}
|
|
PR_HEAD_REF: ${{ needs.check-build.outputs.pr_head_ref }}
|
|
WORKFLOW_FILE: build-pr.yml
|
|
DISPATCH_REF: ${{ github.event.repository.default_branch }}
|
|
run: node --experimental-strip-types .github/scripts/pr-build-dispatch.ts
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 无权限构建:只由 workflow_dispatch 执行 PR 代码
|
|
# ---------------------------------------------------------------------------
|
|
build-framework:
|
|
if: github.event_name == 'workflow_dispatch'
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
outputs:
|
|
status: ${{ steps.build.outcome }}
|
|
error: ${{ steps.build.outputs.error }}
|
|
version: ${{ steps.version.outputs.version }}
|
|
steps:
|
|
- name: Checkout scripts from base
|
|
uses: actions/checkout@v5
|
|
with:
|
|
sparse-checkout: .github/scripts
|
|
sparse-checkout-cone-mode: false
|
|
path: _scripts
|
|
persist-credentials: false
|
|
|
|
- name: Checkout PR code
|
|
uses: actions/checkout@v5
|
|
with:
|
|
repository: ${{ inputs.pr_head_repo }}
|
|
ref: ${{ inputs.pr_sha }}
|
|
path: workspace
|
|
fetch-depth: 0
|
|
persist-credentials: false
|
|
|
|
- name: Setup Node.js 24
|
|
uses: actions/setup-node@v6
|
|
with:
|
|
node-version: 24
|
|
|
|
- name: Setup pnpm
|
|
uses: pnpm/action-setup@v4
|
|
with:
|
|
version: 9
|
|
run_install: false
|
|
|
|
- name: Generate Version
|
|
id: version
|
|
working-directory: workspace
|
|
run: |
|
|
LATEST_TAG=$(git describe --tags --abbrev=0 --match "v[0-9]*.[0-9]*.[0-9]*" 2>/dev/null || echo "v0.0.0")
|
|
BASE_VERSION="${LATEST_TAG#v}"
|
|
SHORT_SHA="${{ inputs.pr_sha }}"
|
|
SHORT_SHA="${SHORT_SHA::7}"
|
|
VERSION="${BASE_VERSION}-pr.${{ inputs.pr_number }}.${{ github.run_number }}+${SHORT_SHA}"
|
|
echo "NAPCAT_VERSION=${VERSION}" >> $GITHUB_ENV
|
|
echo "version=${VERSION}" >> $GITHUB_OUTPUT
|
|
echo "Latest tag: ${LATEST_TAG}"
|
|
echo "Build version: ${VERSION}"
|
|
|
|
- name: Build
|
|
id: build
|
|
working-directory: workspace
|
|
env:
|
|
GITHUB_TOKEN: ''
|
|
NAPCAT_VERSION: ${{ env.NAPCAT_VERSION }}
|
|
run: node --experimental-strip-types ../_scripts/.github/scripts/pr-build-run.ts framework
|
|
continue-on-error: true
|
|
|
|
- name: Upload Artifact
|
|
if: steps.build.outcome == 'success'
|
|
uses: actions/upload-artifact@v6
|
|
with:
|
|
name: NapCat.Framework
|
|
path: workspace/framework-dist
|
|
retention-days: 5
|
|
|
|
build-shell:
|
|
if: github.event_name == 'workflow_dispatch'
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
outputs:
|
|
status: ${{ steps.build.outcome }}
|
|
error: ${{ steps.build.outputs.error }}
|
|
version: ${{ steps.version.outputs.version }}
|
|
steps:
|
|
- name: Checkout scripts from base
|
|
uses: actions/checkout@v5
|
|
with:
|
|
sparse-checkout: .github/scripts
|
|
sparse-checkout-cone-mode: false
|
|
path: _scripts
|
|
persist-credentials: false
|
|
|
|
- name: Checkout PR code
|
|
uses: actions/checkout@v5
|
|
with:
|
|
repository: ${{ inputs.pr_head_repo }}
|
|
ref: ${{ inputs.pr_sha }}
|
|
path: workspace
|
|
fetch-depth: 0
|
|
persist-credentials: false
|
|
|
|
- name: Setup Node.js 24
|
|
uses: actions/setup-node@v6
|
|
with:
|
|
node-version: 24
|
|
|
|
- name: Setup pnpm
|
|
uses: pnpm/action-setup@v4
|
|
with:
|
|
version: 9
|
|
run_install: false
|
|
|
|
- name: Generate Version
|
|
id: version
|
|
working-directory: workspace
|
|
run: |
|
|
LATEST_TAG=$(git describe --tags --abbrev=0 --match "v[0-9]*.[0-9]*.[0-9]*" 2>/dev/null || echo "v0.0.0")
|
|
BASE_VERSION="${LATEST_TAG#v}"
|
|
SHORT_SHA="${{ inputs.pr_sha }}"
|
|
SHORT_SHA="${SHORT_SHA::7}"
|
|
VERSION="${BASE_VERSION}-pr.${{ inputs.pr_number }}.${{ github.run_number }}+${SHORT_SHA}"
|
|
echo "NAPCAT_VERSION=${VERSION}" >> $GITHUB_ENV
|
|
echo "version=${VERSION}" >> $GITHUB_OUTPUT
|
|
echo "Latest tag: ${LATEST_TAG}"
|
|
echo "Build version: ${VERSION}"
|
|
|
|
- name: Build
|
|
id: build
|
|
working-directory: workspace
|
|
env:
|
|
GITHUB_TOKEN: ''
|
|
NAPCAT_VERSION: ${{ env.NAPCAT_VERSION }}
|
|
run: node --experimental-strip-types ../_scripts/.github/scripts/pr-build-run.ts shell
|
|
continue-on-error: true
|
|
|
|
- name: Upload Artifact
|
|
if: steps.build.outcome == 'success'
|
|
uses: actions/upload-artifact@v6
|
|
with:
|
|
name: NapCat.Shell
|
|
path: workspace/shell-dist
|
|
retention-days: 5
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 可信后处理:只下载/打包/发布 artifact,不执行 artifact 中的代码
|
|
# ---------------------------------------------------------------------------
|
|
publish-pr-release:
|
|
needs: [build-framework, build-shell]
|
|
if: always() && github.event_name == 'workflow_dispatch' && (needs.build-framework.outputs.status == 'success' || needs.build-shell.outputs.status == 'success')
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
actions: read
|
|
outputs:
|
|
release_url: ${{ steps.publish.outputs.release_url }}
|
|
release_tag: ${{ steps.publish.outputs.release_tag }}
|
|
install_url: ${{ steps.publish.outputs.install_url }}
|
|
publish_status: ${{ steps.publish.outcome }}
|
|
steps:
|
|
- name: Checkout scripts
|
|
uses: actions/checkout@v5
|
|
with:
|
|
sparse-checkout: .github/scripts
|
|
sparse-checkout-cone-mode: false
|
|
persist-credentials: false
|
|
|
|
- name: Setup Node.js 24
|
|
uses: actions/setup-node@v6
|
|
with:
|
|
node-version: 24
|
|
|
|
- name: Download Framework artifact
|
|
if: needs.build-framework.outputs.status == 'success'
|
|
uses: actions/download-artifact@v6
|
|
with:
|
|
name: NapCat.Framework
|
|
path: ./artifacts/NapCat.Framework
|
|
|
|
- name: Download Shell artifact
|
|
if: needs.build-shell.outputs.status == 'success'
|
|
uses: actions/download-artifact@v6
|
|
with:
|
|
name: NapCat.Shell
|
|
path: ./artifacts/NapCat.Shell
|
|
|
|
- name: Publish release
|
|
id: publish
|
|
continue-on-error: true
|
|
env:
|
|
RELEASE_PAT: ${{ secrets.PR_RELEASE_PAT }}
|
|
PR_NUMBER: ${{ inputs.pr_number }}
|
|
PR_SHA: ${{ inputs.pr_sha }}
|
|
NAPCAT_VERSION: ${{ needs.build-framework.outputs.version || needs.build-shell.outputs.version || '' }}
|
|
ARTIFACTS_DIR: ./artifacts
|
|
run: |
|
|
if [ -z "${RELEASE_PAT}" ]; then
|
|
echo "::warning::PR_RELEASE_PAT secret 未配置,跳过 PR Release 发布"
|
|
exit 0
|
|
fi
|
|
node --experimental-strip-types .github/scripts/pr-publish-release.ts
|
|
|
|
- name: Upload install script as artifact
|
|
if: steps.publish.outcome == 'success'
|
|
uses: actions/upload-artifact@v6
|
|
with:
|
|
name: install-script
|
|
path: ./release-assets/install.sh
|
|
retention-days: 5
|
|
if-no-files-found: ignore
|
|
|
|
update-comment-result:
|
|
needs: [build-framework, build-shell, publish-pr-release]
|
|
if: always() && github.event_name == 'workflow_dispatch'
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
actions: read
|
|
pull-requests: write
|
|
issues: write
|
|
steps:
|
|
- name: Checkout scripts
|
|
uses: actions/checkout@v5
|
|
with:
|
|
sparse-checkout: .github/scripts
|
|
sparse-checkout-cone-mode: false
|
|
persist-credentials: false
|
|
|
|
- name: Setup Node.js 24
|
|
uses: actions/setup-node@v6
|
|
with:
|
|
node-version: 24
|
|
|
|
- name: Update result comment
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
PR_NUMBER: ${{ inputs.pr_number }}
|
|
PR_SHA: ${{ inputs.pr_sha }}
|
|
RUN_ID: ${{ github.run_id }}
|
|
NAPCAT_VERSION: ${{ needs.build-framework.outputs.version || needs.build-shell.outputs.version || '' }}
|
|
FRAMEWORK_STATUS: ${{ needs.build-framework.outputs.status || 'cancelled' }}
|
|
FRAMEWORK_ERROR: ${{ needs.build-framework.outputs.error }}
|
|
SHELL_STATUS: ${{ needs.build-shell.outputs.status || 'cancelled' }}
|
|
SHELL_ERROR: ${{ needs.build-shell.outputs.error }}
|
|
INSTALL_URL: ${{ needs.publish-pr-release.outputs.install_url || '' }}
|
|
RELEASE_URL: ${{ needs.publish-pr-release.outputs.release_url || '' }}
|
|
PUBLISH_STATUS: ${{ needs.publish-pr-release.outputs.publish_status || '' }}
|
|
run: node --experimental-strip-types .github/scripts/pr-build-result.ts
|
|
|
|
trigger-docker:
|
|
needs: [build-shell, publish-pr-release]
|
|
if: github.event_name == 'workflow_dispatch' && needs.build-shell.outputs.status == 'success' && needs.publish-pr-release.outputs.release_tag != ''
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
pull-requests: write
|
|
issues: write
|
|
steps:
|
|
- name: Checkout scripts
|
|
uses: actions/checkout@v5
|
|
with:
|
|
sparse-checkout: .github/scripts
|
|
sparse-checkout-cone-mode: false
|
|
persist-credentials: false
|
|
|
|
- name: Setup Node.js 24
|
|
uses: actions/setup-node@v6
|
|
with:
|
|
node-version: 24
|
|
|
|
- name: Dispatch docker build
|
|
continue-on-error: true
|
|
env:
|
|
DISPATCH_PAT: ${{ secrets.PR_RELEASE_PAT }}
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
PR_NUMBER: ${{ inputs.pr_number }}
|
|
PR_SHA: ${{ inputs.pr_sha }}
|
|
PR_HEAD_REPO: ${{ inputs.pr_head_repo }}
|
|
SOURCE_REPO: ${{ github.repository }}
|
|
RELEASE_REPO: NapNeko/napcat-pr-release
|
|
run: |
|
|
if [ -z "${DISPATCH_PAT}" ]; then
|
|
echo "::warning::PR_RELEASE_PAT secret 未配置,跳过 Docker 测试镜像构建"
|
|
exit 0
|
|
fi
|
|
node --experimental-strip-types .github/scripts/pr-docker-dispatch.ts
|