Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 57 additions & 83 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ jobs:
docs: ${{ steps.filter.outputs.docs }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- uses: dorny/paths-filter@v3
id: filter
with:
Expand All @@ -34,168 +36,140 @@ jobs:
- '*.md'
- '.github/**'

# ==================== 类型检查 ====================
typecheck:
runs-on: ubuntu-latest
timeout-minutes: 10
# ==================== 检测 self-hosted runner 可用性 ====================
check-runner:
runs-on: self-hosted
timeout-minutes: 1
continue-on-error: true
outputs:
available: ${{ steps.check.outputs.available }}
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: ${{ env.NODE_VERSION }}
cache: pnpm
- id: check
run: echo "available=true" >> $GITHUB_OUTPUT

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Type check
run: pnpm typecheck

# ==================== 单元测试 ====================
test:
runs-on: ubuntu-latest
# ==================== 快速检查 (合并 typecheck + build + i18n) ====================
quick-checks:
needs: check-runner
runs-on: ${{ needs.check-runner.outputs.available == 'true' && 'self-hosted' || 'ubuntu-latest' }}
timeout-minutes: 10
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: ${{ env.NODE_VERSION }}
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Unit tests
run: pnpm test

# ==================== i18n 检查 ====================
i18n:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout
uses: actions/checkout@v4

fetch-depth: 1
# GitHub-hosted: 需要安装环境
- name: Setup Bun
if: needs.check-runner.outputs.available != 'true'
uses: oven-sh/setup-bun@v2

- name: Setup pnpm
if: needs.check-runner.outputs.available != 'true'
uses: pnpm/action-setup@v4

- name: Setup Node.js
if: needs.check-runner.outputs.available != 'true'
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: pnpm

# 通用步骤
- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Type check
run: pnpm typecheck
- name: Build
run: pnpm build
- name: Check i18n keys
run: pnpm i18n:check

# ==================== 构建检查 ====================
build:
runs-on: ubuntu-latest
# ==================== 单元测试 (独立运行,耗时长) ====================
test:
needs: check-runner
runs-on: ${{ needs.check-runner.outputs.available == 'true' && 'self-hosted' || 'ubuntu-latest' }}
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v4

with:
fetch-depth: 1
- name: Setup pnpm
if: needs.check-runner.outputs.available != 'true'
uses: pnpm/action-setup@v4

- name: Setup Node.js
if: needs.check-runner.outputs.available != 'true'
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build
run: pnpm build
- name: Unit tests
run: pnpm test

# ==================== E2E 测试(仅代码变更时运行) ====================
e2e:
needs: changes
needs: [changes, check-runner]
if: needs.changes.outputs.code == 'true'
runs-on: ubuntu-latest
runs-on: ${{ needs.check-runner.outputs.available == 'true' && 'self-hosted' || 'ubuntu-latest' }}
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v4

with:
fetch-depth: 1
- name: Setup Bun
if: needs.check-runner.outputs.available != 'true'
uses: oven-sh/setup-bun@v2

- name: Setup pnpm
if: needs.check-runner.outputs.available != 'true'
uses: pnpm/action-setup@v4

- name: Setup Node.js
if: needs.check-runner.outputs.available != 'true'
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

# GitHub-hosted: 需要安装 Playwright
- name: Cache Playwright browsers
if: needs.check-runner.outputs.available != 'true'
id: playwright-cache
uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}

- name: Install Playwright browsers
if: steps.playwright-cache.outputs.cache-hit != 'true'
if: needs.check-runner.outputs.available != 'true' && steps.playwright-cache.outputs.cache-hit != 'true'
run: pnpm exec playwright install --with-deps chromium

- name: Install Playwright deps (if cached)
if: steps.playwright-cache.outputs.cache-hit == 'true'
if: needs.check-runner.outputs.available != 'true' && steps.playwright-cache.outputs.cache-hit == 'true'
run: pnpm exec playwright install-deps chromium

# E2E 测试
- name: E2E smoke (Mobile Chrome)
run: pnpm e2e bioforest-chains --project "Mobile Chrome"

# ==================== 最终状态检查 ====================
checks:
runs-on: ubuntu-latest
needs: [typecheck, test, i18n, build, e2e]
needs: [check-runner, quick-checks, test, e2e]
if: always()
steps:
- name: Report runner status
run: |
if [[ "${{ needs.check-runner.outputs.available }}" == "true" ]]; then
echo "🚀 Using self-hosted runner (M1 Mac) - fast mode"
else
echo "☁️ Using GitHub-hosted runner (ubuntu-latest)"
fi
- name: Check results
run: |
if [[ "${{ needs.typecheck.result }}" == "failure" ]]; then
echo "Typecheck failed"
if [[ "${{ needs.quick-checks.result }}" == "failure" ]]; then
echo "Quick checks (typecheck/build/i18n) failed"
exit 1
fi
if [[ "${{ needs.test.result }}" == "failure" ]]; then
echo "Tests failed"
exit 1
fi
if [[ "${{ needs.i18n.result }}" == "failure" ]]; then
echo "i18n check failed"
exit 1
fi
if [[ "${{ needs.build.result }}" == "failure" ]]; then
echo "Build failed"
exit 1
fi
if [[ "${{ needs.e2e.result }}" == "failure" ]]; then
echo "E2E failed"
exit 1
Expand Down
82 changes: 82 additions & 0 deletions scripts/self-hosted/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Self-hosted Runner 配置指南

配置 GitHub Actions self-hosted runner,加速 CI/CD。

## 快速开始

```bash
# 1. 配置环境 (Node.js, pnpm, bun, Playwright)
./scripts/self-hosted/env-setup.sh

# 2. 获取 GitHub Runner 注册 token
TOKEN=$(gh api -X POST repos/BioforestChain/KeyApp/actions/runners/registration-token --jq '.token')

# 3. 配置多个 runner (推荐 4 个)
./scripts/self-hosted/setup-runners.sh 4 "$TOKEN"

# 4. 启动 runners
~/actions-runners/start-all.sh
```

## 脚本说明

| 脚本 | 说明 |
|------|------|
| `env-setup.sh` | 安装 Node.js, pnpm, bun, Playwright |
| `setup-runners.sh` | 配置多个 GitHub Actions runner |

## CI 工作流程

**Self-hosted 可用时 (快速模式):**
- 跳过环境安装 (预装)
- 直接: checkout → pnpm install → run
- 多个 runner 并行执行

**Self-hosted 不可用时 (Fallback):**
- 自动切换到 GitHub-hosted ubuntu-latest
- 完整安装流程
- 确保 CI 不中断

## Runner 管理

脚本生成在 `~/actions-runners/`:

```bash
~/actions-runners/start-all.sh # 启动
~/actions-runners/stop-all.sh # 停止
~/actions-runners/status.sh # 状态
~/actions-runners/restart-all.sh # 重启
```

## 开机自启动 (macOS)

```bash
# 创建 LaunchAgent
cat > ~/Library/LaunchAgents/com.github.actions-runner.plist << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.github.actions-runner</string>
<key>ProgramArguments</key>
<array>
<string>sh</string>
<string>-c</string>
<string>$HOME/actions-runners/start-all.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
EOF

# 加载
launchctl load ~/Library/LaunchAgents/com.github.actions-runner.plist
```

## 系统要求

- macOS (M1/M2) 或 Linux (x64/arm64)
- 8GB+ RAM (推荐 16GB)
- 稳定网络
Loading