diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml
deleted file mode 100644
index 6fd0af3b6..000000000
--- a/.github/workflows/e2e-tests.yml
+++ /dev/null
@@ -1,91 +0,0 @@
-name: E2E Tests
-# 运行时显示的名称
-run-name: E2E Tests
-
-on:
- push:
- branches: [ develop, main ]
- pull_request:
- branches: [ develop, main ]
-
-jobs:
- e2e-tests:
- timeout-minutes: 60
- # 指定运行环境为最新版本的ubuntu
- runs-on: ubuntu-latest
-
- steps:
- # 步骤1: 检出代码 (处理所有情况)
-
- # 场景 A: 当工作流由 push 事件触发时 (例如合并到 develop 分支)
- # 或者由仓库内部成员创建的 PR 触发时,执行此步骤。
- - name: Checkout for Develop Repo Push or PR
- # 条件:事件不是来自 fork 的 PR
- if: github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'push'
- uses: actions/checkout@v4
- with:
- # 对于源仓库 PR,确认检出最新的代码
- ref: ${{ github.event.pull_request.head.sha || github.sha }}
-
- # 场景 B: 当工作流由外部 fork 仓库的 PR 触发时,执行此步骤
- - name: Checkout for Fork PR
- # 条件:事件是来自 fork 的 PR
- if: github.event.pull_request.head.repo.full_name != github.repository
- uses: actions/checkout@v4
- with:
- repository: ${{ github.event.pull_request.head.repo.full_name }}
- ref: ${{ github.head_ref }}
-
- # 步骤2: 设置pnpm包管理器
- - name: Setup pnpm
- uses: pnpm/action-setup@v4
-
- # 步骤3: 设置Node.js环境
- - name: Setup Node
- uses: actions/setup-node@v4
- with:
- node-version: 20 # 使用Node.js 20版本
- registry-url: 'https://registry.npmjs.org' # 设置npm registry地址
-
- # 步骤4: 获取pnpm缓存目录路径
- - name: Get pnpm store directory
- id: pnpm-cache
- run: |
- echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT
-
- # 步骤5: 配置pnpm缓存
- - uses: actions/cache@v3
- name: Setup pnpm cache
- with:
- path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
- # 使用操作系统类型和pnpm-lock.yaml的哈希值作为缓存键
- key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- restore-keys: |
- ${{ runner.os }}-pnpm-store-
-
- # 步骤6: 安装项目依赖
- - name: Install dependencies
- run: pnpm i --no-frozen-lockfile
-
- # 步骤7: 构建组件
- - name: Run Build Components
- run: pnpm build
-
- # 步骤8: 安装 Playwright 浏览器
- - name: Install Playwright Browser
- run: |
- cd packages/test
- npx playwright install --with-deps chromium
-
- # 步骤9: 运行 E2E 测试
- - name: Run Playwright Tests
- run: pnpm test
-
- # 步骤10: 上传 Playwright 测试报告
- - name: Upload Playwright Test Report
- uses: actions/upload-artifact@v4
- if: always()
- with:
- name: playwright-report
- path: packages/test/playwright-report/
- retention-days: 13
\ No newline at end of file
diff --git a/.github/workflows/pr-ci-build.yml b/.github/workflows/pr-ci-build.yml
new file mode 100644
index 000000000..d4caa4441
--- /dev/null
+++ b/.github/workflows/pr-ci-build.yml
@@ -0,0 +1,53 @@
+name: 'CI: Build'
+
+on:
+ workflow_call:
+ inputs:
+ skip-playground:
+ description: 'Skip playground build'
+ type: boolean
+ default: false
+
+jobs:
+ build-job:
+ name: build
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup pnpm
+ uses: pnpm/action-setup@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 20
+
+ - name: Get pnpm store directory
+ id: pnpm-cache
+ run: echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT
+
+ - name: Setup pnpm cache
+ uses: actions/cache@v4
+ with:
+ path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
+ key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
+ restore-keys: |
+ ${{ runner.os }}-pnpm-store-
+
+ - name: Install dependencies
+ run: pnpm i --no-frozen-lockfile
+
+ - name: Build components
+ run: pnpm build
+
+ - name: Upload build artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: build-${{ github.sha }}
+ path: |
+ packages/components/dist
+ packages/kit/dist
+ packages/svgs/dist
+ retention-days: 1
diff --git a/.github/workflows/pr-ci-e2e-test.yml b/.github/workflows/pr-ci-e2e-test.yml
new file mode 100644
index 000000000..600ad414f
--- /dev/null
+++ b/.github/workflows/pr-ci-e2e-test.yml
@@ -0,0 +1,58 @@
+name: 'CI: E2E Test'
+
+on:
+ workflow_call:
+
+jobs:
+ test-job:
+ name: e2e-test
+ runs-on: ubuntu-latest
+ timeout-minutes: 60
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup pnpm
+ uses: pnpm/action-setup@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 20
+
+ - name: Get pnpm store directory
+ id: pnpm-cache
+ run: echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT
+
+ - name: Setup pnpm cache
+ uses: actions/cache@v4
+ with:
+ path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
+ key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
+ restore-keys: |
+ ${{ runner.os }}-pnpm-store-
+
+ - name: Install dependencies
+ run: pnpm i --no-frozen-lockfile
+
+ - name: Download build artifacts
+ uses: actions/download-artifact@v4
+ with:
+ name: build-${{ github.sha }}
+ path: packages
+
+ - name: Install Playwright Browser
+ run: |
+ cd packages/test
+ npx playwright install --with-deps chromium
+
+ - name: Run Playwright Tests
+ run: pnpm test
+
+ - name: Upload Playwright Test Report
+ uses: actions/upload-artifact@v4
+ if: always()
+ with:
+ name: playwright-report-${{ github.sha }}
+ path: packages/test/playwright-report/
+ retention-days: 13
diff --git a/.github/workflows/pr-ci-preview.yml b/.github/workflows/pr-ci-preview.yml
new file mode 100644
index 000000000..17f7153e4
--- /dev/null
+++ b/.github/workflows/pr-ci-preview.yml
@@ -0,0 +1,160 @@
+name: 'CI: Preview'
+
+on:
+ workflow_call:
+ inputs:
+ pr-number:
+ description: 'Pull Request number'
+ type: number
+ required: true
+
+jobs:
+ preview-job:
+ name: preview
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup pnpm
+ uses: pnpm/action-setup@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 20
+ registry-url: 'https://registry.npmjs.org'
+
+ - name: Get pnpm store directory
+ id: pnpm-cache
+ run: echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT
+
+ - name: Setup pnpm cache
+ uses: actions/cache@v4
+ with:
+ path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
+ key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
+ restore-keys: |
+ ${{ runner.os }}-pnpm-store-
+
+ - name: Install dependencies
+ run: pnpm i --no-frozen-lockfile
+
+ - name: Download build artifacts
+ uses: actions/download-artifact@v4
+ with:
+ name: build-${{ github.sha }}
+ path: packages
+
+ - name: Build docs only (components already built)
+ run: pnpm -F docs build
+
+ - name: Comment build generating
+ if: github.event_name == 'pull_request'
+ uses: actions-cool/maintain-one-comment@v3.1.1
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ body: |
+ [
](#)
+
+ 🔨 Building packages...
+
+ body-include: ''
+ number: ${{ inputs.pr-number }}
+
+ - name: Publish to pkg.pr.new
+ id: pkg-pr-new
+ run: |
+ npx pkg-pr-new publish \
+ ./packages/components \
+ ./packages/kit \
+ ./packages/svgs \
+ --pnpm \
+ --json output.json \
+ --comment=off
+
+ - name: Post or update pkg.pr.new comment
+ if: github.event_name == 'pull_request'
+ uses: actions/github-script@v7
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const fs = require('fs');
+ const output = JSON.parse(fs.readFileSync('output.json', 'utf8'));
+
+ const sha = context.payload.pull_request.head.sha.substring(0, 7);
+
+ const packages = output.packages.map((p) => {
+ const shortUrl = p.url.replace(/@[a-f0-9]{40}/, '@' + sha);
+ return 'pnpm add ' + shortUrl;
+ }).join('\n\n');
+
+ const commitUrl = 'https://github.com/' + context.repo.owner + '/' + context.repo.repo + '/commit/' + sha;
+
+ const body = '## 📦 Package Preview\n\n' + packages + '\n\n**commit:** [' + sha + '](' + commitUrl + ')\n\n';
+ const botCommentIdentifier = '';
+
+ const comments = await github.rest.issues.listComments({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: context.issue.number,
+ });
+
+ const existingComment = comments.data.find((comment) =>
+ comment.body.includes(botCommentIdentifier)
+ );
+
+ if (existingComment) {
+ await github.rest.issues.updateComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ comment_id: existingComment.id,
+ body: body,
+ });
+ } else {
+ await github.rest.issues.createComment({
+ issue_number: context.issue.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ body: body,
+ });
+ }
+
+ - name: Deploy Site to Surge
+ id: deploy
+ run: |
+ DEPLOY_DOMAIN=preview-${{ inputs.pr-number }}-tiny-robot.surge.sh
+ echo "Deploying to: https://$DEPLOY_DOMAIN"
+ npx surge --project ./docs/dist --domain $DEPLOY_DOMAIN --token $SURGE_TOKEN
+ echo "url=https://$DEPLOY_DOMAIN" >> $GITHUB_OUTPUT
+ env:
+ SURGE_TOKEN: ${{ secrets.SURGE_TOKEN }}
+
+ - name: Comment build success
+ if: ${{ success() && github.event_name == 'pull_request' }}
+ uses: actions-cool/maintain-one-comment@v3.1.1
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ body: |
+ [
](https://preview-${{ inputs.pr-number }}-tiny-robot.surge.sh)
+
+ ✅ Preview build completed successfully!
+
+ > Click the image above to preview.
+ > Preview will be automatically removed when this PR is closed.
+
+ body-include: ''
+ number: ${{ inputs.pr-number }}
+
+ - name: Comment build failed
+ if: ${{ failure() && github.event_name == 'pull_request' }}
+ uses: actions-cool/maintain-one-comment@v3.1.1
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ body: |
+ [
](#)
+
+ ❌ Build failed. Please check the logs for details.
+
+ body-include: ''
+ number: ${{ inputs.pr-number }}
diff --git a/.github/workflows/pr-ci.yml b/.github/workflows/pr-ci.yml
new file mode 100644
index 000000000..a1c4d353d
--- /dev/null
+++ b/.github/workflows/pr-ci.yml
@@ -0,0 +1,43 @@
+name: ci
+
+on:
+ push:
+ branches: [ develop, main ]
+ pull_request:
+ types: [opened, synchronize, reopened]
+
+# 取消同一 PR 的旧运行
+concurrency:
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ # 构建组件
+ build:
+ # 跳过不需要构建的 PR(仅检查 PR 标题)
+ if: |
+ !(
+ github.event_name == 'pull_request' && (
+ contains(github.event.pull_request.title, 'docs:') ||
+ contains(github.event.pull_request.title, 'chore: release') ||
+ startsWith(github.event.pull_request.title, 'v') ||
+ startsWith(github.event.pull_request.title, 'V')
+ )
+ )
+ uses: ./.github/workflows/pr-ci-build.yml
+ with:
+ skip-playground: true
+
+ # 生成预览(依赖构建,仅 PR 事件)
+ preview:
+ needs: [build]
+ if: github.event_name == 'pull_request'
+ uses: ./.github/workflows/pr-ci-preview.yml
+ with:
+ pr-number: ${{ github.event.pull_request.number }}
+ secrets: inherit
+
+ # E2E 测试(依赖构建,与预览并行)
+ e2e-test:
+ needs: [build]
+ uses: ./.github/workflows/pr-ci-e2e-test.yml
diff --git a/.github/workflows/pr-cleanup.yml b/.github/workflows/pr-cleanup.yml
new file mode 100644
index 000000000..11b74472a
--- /dev/null
+++ b/.github/workflows/pr-cleanup.yml
@@ -0,0 +1,31 @@
+name: PR Docs Cleanup
+
+on:
+ pull_request:
+ types: [closed]
+
+jobs:
+ cleanup:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Remove Surge deployment
+ run: |
+ DEPLOY_DOMAIN=preview-${{ github.event.pull_request.number }}-tiny-robot.surge.sh
+ echo "Removing deployment: $DEPLOY_DOMAIN"
+ npx surge teardown $DEPLOY_DOMAIN --token $SURGE_TOKEN
+ env:
+ SURGE_TOKEN: ${{ secrets.SURGE_TOKEN }}
+ continue-on-error: true
+
+ - name: Comment cleanup status
+ uses: actions-cool/maintain-one-comment@v3.1.1
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ body: |
+ ## 🧹 Preview Cleaned Up
+
+ The preview deployment has been removed.
+
+
+ body-include: ''
+ number: ${{ github.event.pull_request.number }}