Files
CherryHQ-cherry-studio/.github/workflows/ci.yml
2026-07-03 13:01:36 +08:00

325 lines
9.9 KiB
YAML

name: CI
permissions:
contents: read
on:
workflow_dispatch:
push:
branches:
- main
- v2
pull_request:
branches:
- main
- develop
- v2
types: [ready_for_review, synchronize, opened]
jobs:
changes:
runs-on: ubuntu-latest
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' || github.event.pull_request.draft == false
outputs:
main: ${{ steps.filter.outputs.main }}
renderer: ${{ steps.filter.outputs.renderer }}
shared: ${{ steps.filter.outputs.shared }}
steps:
- name: Check out Git repository
uses: actions/checkout@v6
- name: Detect code changes
id: filter
uses: dorny/paths-filter@v4
with:
filters: |
main:
- 'src/main/**'
- 'src/preload/**'
- 'scripts/**'
renderer:
- 'src/renderer/**'
shared:
- 'packages/**'
- 'tests/**'
- 'package.json'
- 'pnpm-lock.yaml'
- 'pnpm-workspace.yaml'
- 'tsconfig*.json'
- 'electron.vite.config.ts'
- 'vitest.config.ts'
- 'patches/**'
catalog-hand-edit-check:
# provider-registry data/*.json are GENERATED from src/creators + src/providers. Catch a hand-edit:
# if the data changed but no source under src/ or scripts/ did, the JSON was edited directly.
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' && github.event.pull_request.draft == false
steps:
- name: Check out Git repository
uses: actions/checkout@v6
- name: Detect provider-registry changes
id: filter
uses: dorny/paths-filter@v4
with:
filters: |
data:
- 'packages/provider-registry/data/**'
source:
- 'packages/provider-registry/src/**'
- 'packages/provider-registry/scripts/**'
- name: Reject hand-edited catalog
if: steps.filter.outputs.data == 'true' && steps.filter.outputs.source == 'false'
run: |
echo "::error::packages/provider-registry/data/*.json changed but no src/ or scripts/ change — that's a hand-edit. The data files are generated: edit src/creators|src/providers and run \`pnpm --filter @cherrystudio/provider-registry generate\`, then commit. Never hand-edit data/*.json."
exit 1
changeset-check:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' && github.event.pull_request.draft == false && github.head_ref != 'changeset-release/main'
steps:
- name: Check out Git repository
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Install Node.js
uses: actions/setup-node@v6
with:
node-version-file: ".node-version"
- name: Install pnpm
uses: pnpm/action-setup@v5
- name: Get pnpm store directory
id: pnpm-cache
shell: bash
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
- name: Cache pnpm dependencies
uses: actions/cache@v5
with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-
- name: Install Dependencies
run: pnpm install
- name: Check changesets
env:
BASE_REF: ${{ github.event.pull_request.base.ref }}
run: pnpm changeset status --since="origin/$BASE_REF"
basic-checks:
runs-on: ubuntu-latest
env:
CI: true
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' || github.event.pull_request.draft == false
steps:
- name: Check out Git repository
uses: actions/checkout@v6
- name: Install Node.js
uses: actions/setup-node@v6
with:
node-version-file: ".node-version"
- name: Install pnpm
uses: pnpm/action-setup@v5
- name: Get pnpm store directory
id: pnpm-cache
shell: bash
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
- name: Cache pnpm dependencies
uses: actions/cache@v5
with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-
- name: Install Dependencies
run: pnpm install
- name: Lint Check
run: pnpm test:lint
- name: Format Check
run: pnpm format:check
- name: Type Check
run: pnpm typecheck
- name: DB Migrations Check
run: |
# 1) Snapshot chain integrity — exits 1 on duplicate id / forked prevId (e.g. #15438).
pnpm db:migrations:check
# 2) Drift — schema source must match committed migrations. Catches "renamed a migration
# on merge instead of re-running generate" even when it does not collide an id.
# Note: `generate` exits 0 even on a chain collision, so it can never replace step 1.
pnpm db:migrations:generate
if [ -n "$(git status --porcelain migrations/)" ]; then
echo "::error::Schema and migrations are out of sync. Run \`pnpm db:migrations:generate\` and commit the result."
git --no-pager status --porcelain migrations/
git --no-pager diff -- migrations/
exit 1
fi
- name: i18n Check
run: pnpm i18n:check
- name: Hardcoded Strings Check
run: pnpm i18n:hardcoded:strict
- name: Skills Check
run: pnpm skills:check
general-test:
runs-on: ubuntu-latest
needs: changes
if: >-
needs.changes.outputs.main == 'true' ||
needs.changes.outputs.shared == 'true' ||
github.event_name == 'push' ||
github.event_name == 'workflow_dispatch'
env:
CI: true
steps:
- name: Check out Git repository
uses: actions/checkout@v6
- name: Install Node.js
uses: actions/setup-node@v6
with:
node-version-file: ".node-version"
- name: Install pnpm
uses: pnpm/action-setup@v5
- name: Get pnpm store directory
id: pnpm-cache
shell: bash
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
- name: Cache pnpm dependencies
uses: actions/cache@v5
with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-
- name: Install Dependencies
run: pnpm install
- name: Main Process Test
run: pnpm test:main
- name: AI Core Test
run: pnpm test:aicore
- name: Shared Package Test
run: pnpm test:shared
- name: Provider Registry Package Test
run: pnpm test:provider-registry
- name: Scripts Test
run: pnpm test:scripts
render-test:
runs-on: ubuntu-latest
needs: changes
if: >-
needs.changes.outputs.renderer == 'true' ||
needs.changes.outputs.shared == 'true' ||
github.event_name == 'push' ||
github.event_name == 'workflow_dispatch'
env:
CI: true
steps:
- name: Check out Git repository
uses: actions/checkout@v6
- name: Install Node.js
uses: actions/setup-node@v6
with:
node-version-file: ".node-version"
- name: Install pnpm
uses: pnpm/action-setup@v5
- name: Get pnpm store directory
id: pnpm-cache
shell: bash
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
- name: Cache pnpm dependencies
uses: actions/cache@v5
with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-
- name: Install Dependencies
run: pnpm install
- name: Renderer Test
run: pnpm test:renderer
notify:
runs-on: ubuntu-latest
needs: [basic-checks, general-test, render-test]
if: always() && (needs.basic-checks.result == 'failure' || needs.general-test.result == 'failure' || needs.render-test.result == 'failure') && github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- name: Check out Git repository
uses: actions/checkout@v6
- name: Install Node.js
uses: actions/setup-node@v6
with:
node-version-file: ".node-version"
- name: Install pnpm
uses: pnpm/action-setup@v5
- name: Get pnpm store directory
id: pnpm-cache
shell: bash
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
- name: Cache pnpm dependencies
uses: actions/cache@v5
with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-
- name: Install Dependencies
run: pnpm install
- name: Send failure notification to Feishu
env:
FEISHU_WEBHOOK_URL: ${{ secrets.FEISHU_WEBHOOK_URL }}
FEISHU_WEBHOOK_SECRET: ${{ secrets.FEISHU_WEBHOOK_SECRET }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
COMMIT_SHA: ${{ github.sha }}
COMMIT_MSG: ${{ github.event.head_commit.message }}
run: |
DESCRIPTION=$(printf "**分支:** main\n\n**提交:** %.7s\n\n**信息:** %s\n\n**工作流:** [查看详情](%s)" "$COMMIT_SHA" "$COMMIT_MSG" "$RUN_URL")
pnpm tsx scripts/feishu-notify.ts send \
-t "Main 分支 CI 失败" \
-d "$DESCRIPTION" \
-c "red"