mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-07-08 16:43:59 +08:00
### What this PR does Before this PR: `.claude/skills/<name>/SKILL.md` files were **copied** from `.agents/skills/<name>/SKILL.md` via `pnpm skills:sync`. A dedicated `skills-check-windows` CI job ran on `windows-latest` to verify cross-platform file-copy compatibility. After this PR: `.claude/skills/<name>` entries are **directory symlinks** pointing to `../../.agents/skills/<name>`, following the Single Source of Truth (SSoT) principle. The Windows-specific CI job is removed; Windows developers are expected to enable symlink support. ### Why we need it and why it was done in this way The following tradeoffs were made: - Windows developers must now manually enable symlink support (Developer Mode + `git config --global core.symlinks true`). This is acceptable because: 1. The existing `AGENTS.md` is already a symlink, so Windows compatibility was never fully enforced. 2. Symlinks eliminate the need for file-copy synchronization, reducing maintenance complexity. 3. Contributors are expected to have sufficient technical capability to configure their environments. The following alternatives were considered: - Keeping file-copy sync: rejected because it duplicates content and requires extra CI to verify consistency. ### Breaking changes Windows developers who clone without symlink support enabled will get plain text files instead of symlinks. They must: 1. Enable Developer Mode or grant `SeCreateSymbolicLinkPrivilege` 2. Run `git config --global core.symlinks true` 3. Re-clone or run `pnpm skills:sync` ### Special notes for your reviewer - The `.github/workflows/ci.yml` diff includes minor quote-style changes (`'` → `"`) from the YAML formatter — these are cosmetic only. ### Checklist - [x] PR: The PR description is expressive enough and will help future contributors - [x] Code: [Write code that humans can understand](https://en.wikiquote.org/wiki/Martin_Fowler#code-for-humans) and [Keep it simple](https://en.wikipedia.org/wiki/KISS_principle) - [x] Refactor: You have [left the code cleaner than you found it (Boy Scout Rule)](https://learning.oreilly.com/library/view/97-things-every/9780596809515/ch08.html) - [x] Upgrade: Impact of this change on upgrade flows was considered and addressed if required - [x] Documentation: A [user-guide update](https://docs.cherry-ai.com) was considered and is present (link) or not required. Check this only when the PR introduces or changes a user-facing feature or behavior. - [ ] Self-review: I have reviewed my own code (e.g., via [`/gh-pr-review`](/.claude/skills/gh-pr-review/SKILL.md), `gh pr diff`, or GitHub UI) before requesting review from others ### Release note ```release-note NONE ``` Signed-off-by: icarus <eurfelux@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
245 lines
6.7 KiB
YAML
245 lines
6.7 KiB
YAML
name: CI
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
push:
|
|
branches:
|
|
- main
|
|
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@v3
|
|
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/**'
|
|
|
|
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@v4
|
|
|
|
- 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: i18n Check
|
|
run: pnpm i18n:check
|
|
|
|
- name: Hardcoded Strings Check
|
|
run: pnpm i18n:hardcoded:strict
|
|
|
|
- name: OpenAPI Spec Check
|
|
run: pnpm openapi:check
|
|
|
|
- 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@v4
|
|
|
|
- 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: 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@v4
|
|
|
|
- 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@v4
|
|
|
|
- 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"
|