Skip to content

fix(backup): parallelize backup dialog metadata requests #19709

fix(backup): parallelize backup dialog metadata requests

fix(backup): parallelize backup dialog metadata requests #19709

Workflow file for this run

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 }}
ai-core: ${{ steps.filter.outputs.ai-core }}
ui: ${{ steps.filter.outputs.ui }}
provider-registry: ${{ steps.filter.outputs.provider-registry }}
scripts: ${{ steps.filter.outputs.scripts }}
extension-table-plus: ${{ steps.filter.outputs.extension-table-plus }}
test-infra: ${{ steps.filter.outputs.test-infra }}
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/**'
- 'tests/helpers/**'
- 'tests/main.setup.ts'
- 'tests/__mocks__/main/**'
- 'tests/__mocks__/MainLoggerService.ts'
- 'migrations/**'
renderer:
- 'src/renderer/**'
- 'src/preload/**'
- 'tests/renderer.setup.ts'
- 'tests/__mocks__/renderer/**'
- 'tests/__mocks__/RendererLoggerService.ts'
- 'tests/__mocks__/requestAnimationFrame.ts'
shared:
- 'src/shared/**'
ai-core:
- 'packages/aiCore/**'
- 'packages/ai-sdk-provider/**'
ui:
- 'packages/ui/**'
provider-registry:
- 'packages/provider-registry/**'
scripts:
- 'scripts/**'
extension-table-plus:
- 'packages/extension-table-plus/**'
test-infra:
- '.github/workflows/ci.yml'
- '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
with:
# The PR merge commit and its parents are enough for changeset comparison.
fetch-depth: ${{ github.event_name == 'pull_request' && '2' || '1' }}
- name: Prepare base reference
if: github.event_name == 'pull_request'
env:
BASE_REF: ${{ github.event.pull_request.base.ref }}
run: git update-ref "refs/remotes/origin/$BASE_REF" "$(git rev-parse HEAD^1)"
# provider-registry data/*.json are generated from src/creators + src/providers.
# The daily catalog workflow regenerates data without source changes, so exempt only
# its exact same-repository branch from the hand-edit guard.
- name: Detect provider-registry changes
if: github.event_name == 'pull_request' && !(github.head_ref == 'auto-update-catalog-daily' && github.event.pull_request.head.repo.full_name == github.repository)
id: catalog-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.catalog-filter.outputs.data == 'true' && steps.catalog-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
- 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: Restore pnpm dependencies
if: github.event_name == 'pull_request'
uses: actions/cache/restore@v5
with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-
- name: Cache pnpm dependencies
if: github.event_name != 'pull_request'
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
if: github.event_name == 'pull_request' && github.head_ref != 'changeset-release/main'
env:
BASE_REF: ${{ github.event.pull_request.base.ref }}
run: pnpm changeset status --since="origin/$BASE_REF"
- name: Read-only Checks
run: |
# Share one checkout/install while keeping independent checks parallel.
# Do not kill sibling checks on failure so every failure is reported.
pnpm exec concurrently \
--names "Lint,Type and Hardcoded Strings,Format and Repository" \
--prefix-colors "blue,yellow,cyan" \
"pnpm test:lint" \
"pnpm typecheck && pnpm i18n:hardcoded:strict" \
"pnpm format:check && pnpm build:builtin-knowledge:check && pnpm i18n:check && pnpm skills:check"
- 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: System Provider IDs Check
run: |
# SystemProviderId is generated from packages/provider-registry — fail on drift.
pnpm gen:system-provider-ids
if [ -n "$(git status --porcelain src/shared/utils/systemProviderId.ts)" ]; then
echo "::error::systemProviderId.ts is out of sync with provider-registry. Run \`pnpm gen:system-provider-ids\` and commit the result."
git --no-pager diff -- src/shared/utils/systemProviderId.ts
exit 1
fi
main-test-shard:
name: main-test (${{ matrix.os }} ${{ matrix.shard }}/${{ matrix.shards }})
runs-on: ${{ matrix.os }}
needs: changes
if: >-
needs.changes.outputs.main == 'true' ||
needs.changes.outputs.shared == 'true' ||
needs.changes.outputs.ai-core == 'true' ||
needs.changes.outputs.provider-registry == 'true' ||
needs.changes.outputs.test-infra == 'true' ||
github.event_name == 'push' ||
github.event_name == 'workflow_dispatch'
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
shard: [1, 2, 3]
include:
- os: ubuntu-latest
shards: 3
# macOS/Windows run only the platform-gated files (see the test step) —
# a small enough set that sharding it would just add runner overhead.
- os: macos-latest
shard: 1
shards: 1
- os: windows-2022
shard: 1
shards: 1
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: macos-latest dependencies fix
if: matrix.os == 'macos-latest'
run: brew install python-setuptools
- 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: Restore pnpm dependencies
uses: actions/cache/restore@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 and Package Tests
shell: bash
env:
AI_CORE_CHANGED: ${{ needs.changes.outputs.ai-core }}
UI_CHANGED: ${{ needs.changes.outputs.ui }}
SHARED_CHANGED: ${{ needs.changes.outputs.shared }}
PROVIDER_REGISTRY_CHANGED: ${{ needs.changes.outputs.provider-registry }}
SCRIPTS_CHANGED: ${{ needs.changes.outputs.scripts }}
TEST_INFRA_CHANGED: ${{ needs.changes.outputs.test-infra }}
run: |
# Non-Linux runners exist to execute the `process.platform` branches that Linux
# skips (symlinks, path casing, Windows command resolution). The rest of the
# suite hardcodes POSIX paths in its fixtures and carries no per-OS signal, so
# only the gated files run there.
if [[ "$RUNNER_OS" != "Linux" ]]; then
gated=$(grep -rl 'process\.platform' src/main src/shared --include='*.test.ts')
if [[ -z "$gated" ]]; then
echo "::error::No platform-gated test files found — refusing to run the whole suite."
exit 1
fi
pnpm rebuild:node
pnpm exec vitest run --project main --project shared $gated
exit 0
fi
projects=(--project main)
if [[ "$AI_CORE_CHANGED" == "true" || "$TEST_INFRA_CHANGED" == "true" || "${{ github.event_name }}" != "pull_request" ]]; then
projects+=(--project aiCore)
fi
if [[ "$UI_CHANGED" == "true" || "$TEST_INFRA_CHANGED" == "true" || "${{ github.event_name }}" != "pull_request" ]]; then
projects+=(--project ui)
fi
if [[ "$SHARED_CHANGED" == "true" || "$PROVIDER_REGISTRY_CHANGED" == "true" || "$TEST_INFRA_CHANGED" == "true" || "${{ github.event_name }}" != "pull_request" ]]; then
projects+=(--project shared)
fi
if [[ "$PROVIDER_REGISTRY_CHANGED" == "true" || "$SHARED_CHANGED" == "true" || "$TEST_INFRA_CHANGED" == "true" || "${{ github.event_name }}" != "pull_request" ]]; then
projects+=(--project provider-registry)
fi
if [[ "$SCRIPTS_CHANGED" == "true" || "$PROVIDER_REGISTRY_CHANGED" == "true" || "$TEST_INFRA_CHANGED" == "true" || "${{ github.event_name }}" != "pull_request" ]]; then
projects+=(--project scripts)
fi
pnpm rebuild:node
pnpm exec vitest run "${projects[@]}" --shard=${{ matrix.shard }}/${{ matrix.shards }}
general-test:
runs-on: ubuntu-latest
needs: [changes, main-test-shard, package-test]
if: >-
always() &&
(needs.changes.outputs.main == 'true' ||
needs.changes.outputs.shared == 'true' ||
needs.changes.outputs.ai-core == 'true' ||
needs.changes.outputs.ui == 'true' ||
needs.changes.outputs.provider-registry == 'true' ||
needs.changes.outputs.scripts == 'true' ||
needs.changes.outputs.test-infra == 'true' ||
github.event_name == 'push' ||
github.event_name == 'workflow_dispatch')
steps:
- name: Verify main and package tests
env:
MAIN_RESULT: ${{ needs.main-test-shard.result }}
PACKAGE_RESULT: ${{ needs.package-test.result }}
run: |
test "$MAIN_RESULT" = "success" || test "$MAIN_RESULT" = "skipped"
test "$PACKAGE_RESULT" = "success" || test "$PACKAGE_RESULT" = "skipped"
package-test:
runs-on: ubuntu-latest
needs: changes
if: >-
(needs.changes.outputs.ui == 'true' ||
needs.changes.outputs.scripts == 'true') &&
needs.changes.outputs.main != 'true' &&
needs.changes.outputs.shared != 'true' &&
needs.changes.outputs.ai-core != 'true' &&
needs.changes.outputs.provider-registry != 'true' &&
needs.changes.outputs.test-infra != 'true' &&
github.event_name == 'pull_request'
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: Restore pnpm dependencies
uses: actions/cache/restore@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: AI Core Test
if: needs.changes.outputs.ai-core == 'true' || needs.changes.outputs.test-infra == 'true' || github.event_name == 'push' || github.event_name == 'workflow_dispatch'
run: pnpm test:aicore
- name: UI Package Test
if: needs.changes.outputs.ui == 'true' || needs.changes.outputs.test-infra == 'true' || github.event_name == 'push' || github.event_name == 'workflow_dispatch'
run: pnpm test:pkg:ui
- name: Shared Package Test
if: needs.changes.outputs.shared == 'true' || needs.changes.outputs.provider-registry == 'true' || needs.changes.outputs.test-infra == 'true' || github.event_name == 'push' || github.event_name == 'workflow_dispatch'
run: pnpm test:shared
- name: Provider Registry Package Test
if: needs.changes.outputs.provider-registry == 'true' || needs.changes.outputs.shared == 'true' || needs.changes.outputs.test-infra == 'true' || github.event_name == 'push' || github.event_name == 'workflow_dispatch'
run: pnpm test:provider-registry
- name: Scripts Test
if: needs.changes.outputs.scripts == 'true' || needs.changes.outputs.provider-registry == 'true' || needs.changes.outputs.test-infra == 'true' || github.event_name == 'push' || github.event_name == 'workflow_dispatch'
run: pnpm test:scripts
renderer-test-shard:
name: renderer-test (${{ matrix.shard }}/5)
runs-on: ubuntu-latest
needs: changes
if: >-
needs.changes.outputs.renderer == 'true' ||
needs.changes.outputs.shared == 'true' ||
needs.changes.outputs.ai-core == 'true' ||
needs.changes.outputs.ui == 'true' ||
needs.changes.outputs.provider-registry == 'true' ||
needs.changes.outputs.extension-table-plus == 'true' ||
needs.changes.outputs.test-infra == 'true' ||
github.event_name == 'push' ||
github.event_name == 'workflow_dispatch'
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4, 5]
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: Restore pnpm dependencies
uses: actions/cache/restore@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 --shard=${{ matrix.shard }}/5
render-test:
runs-on: ubuntu-latest
needs: [changes, renderer-test-shard]
if: >-
always() &&
(needs.changes.outputs.renderer == 'true' ||
needs.changes.outputs.shared == 'true' ||
needs.changes.outputs.ai-core == 'true' ||
needs.changes.outputs.ui == 'true' ||
needs.changes.outputs.provider-registry == 'true' ||
needs.changes.outputs.extension-table-plus == 'true' ||
needs.changes.outputs.test-infra == 'true' ||
github.event_name == 'push' ||
github.event_name == 'workflow_dispatch')
steps:
- name: Verify renderer test shards
env:
SHARD_RESULT: ${{ needs.renderer-test-shard.result }}
run: test "$SHARD_RESULT" = "success"
notify:
runs-on: ubuntu-latest
needs: [basic-checks, general-test, package-test, render-test]
if: always() && (needs.basic-checks.result == 'failure' || needs.general-test.result == 'failure' || needs.package-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"