|
23253
|
17733
|
6
|
5
|
f8c99c0bccfc306a4dcc9afd03e1247a4ebd4a97
|
0
|
安装依赖
|
0
|
name: Test Pipeline
"on":
push:
name: Test Pipeline
"on":
push:
branches: [main, develop, 'feature/**', 'claude/**']
pull_request:
branches: [main, develop]
workflow_dispatch:
inputs:
coverage_threshold:
description: '覆盖率阈值 (%)'
required: false
default: '80'
run_ai_tests:
description: '运行 AI 模块测试'
required: false
default: 'true'
type: boolean
run_api_tests:
description: '运行 API 集成测试'
required: false
default: 'true'
type: boolean
run_security_audit:
description: '运行安全审计'
required: false
default: 'true'
type: boolean
env:
COVERAGE_THRESHOLD: ${{ github.event.inputs.coverage_threshold || '80' }}
NODE_VERSION: "20"
PNPM_VERSION: "8"
jobs:
setup:
name: 安装依赖
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: 安装 pnpm
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- name: 设置 Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- name: 安装依赖
run: pnpm install --frozen-lockfile
- name: 构建共享包
run: pnpm --filter @juhi/shared run build
- name: 缓存依赖
uses: actions/cache/save@v4
with:
key: pipeline-deps-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
path: |
node_modules
backend/node_modules
frontend/node_modules
shared/node_modules
shared/dist
e2e/node_modules
...
|
setup
|
null
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1777219010
|
1777218745
|
1777219010
|
|
0
|
|
0
|
Edit
Delete
|
|
23274
|
17734
|
6
|
5
|
f8c99c0bccfc306a4dcc9afd03e1247a4ebd4a97
|
0
|
生产环境健康检查
|
1
|
name: Health Check
"on":
schedule:
name: Health Check
"on":
schedule:
# 每 5 分钟检查一次
- cron: '*/5 * * * *'
workflow_dispatch:
jobs:
health-check:
name: 生产环境健康检查
runs-on: ubuntu-latest
if: github.repository == 'your-org/juhi' # 替换为实际仓库
steps:
- id: api-health
name: API 健康检查
run: |
RESPONSE=$(curl -sf https://juhi.example.com/health || echo '{"status":"error"}')
echo "response=$RESPONSE" >> $GITHUB_OUTPUT
STATUS=$(echo $RESPONSE | jq -r '.status // "error"')
if [ "$STATUS" != "ok" ]; then
echo "API 健康检查失败"
exit 1
fi
echo "API 健康检查通过"
- name: 前端可访问性检查
run: |
HTTP_STATUS=$(curl -so /dev/null -w "%{http_code}" https://juhi.example.com/)
if [ "$HTTP_STATUS" != "200" ]; then
echo "前端返回 HTTP $HTTP_STATUS"
exit 1
fi
echo "前端可访问性检查通过"
- name: SSL 证书检查
run: |
EXPIRY_DATE=$(echo | openssl s_client -servername juhi.example.com -connect juhi.example.com:443 2>/dev/null | openssl x509 -noout -enddate | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$EXPIRY_DATE" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( ($EXPIRY_EPOCH - $NOW_EPOCH) / 86400 ))
echo "SSL 证书剩余 $DAYS_LEFT 天"
if [ $DAYS_LEFT -lt 7 ]; then
echo "::warning::SSL 证书将在 $DAYS_LEFT 天后过期!"
fi
if [ $DAYS_LEFT -lt 0 ]; then
echo "SSL 证书已过期"
exit 1
fi
- name: 响应时间检查
run: |
RESPONSE_TIME=$(curl -so /dev/null -w "%{time_total}" https://juhi.example.com/health)
echo "API 响应时间: ${RESPONSE_TIME}s"
# 响应时间超过 5 秒告警
if (( $(echo "$RESPONSE_TIME > 5.0" | bc -l) )); then
echo "::warning::API 响应时间过长: ${RESPONSE_TIME}s"
fi
- if: failure()
name: Slack 通知(失败时)
uses: 8398a7/action-slack@v3
with:
fields: repo,message,commit,author,action,eventName,workflow
status: ${{ job.status }}
text: "\U0001F6A8 生产环境健康检查失败!请立即检查。"
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
...
|
health-check
|
null
|
["ubuntu-latest"]
|
20226
|
4
|
1777222403
|
1777222403
|
1777218950
|
1777222404
|
|
0
|
|
0
|
Edit
Delete
|
|
23275
|
17735
|
6
|
5
|
bd8162cf2c08d175a1cab0f69d42b648bb825c10
|
0
|
Frontend Type Check
|
0
|
name: CI
"on":
push:
branches: name: CI
"on":
push:
branches: [main]
pull_request:
branches: [main]
env:
NODE_VERSION: "20"
PNPM_VERSION: "9"
jobs:
frontend-typecheck:
name: Frontend Type Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- run: pnpm install --frozen-lockfile
- name: Build shared package
run: pnpm -C shared run build
- name: 前端类型检查(vue-tsc --noEmit)
run: pnpm --filter ./frontend run type-check
permissions:
contents: read
...
|
frontend-typecheck
|
null
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1777219584
|
1777219008
|
1777219584
|
|
0
|
|
0
|
Edit
Delete
|
|
23276
|
17735
|
6
|
5
|
bd8162cf2c08d175a1cab0f69d42b648bb825c10
|
0
|
Lint & Type Check
|
0
|
name: CI
"on":
push:
branches: name: CI
"on":
push:
branches: [main]
pull_request:
branches: [main]
env:
NODE_VERSION: "20"
PNPM_VERSION: "9"
jobs:
lint-and-typecheck:
name: Lint & Type Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- run: pnpm install --frozen-lockfile
- name: Generate Prisma Client
run: pnpm -C backend exec prisma generate
- name: Build shared package
run: pnpm -C shared run build
- name: Backend type check
run: pnpm -C backend run type-check
- name: Lint
run: pnpm -C backend run lint || true
permissions:
contents: read
...
|
lint-and-typecheck
|
null
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1777219584
|
1777219008
|
1777219584
|
|
0
|
|
0
|
Edit
Delete
|
|
23279
|
17736
|
6
|
5
|
bd8162cf2c08d175a1cab0f69d42b648bb825c10
|
0
|
pr-validation
|
1
|
name: E2E Tests
"on":
# PR validation name: E2E Tests
"on":
# PR validation - 运行关键测试
pull_request:
branches: [main, develop]
paths:
- 'frontend/**'
- 'backend/**'
- 'e2e/**'
- 'package.json'
- 'pnpm-lock.yaml'
# Push to main - 运行完整测试套件
push:
branches: [main]
# 每日定时全量测试 (UTC 时间 00:00 = 北京时间 08:00)
schedule:
- cron: '0 0 * * *'
# 手动触发
workflow_dispatch:
inputs:
test_suite:
description: 'Test suite to run'
required: true
default: 'all'
type: choice
options:
- all
- critical
- business-flows
- visual-regression
- performance
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
pr-validation:
name: pr-validation
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup test database
run: |
cd backend
npx prisma migrate deploy
npx prisma db seed
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
- name: Build backend
run: |
cd backend
npm run build
- name: Build frontend
run: |
cd frontend
npm run build
- name: Install Playwright Browsers
run: npx playwright install --with-deps chromium
- name: Start backend server
run: |
cd backend
npm run start &
sleep 10
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
REDIS_URL: redis://localhost:6379
NODE_ENV: test
- name: Start frontend server
run: |
cd frontend
npm run preview &
sleep 5
env:
VITE_API_URL: http://localhost:3000
- name: Run critical E2E tests
run: |
cd e2e
npx playwright test \
tests/auth/login.spec.ts \
tests/navigation/full-menu-click.spec.ts \
tests/multi-tenant/data-isolation.spec.ts \
tests/form-interactions/ \
--reporter=html
env:
E2E_BASE_URL: http://localhost:5173
E2E_TEST_USER: admin@juhi.com
E2E_TEST_PASSWORD: Admin@123
- if: always()
name: Upload test results
uses: actions/upload-artifact@v4
with:
name: playwright-report-pr
path: e2e/playwright-report/
retention-days: "7"
- if: always() && github.event_name == 'pull_request'
name: Comment PR with test results
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const reportPath = 'e2e/playwright-report/index.html';
const testsPassed = !fs.existsSync('e2e/test-results/');
const comment = testsPassed
? '✅ E2E 测试通过!'
: '❌ E2E 测试失败,请查看报告';
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: comment
});
timeout-minutes: "30"
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: juhi_test
POSTGRES_PASSWORD: test_password
POSTGRES_USER: test_user
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
...
|
pr-validation
|
null
|
["ubuntu-latest"]
|
20227
|
4
|
1777222405
|
1777222405
|
1777219009
|
1777222406
|
|
0
|
|
0
|
Edit
Delete
|
|
23280
|
17736
|
6
|
5
|
bd8162cf2c08d175a1cab0f69d42b648bb825c10
|
0
|
full-test-suite (chromium)
|
1
|
name: E2E Tests
"on":
# PR validation name: E2E Tests
"on":
# PR validation - 运行关键测试
pull_request:
branches: [main, develop]
paths:
- 'frontend/**'
- 'backend/**'
- 'e2e/**'
- 'package.json'
- 'pnpm-lock.yaml'
# Push to main - 运行完整测试套件
push:
branches: [main]
# 每日定时全量测试 (UTC 时间 00:00 = 北京时间 08:00)
schedule:
- cron: '0 0 * * *'
# 手动触发
workflow_dispatch:
inputs:
test_suite:
description: 'Test suite to run'
required: true
default: 'all'
type: choice
options:
- all
- critical
- business-flows
- visual-regression
- performance
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
full-test-suite:
name: full-test-suite (chromium)
runs-on: ubuntu-latest
if: github.event_name == 'push' || github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.test_suite == 'all')
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup test database
run: |
cd backend
npx prisma migrate deploy
npx prisma db seed
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
- name: Build backend
run: |
cd backend
npm run build
- name: Build frontend
run: |
cd frontend
npm run build
- name: Install Playwright Browsers
run: npx playwright install --with-deps ${{ matrix.browser }}
- name: Start backend server
run: |
cd backend
npm run start &
sleep 10
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
REDIS_URL: redis://localhost:6379
NODE_ENV: test
- name: Start frontend server
run: |
cd frontend
npm run preview &
sleep 5
env:
VITE_API_URL: http://localhost:3000
- name: Run all E2E tests
run: |
cd e2e
npx playwright test --project=${{ matrix.browser }} --reporter=html,json
env:
E2E_BASE_URL: http://localhost:5173
E2E_TEST_USER: admin@juhi.com
E2E_TEST_PASSWORD: Admin@123
- if: always()
name: Upload test results
uses: actions/upload-artifact@v4
with:
name: playwright-report-${{ matrix.browser }}
path: e2e/playwright-report/
retention-days: "30"
- if: always()
name: Upload test artifacts
uses: actions/upload-artifact@v4
with:
name: test-results-${{ matrix.browser }}
path: e2e/test-results/
retention-days: "30"
timeout-minutes: "60"
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: juhi_test
POSTGRES_PASSWORD: test_password
POSTGRES_USER: test_user
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
strategy:
fail-fast: "false"
matrix:
browser:
- chromium
...
|
full-test-suite
|
null
|
["ubuntu-latest"]
|
20228
|
2
|
1777222407
|
1777222498
|
1777219009
|
1777222498
|
|
0
|
|
0
|
Edit
Delete
|
|
23281
|
17736
|
6
|
5
|
bd8162cf2c08d175a1cab0f69d42b648bb825c10
|
0
|
full-test-suite (firefox)
|
1
|
name: E2E Tests
"on":
# PR validation name: E2E Tests
"on":
# PR validation - 运行关键测试
pull_request:
branches: [main, develop]
paths:
- 'frontend/**'
- 'backend/**'
- 'e2e/**'
- 'package.json'
- 'pnpm-lock.yaml'
# Push to main - 运行完整测试套件
push:
branches: [main]
# 每日定时全量测试 (UTC 时间 00:00 = 北京时间 08:00)
schedule:
- cron: '0 0 * * *'
# 手动触发
workflow_dispatch:
inputs:
test_suite:
description: 'Test suite to run'
required: true
default: 'all'
type: choice
options:
- all
- critical
- business-flows
- visual-regression
- performance
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
full-test-suite:
name: full-test-suite (firefox)
runs-on: ubuntu-latest
if: github.event_name == 'push' || github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.test_suite == 'all')
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup test database
run: |
cd backend
npx prisma migrate deploy
npx prisma db seed
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
- name: Build backend
run: |
cd backend
npm run build
- name: Build frontend
run: |
cd frontend
npm run build
- name: Install Playwright Browsers
run: npx playwright install --with-deps ${{ matrix.browser }}
- name: Start backend server
run: |
cd backend
npm run start &
sleep 10
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
REDIS_URL: redis://localhost:6379
NODE_ENV: test
- name: Start frontend server
run: |
cd frontend
npm run preview &
sleep 5
env:
VITE_API_URL: http://localhost:3000
- name: Run all E2E tests
run: |
cd e2e
npx playwright test --project=${{ matrix.browser }} --reporter=html,json
env:
E2E_BASE_URL: http://localhost:5173
E2E_TEST_USER: admin@juhi.com
E2E_TEST_PASSWORD: Admin@123
- if: always()
name: Upload test results
uses: actions/upload-artifact@v4
with:
name: playwright-report-${{ matrix.browser }}
path: e2e/playwright-report/
retention-days: "30"
- if: always()
name: Upload test artifacts
uses: actions/upload-artifact@v4
with:
name: test-results-${{ matrix.browser }}
path: e2e/test-results/
retention-days: "30"
timeout-minutes: "60"
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: juhi_test
POSTGRES_PASSWORD: test_password
POSTGRES_USER: test_user
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
strategy:
fail-fast: "false"
matrix:
browser:
- firefox
...
|
full-test-suite
|
null
|
["ubuntu-latest"]
|
20229
|
2
|
1777222498
|
1777222528
|
1777219009
|
1777222529
|
|
0
|
|
0
|
Edit
Delete
|
|
23282
|
17736
|
6
|
5
|
bd8162cf2c08d175a1cab0f69d42b648bb825c10
|
0
|
full-test-suite (webkit)
|
1
|
name: E2E Tests
"on":
# PR validation name: E2E Tests
"on":
# PR validation - 运行关键测试
pull_request:
branches: [main, develop]
paths:
- 'frontend/**'
- 'backend/**'
- 'e2e/**'
- 'package.json'
- 'pnpm-lock.yaml'
# Push to main - 运行完整测试套件
push:
branches: [main]
# 每日定时全量测试 (UTC 时间 00:00 = 北京时间 08:00)
schedule:
- cron: '0 0 * * *'
# 手动触发
workflow_dispatch:
inputs:
test_suite:
description: 'Test suite to run'
required: true
default: 'all'
type: choice
options:
- all
- critical
- business-flows
- visual-regression
- performance
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
full-test-suite:
name: full-test-suite (webkit)
runs-on: ubuntu-latest
if: github.event_name == 'push' || github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.test_suite == 'all')
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup test database
run: |
cd backend
npx prisma migrate deploy
npx prisma db seed
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
- name: Build backend
run: |
cd backend
npm run build
- name: Build frontend
run: |
cd frontend
npm run build
- name: Install Playwright Browsers
run: npx playwright install --with-deps ${{ matrix.browser }}
- name: Start backend server
run: |
cd backend
npm run start &
sleep 10
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
REDIS_URL: redis://localhost:6379
NODE_ENV: test
- name: Start frontend server
run: |
cd frontend
npm run preview &
sleep 5
env:
VITE_API_URL: http://localhost:3000
- name: Run all E2E tests
run: |
cd e2e
npx playwright test --project=${{ matrix.browser }} --reporter=html,json
env:
E2E_BASE_URL: http://localhost:5173
E2E_TEST_USER: admin@juhi.com
E2E_TEST_PASSWORD: Admin@123
- if: always()
name: Upload test results
uses: actions/upload-artifact@v4
with:
name: playwright-report-${{ matrix.browser }}
path: e2e/playwright-report/
retention-days: "30"
- if: always()
name: Upload test artifacts
uses: actions/upload-artifact@v4
with:
name: test-results-${{ matrix.browser }}
path: e2e/test-results/
retention-days: "30"
timeout-minutes: "60"
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: juhi_test
POSTGRES_PASSWORD: test_password
POSTGRES_USER: test_user
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
strategy:
fail-fast: "false"
matrix:
browser:
- webkit
...
|
full-test-suite
|
null
|
["ubuntu-latest"]
|
20230
|
2
|
1777222529
|
1777222619
|
1777219009
|
1777222620
|
|
0
|
|
0
|
Edit
Delete
|
|
23283
|
17736
|
6
|
5
|
bd8162cf2c08d175a1cab0f69d42b648bb825c10
|
0
|
business-flows
|
1
|
name: E2E Tests
"on":
# PR validation name: E2E Tests
"on":
# PR validation - 运行关键测试
pull_request:
branches: [main, develop]
paths:
- 'frontend/**'
- 'backend/**'
- 'e2e/**'
- 'package.json'
- 'pnpm-lock.yaml'
# Push to main - 运行完整测试套件
push:
branches: [main]
# 每日定时全量测试 (UTC 时间 00:00 = 北京时间 08:00)
schedule:
- cron: '0 0 * * *'
# 手动触发
workflow_dispatch:
inputs:
test_suite:
description: 'Test suite to run'
required: true
default: 'all'
type: choice
options:
- all
- critical
- business-flows
- visual-regression
- performance
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
business-flows:
name: business-flows
runs-on: ubuntu-latest
if: github.event_name == 'workflow_dispatch' && github.event.inputs.test_suite == 'business-flows'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup test database
run: |
cd backend
npx prisma migrate deploy
npx prisma db seed
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
- name: Build and start services
run: |
cd backend && npm run build && npm run start &
cd frontend && npm run build && npm run preview &
sleep 15
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
REDIS_URL: redis://localhost:6379
NODE_ENV: test
VITE_API_URL: http://localhost:3000
- name: Install Playwright
run: npx playwright install --with-deps chromium
- name: Run business flow tests
run: |
cd e2e
npx playwright test tests/business-flows/ --reporter=html,json
env:
E2E_BASE_URL: http://localhost:5173
E2E_TEST_USER: admin@juhi.com
E2E_TEST_PASSWORD: Admin@123
- if: always()
name: Upload test results
uses: actions/upload-artifact@v4
with:
name: business-flows-report
path: e2e/playwright-report/
retention-days: "30"
timeout-minutes: "45"
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: juhi_test
POSTGRES_PASSWORD: test_password
POSTGRES_USER: test_user
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
...
|
business-flows
|
null
|
["ubuntu-latest"]
|
20231
|
4
|
1777222620
|
1777222620
|
1777219009
|
1777222620
|
|
0
|
|
0
|
Edit
Delete
|
|
23284
|
17736
|
6
|
5
|
bd8162cf2c08d175a1cab0f69d42b648bb825c10
|
0
|
performance-benchmarks
|
1
|
name: E2E Tests
"on":
# PR validation name: E2E Tests
"on":
# PR validation - 运行关键测试
pull_request:
branches: [main, develop]
paths:
- 'frontend/**'
- 'backend/**'
- 'e2e/**'
- 'package.json'
- 'pnpm-lock.yaml'
# Push to main - 运行完整测试套件
push:
branches: [main]
# 每日定时全量测试 (UTC 时间 00:00 = 北京时间 08:00)
schedule:
- cron: '0 0 * * *'
# 手动触发
workflow_dispatch:
inputs:
test_suite:
description: 'Test suite to run'
required: true
default: 'all'
type: choice
options:
- all
- critical
- business-flows
- visual-regression
- performance
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
performance-benchmarks:
name: performance-benchmarks
runs-on: ubuntu-latest
if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.test_suite == 'performance')
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup test database
run: |
cd backend
npx prisma migrate deploy
npx prisma db seed
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
- name: Build and start services
run: |
cd backend && npm run build && npm run start &
cd frontend && npm run build && npm run preview &
sleep 15
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
REDIS_URL: redis://localhost:6379
NODE_ENV: production
VITE_API_URL: http://localhost:3000
- name: Install Playwright
run: npx playwright install --with-deps chromium
- name: Run performance tests
run: |
cd e2e
npx playwright test \
tests/flows/complete-sales-flow.spec.ts \
tests/lead-to-cash.spec.ts \
--reporter=html,json
env:
E2E_BASE_URL: http://localhost:5173
E2E_TEST_USER: admin@juhi.com
E2E_TEST_PASSWORD: Admin@123
- name: Generate performance report
run: |
cd e2e
node scripts/generate-performance-report.js
- if: always()
name: Upload performance results
uses: actions/upload-artifact@v4
with:
name: performance-report
path: |
e2e/playwright-report/
e2e/performance-results.json
retention-days: "90"
- if: github.event_name == 'schedule'
name: Comment with performance results
uses: actions/github-script@v7
with:
script: "const fs = require('fs');\nconst results = JSON.parse(fs.readFileSync('e2e/performance-results.json', 'utf8'));\n\nconst comment = `\n## \U0001F4CA 每日性能基准测试报告\n\n- **页面加载时间**: ${results.pageLoadTime}ms\n- **首次内容绘制 (FCP)**: ${results.fcp}ms\n- **最大内容绘制 (LCP)**: ${results.lcp}ms\n- **首次输入延迟 (FID)**: ${results.fid}ms\n- **累积布局偏移 (CLS)**: ${results.cls}\n\n${results.passed ? '✅ 所有性能指标达标' : '⚠️ 部分指标未达标,请关注'}\n`;\n\n// 创建 Issue 或发送通知\n// ...\n"
timeout-minutes: "30"
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: juhi_test
POSTGRES_PASSWORD: test_password
POSTGRES_USER: test_user
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
...
|
performance-benchmarks
|
null
|
["ubuntu-latest"]
|
20232
|
4
|
1777222622
|
1777222622
|
1777219009
|
1777222622
|
|
0
|
|
0
|
Edit
Delete
|
|
23285
|
17736
|
6
|
5
|
bd8162cf2c08d175a1cab0f69d42b648bb825c10
|
0
|
visual-regression
|
1
|
name: E2E Tests
"on":
# PR validation name: E2E Tests
"on":
# PR validation - 运行关键测试
pull_request:
branches: [main, develop]
paths:
- 'frontend/**'
- 'backend/**'
- 'e2e/**'
- 'package.json'
- 'pnpm-lock.yaml'
# Push to main - 运行完整测试套件
push:
branches: [main]
# 每日定时全量测试 (UTC 时间 00:00 = 北京时间 08:00)
schedule:
- cron: '0 0 * * *'
# 手动触发
workflow_dispatch:
inputs:
test_suite:
description: 'Test suite to run'
required: true
default: 'all'
type: choice
options:
- all
- critical
- business-flows
- visual-regression
- performance
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
visual-regression:
name: visual-regression
runs-on: ubuntu-latest
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.test_suite == 'visual-regression')
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: "0"
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup test database
run: |
cd backend
npx prisma migrate deploy
npx prisma db seed
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
- name: Build and start services
run: |
cd backend && npm run build && npm run start &
cd frontend && npm run build && npm run preview &
sleep 15
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
REDIS_URL: redis://localhost:6379
NODE_ENV: test
VITE_API_URL: http://localhost:3000
- name: Install Playwright
run: npx playwright install --with-deps chromium
- name: Run visual regression tests
run: |
cd e2e
npx playwright test tests/visual-regression/ --reporter=html
env:
E2E_BASE_URL: http://localhost:5173
E2E_TEST_USER: admin@juhi.com
E2E_TEST_PASSWORD: Admin@123
- if: always()
name: Upload visual diff results
uses: actions/upload-artifact@v4
with:
name: visual-regression-report
path: |
e2e/playwright-report/
e2e/test-results/
retention-days: "30"
- if: github.event_name == 'push' && github.ref == 'refs/heads/main'
name: Update baseline screenshots
run: |
cd e2e
npx playwright test tests/visual-regression/ --update-snapshots
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git add tests/visual-regression/**/*.png
git commit -m "chore: update visual regression baselines" || echo "No changes"
git push
timeout-minutes: "30"
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: juhi_test
POSTGRES_PASSWORD: test_password
POSTGRES_USER: test_user
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
...
|
visual-regression
|
null
|
["ubuntu-latest"]
|
20233
|
2
|
1777222624
|
1777222654
|
1777219009
|
1777222654
|
|
0
|
|
0
|
Edit
Delete
|
|
23287
|
17737
|
6
|
5
|
bd8162cf2c08d175a1cab0f69d42b648bb825c10
|
0
|
部署门禁
|
0
|
name: CI/CD Deploy
"on":
# test-pipeli name: CI/CD Deploy
"on":
# test-pipeline 通过后自动触发(仅 main 分支)
workflow_run:
workflows: ["Test Pipeline"]
types: [completed]
branches: [main]
# 版本标签触发完整部署
push:
tags: ['v*']
# 手动触发
workflow_dispatch:
inputs:
environment:
description: '部署环境'
required: true
default: 'staging'
type: choice
options:
- staging
- production
- aliyun
- rollback-production
- rollback-aliyun
skip_tests:
description: '跳过测试(紧急修复)'
required: false
default: false
type: boolean
version:
description: '部署版本号(留空使用自动版本)'
required: false
type: string
env:
IMAGE_PREFIX: ${{ github.repository_owner }}/juhi
NODE_VERSION: "20"
PNPM_VERSION: "8"
REGISTRY: ghcr.io
jobs:
gate:
name: 部署门禁
runs-on: ubuntu-latest
steps:
- id: check
name: 检查部署条件
run: |
SHOULD_DEPLOY="false"
IS_ROLLBACK="false"
TARGET_ENV="staging"
# 回滚请求
if [[ "${{ github.event.inputs.environment }}" == rollback-* ]]; then
IS_ROLLBACK="true"
TARGET_ENV="${{ github.event.inputs.environment }}"
SHOULD_DEPLOY="true"
# 手动触发
elif [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
TARGET_ENV="${{ github.event.inputs.environment }}"
SHOULD_DEPLOY="true"
# 版本标签
elif [ "${{ github.event_name }}" == "push" ]; then
TARGET_ENV="production"
SHOULD_DEPLOY="true"
# test-pipeline 通过后自动部署 staging
elif [ "${{ github.event_name }}" == "workflow_run" ]; then
if [ "${{ github.event.workflow_run.conclusion }}" == "success" ]; then
TARGET_ENV="staging"
SHOULD_DEPLOY="true"
else
echo "Test Pipeline 未通过,跳过部署"
fi
fi
echo "should_deploy=$SHOULD_DEPLOY" >> $GITHUB_OUTPUT
echo "is_rollback=$IS_ROLLBACK" >> $GITHUB_OUTPUT
echo "target_env=$TARGET_ENV" >> $GITHUB_OUTPUT
echo "## 部署门禁" >> $GITHUB_STEP_SUMMARY
echo "- 触发方式: ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY
echo "- 目标环境: $TARGET_ENV" >> $GITHUB_STEP_SUMMARY
echo "- 允许部署: $SHOULD_DEPLOY" >> $GITHUB_STEP_SUMMARY
echo "- 回滚模式: $IS_ROLLBACK" >> $GITHUB_STEP_SUMMARY
outputs:
is_rollback: ${{ steps.check.outputs.is_rollback }}
should_deploy: ${{ steps.check.outputs.should_deploy }}
target_env: ${{ steps.check.outputs.target_env }}
permissions:
contents: read
...
|
gate
|
null
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1777219592
|
1777219017
|
1777219592
|
|
0
|
|
0
|
Edit
Delete
|
|
23297
|
17738
|
6
|
5
|
bd8162cf2c08d175a1cab0f69d42b648bb825c10
|
0
|
检测代码变更
|
0
|
name: Test Pipeline
"on":
push:
name: Test Pipeline
"on":
push:
branches: [main, develop, 'feature/**', 'claude/**']
pull_request:
branches: [main, develop]
workflow_dispatch:
inputs:
coverage_threshold:
description: '覆盖率阈值 (%)'
required: false
default: '80'
run_ai_tests:
description: '运行 AI 模块测试'
required: false
default: 'true'
type: boolean
run_api_tests:
description: '运行 API 集成测试'
required: false
default: 'true'
type: boolean
run_security_audit:
description: '运行安全审计'
required: false
default: 'true'
type: boolean
env:
COVERAGE_THRESHOLD: ${{ github.event.inputs.coverage_threshold || '80' }}
NODE_VERSION: "20"
PNPM_VERSION: "8"
jobs:
detect-changes:
name: 检测代码变更
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- id: filter
uses: dorny/paths-filter@v3
with:
filters: |
backend:
- 'backend/**'
- 'shared/**'
frontend:
- 'frontend/**'
- 'shared/**'
shared:
- 'shared/**'
ai-modules:
- 'backend/src/modules/ai-agents/**'
- 'backend/src/modules/ai-agent-skills/**'
- 'backend/src/modules/ai-review-queue/**'
- 'backend/src/modules/ai-copilot/**'
- 'backend/src/modules/lead-scoring-ai/**'
- 'backend/src/shared/state-machines/machines/ai-*.ts'
- 'backend/src/shared/state-machines/machines/training-*.ts'
prisma:
- 'backend/prisma/**'
e2e:
- 'e2e/**'
outputs:
ai-modules: ${{ steps.filter.outputs.ai-modules }}
backend: ${{ steps.filter.outputs.backend }}
e2e: ${{ steps.filter.outputs.e2e }}
frontend: ${{ steps.filter.outputs.frontend }}
prisma: ${{ steps.filter.outputs.prisma }}
shared: ${{ steps.filter.outputs.shared }}
...
|
detect-changes
|
null
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1777219587
|
1777219018
|
1777219587
|
|
0
|
|
0
|
Edit
Delete
|
|
23298
|
17738
|
6
|
5
|
bd8162cf2c08d175a1cab0f69d42b648bb825c10
|
0
|
安装依赖
|
0
|
name: Test Pipeline
"on":
push:
name: Test Pipeline
"on":
push:
branches: [main, develop, 'feature/**', 'claude/**']
pull_request:
branches: [main, develop]
workflow_dispatch:
inputs:
coverage_threshold:
description: '覆盖率阈值 (%)'
required: false
default: '80'
run_ai_tests:
description: '运行 AI 模块测试'
required: false
default: 'true'
type: boolean
run_api_tests:
description: '运行 API 集成测试'
required: false
default: 'true'
type: boolean
run_security_audit:
description: '运行安全审计'
required: false
default: 'true'
type: boolean
env:
COVERAGE_THRESHOLD: ${{ github.event.inputs.coverage_threshold || '80' }}
NODE_VERSION: "20"
PNPM_VERSION: "8"
jobs:
setup:
name: 安装依赖
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: 安装 pnpm
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- name: 设置 Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- name: 安装依赖
run: pnpm install --frozen-lockfile
- name: 构建共享包
run: pnpm --filter @juhi/shared run build
- name: 缓存依赖
uses: actions/cache/save@v4
with:
key: pipeline-deps-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
path: |
node_modules
backend/node_modules
frontend/node_modules
shared/node_modules
shared/dist
e2e/node_modules
...
|
setup
|
null
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1777219587
|
1777219018
|
1777219587
|
|
0
|
|
0
|
Edit
Delete
|
|
23319
|
17739
|
6
|
5
|
bd8162cf2c08d175a1cab0f69d42b648bb825c10
|
0
|
生产环境健康检查
|
1
|
name: Health Check
"on":
schedule:
name: Health Check
"on":
schedule:
# 每 5 分钟检查一次
- cron: '*/5 * * * *'
workflow_dispatch:
jobs:
health-check:
name: 生产环境健康检查
runs-on: ubuntu-latest
if: github.repository == 'your-org/juhi' # 替换为实际仓库
steps:
- id: api-health
name: API 健康检查
run: |
RESPONSE=$(curl -sf https://juhi.example.com/health || echo '{"status":"error"}')
echo "response=$RESPONSE" >> $GITHUB_OUTPUT
STATUS=$(echo $RESPONSE | jq -r '.status // "error"')
if [ "$STATUS" != "ok" ]; then
echo "API 健康检查失败"
exit 1
fi
echo "API 健康检查通过"
- name: 前端可访问性检查
run: |
HTTP_STATUS=$(curl -so /dev/null -w "%{http_code}" https://juhi.example.com/)
if [ "$HTTP_STATUS" != "200" ]; then
echo "前端返回 HTTP $HTTP_STATUS"
exit 1
fi
echo "前端可访问性检查通过"
- name: SSL 证书检查
run: |
EXPIRY_DATE=$(echo | openssl s_client -servername juhi.example.com -connect juhi.example.com:443 2>/dev/null | openssl x509 -noout -enddate | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$EXPIRY_DATE" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( ($EXPIRY_EPOCH - $NOW_EPOCH) / 86400 ))
echo "SSL 证书剩余 $DAYS_LEFT 天"
if [ $DAYS_LEFT -lt 7 ]; then
echo "::warning::SSL 证书将在 $DAYS_LEFT 天后过期!"
fi
if [ $DAYS_LEFT -lt 0 ]; then
echo "SSL 证书已过期"
exit 1
fi
- name: 响应时间检查
run: |
RESPONSE_TIME=$(curl -so /dev/null -w "%{time_total}" https://juhi.example.com/health)
echo "API 响应时间: ${RESPONSE_TIME}s"
# 响应时间超过 5 秒告警
if (( $(echo "$RESPONSE_TIME > 5.0" | bc -l) )); then
echo "::warning::API 响应时间过长: ${RESPONSE_TIME}s"
fi
- if: failure()
name: Slack 通知(失败时)
uses: 8398a7/action-slack@v3
with:
fields: repo,message,commit,author,action,eventName,workflow
status: ${{ job.status }}
text: "\U0001F6A8 生产环境健康检查失败!请立即检查。"
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
...
|
health-check
|
null
|
["ubuntu-latest"]
|
20234
|
4
|
1777222656
|
1777222656
|
1777219267
|
1777222656
|
|
0
|
|
0
|
Edit
Delete
|
|
23320
|
17740
|
6
|
5
|
bd8162cf2c08d175a1cab0f69d42b648bb825c10
|
0
|
生产环境健康检查
|
1
|
name: Health Check
"on":
schedule:
name: Health Check
"on":
schedule:
# 每 5 分钟检查一次
- cron: '*/5 * * * *'
workflow_dispatch:
jobs:
health-check:
name: 生产环境健康检查
runs-on: ubuntu-latest
if: github.repository == 'your-org/juhi' # 替换为实际仓库
steps:
- id: api-health
name: API 健康检查
run: |
RESPONSE=$(curl -sf https://juhi.example.com/health || echo '{"status":"error"}')
echo "response=$RESPONSE" >> $GITHUB_OUTPUT
STATUS=$(echo $RESPONSE | jq -r '.status // "error"')
if [ "$STATUS" != "ok" ]; then
echo "API 健康检查失败"
exit 1
fi
echo "API 健康检查通过"
- name: 前端可访问性检查
run: |
HTTP_STATUS=$(curl -so /dev/null -w "%{http_code}" https://juhi.example.com/)
if [ "$HTTP_STATUS" != "200" ]; then
echo "前端返回 HTTP $HTTP_STATUS"
exit 1
fi
echo "前端可访问性检查通过"
- name: SSL 证书检查
run: |
EXPIRY_DATE=$(echo | openssl s_client -servername juhi.example.com -connect juhi.example.com:443 2>/dev/null | openssl x509 -noout -enddate | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$EXPIRY_DATE" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( ($EXPIRY_EPOCH - $NOW_EPOCH) / 86400 ))
echo "SSL 证书剩余 $DAYS_LEFT 天"
if [ $DAYS_LEFT -lt 7 ]; then
echo "::warning::SSL 证书将在 $DAYS_LEFT 天后过期!"
fi
if [ $DAYS_LEFT -lt 0 ]; then
echo "SSL 证书已过期"
exit 1
fi
- name: 响应时间检查
run: |
RESPONSE_TIME=$(curl -so /dev/null -w "%{time_total}" https://juhi.example.com/health)
echo "API 响应时间: ${RESPONSE_TIME}s"
# 响应时间超过 5 秒告警
if (( $(echo "$RESPONSE_TIME > 5.0" | bc -l) )); then
echo "::warning::API 响应时间过长: ${RESPONSE_TIME}s"
fi
- if: failure()
name: Slack 通知(失败时)
uses: 8398a7/action-slack@v3
with:
fields: repo,message,commit,author,action,eventName,workflow
status: ${{ job.status }}
text: "\U0001F6A8 生产环境健康检查失败!请立即检查。"
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
...
|
health-check
|
null
|
["ubuntu-latest"]
|
20235
|
4
|
1777222658
|
1777222658
|
1777219553
|
1777222658
|
|
0
|
|
0
|
Edit
Delete
|
|
23321
|
17741
|
6
|
5
|
6f30ee7954e6142227f44fbcb36106e5ce6018eb
|
0
|
Frontend Type Check
|
0
|
name: CI
"on":
push:
branches: name: CI
"on":
push:
branches: [main]
pull_request:
branches: [main]
env:
NODE_VERSION: "20"
PNPM_VERSION: "9"
jobs:
frontend-typecheck:
name: Frontend Type Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- run: pnpm install --frozen-lockfile
- name: Build shared package
run: pnpm -C shared run build
- name: 前端类型检查(vue-tsc --noEmit)
run: pnpm --filter ./frontend run type-check
permissions:
contents: read
...
|
frontend-typecheck
|
null
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1777219989
|
1777219586
|
1777219989
|
|
0
|
|
0
|
Edit
Delete
|
|
23322
|
17741
|
6
|
5
|
6f30ee7954e6142227f44fbcb36106e5ce6018eb
|
0
|
Lint & Type Check
|
0
|
name: CI
"on":
push:
branches: name: CI
"on":
push:
branches: [main]
pull_request:
branches: [main]
env:
NODE_VERSION: "20"
PNPM_VERSION: "9"
jobs:
lint-and-typecheck:
name: Lint & Type Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- run: pnpm install --frozen-lockfile
- name: Generate Prisma Client
run: pnpm -C backend exec prisma generate
- name: Build shared package
run: pnpm -C shared run build
- name: Backend type check
run: pnpm -C backend run type-check
- name: Lint
run: pnpm -C backend run lint || true
permissions:
contents: read
...
|
lint-and-typecheck
|
null
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1777219990
|
1777219586
|
1777219990
|
|
0
|
|
0
|
Edit
Delete
|
|
23325
|
17742
|
6
|
5
|
6f30ee7954e6142227f44fbcb36106e5ce6018eb
|
0
|
pr-validation
|
1
|
name: E2E Tests
"on":
# PR validation name: E2E Tests
"on":
# PR validation - 运行关键测试
pull_request:
branches: [main, develop]
paths:
- 'frontend/**'
- 'backend/**'
- 'e2e/**'
- 'package.json'
- 'pnpm-lock.yaml'
# Push to main - 运行完整测试套件
push:
branches: [main]
# 每日定时全量测试 (UTC 时间 00:00 = 北京时间 08:00)
schedule:
- cron: '0 0 * * *'
# 手动触发
workflow_dispatch:
inputs:
test_suite:
description: 'Test suite to run'
required: true
default: 'all'
type: choice
options:
- all
- critical
- business-flows
- visual-regression
- performance
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
pr-validation:
name: pr-validation
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup test database
run: |
cd backend
npx prisma migrate deploy
npx prisma db seed
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
- name: Build backend
run: |
cd backend
npm run build
- name: Build frontend
run: |
cd frontend
npm run build
- name: Install Playwright Browsers
run: npx playwright install --with-deps chromium
- name: Start backend server
run: |
cd backend
npm run start &
sleep 10
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
REDIS_URL: redis://localhost:6379
NODE_ENV: test
- name: Start frontend server
run: |
cd frontend
npm run preview &
sleep 5
env:
VITE_API_URL: http://localhost:3000
- name: Run critical E2E tests
run: |
cd e2e
npx playwright test \
tests/auth/login.spec.ts \
tests/navigation/full-menu-click.spec.ts \
tests/multi-tenant/data-isolation.spec.ts \
tests/form-interactions/ \
--reporter=html
env:
E2E_BASE_URL: http://localhost:5173
E2E_TEST_USER: admin@juhi.com
E2E_TEST_PASSWORD: Admin@123
- if: always()
name: Upload test results
uses: actions/upload-artifact@v4
with:
name: playwright-report-pr
path: e2e/playwright-report/
retention-days: "7"
- if: always() && github.event_name == 'pull_request'
name: Comment PR with test results
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const reportPath = 'e2e/playwright-report/index.html';
const testsPassed = !fs.existsSync('e2e/test-results/');
const comment = testsPassed
? '✅ E2E 测试通过!'
: '❌ E2E 测试失败,请查看报告';
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: comment
});
timeout-minutes: "30"
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: juhi_test
POSTGRES_PASSWORD: test_password
POSTGRES_USER: test_user
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
...
|
pr-validation
|
null
|
["ubuntu-latest"]
|
20236
|
4
|
1777222660
|
1777222660
|
1777219586
|
1777222660
|
|
0
|
|
0
|
Edit
Delete
|
|
23326
|
17742
|
6
|
5
|
6f30ee7954e6142227f44fbcb36106e5ce6018eb
|
0
|
full-test-suite (chromium)
|
1
|
name: E2E Tests
"on":
# PR validation name: E2E Tests
"on":
# PR validation - 运行关键测试
pull_request:
branches: [main, develop]
paths:
- 'frontend/**'
- 'backend/**'
- 'e2e/**'
- 'package.json'
- 'pnpm-lock.yaml'
# Push to main - 运行完整测试套件
push:
branches: [main]
# 每日定时全量测试 (UTC 时间 00:00 = 北京时间 08:00)
schedule:
- cron: '0 0 * * *'
# 手动触发
workflow_dispatch:
inputs:
test_suite:
description: 'Test suite to run'
required: true
default: 'all'
type: choice
options:
- all
- critical
- business-flows
- visual-regression
- performance
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
full-test-suite:
name: full-test-suite (chromium)
runs-on: ubuntu-latest
if: github.event_name == 'push' || github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.test_suite == 'all')
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup test database
run: |
cd backend
npx prisma migrate deploy
npx prisma db seed
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
- name: Build backend
run: |
cd backend
npm run build
- name: Build frontend
run: |
cd frontend
npm run build
- name: Install Playwright Browsers
run: npx playwright install --with-deps ${{ matrix.browser }}
- name: Start backend server
run: |
cd backend
npm run start &
sleep 10
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
REDIS_URL: redis://localhost:6379
NODE_ENV: test
- name: Start frontend server
run: |
cd frontend
npm run preview &
sleep 5
env:
VITE_API_URL: http://localhost:3000
- name: Run all E2E tests
run: |
cd e2e
npx playwright test --project=${{ matrix.browser }} --reporter=html,json
env:
E2E_BASE_URL: http://localhost:5173
E2E_TEST_USER: admin@juhi.com
E2E_TEST_PASSWORD: Admin@123
- if: always()
name: Upload test results
uses: actions/upload-artifact@v4
with:
name: playwright-report-${{ matrix.browser }}
path: e2e/playwright-report/
retention-days: "30"
- if: always()
name: Upload test artifacts
uses: actions/upload-artifact@v4
with:
name: test-results-${{ matrix.browser }}
path: e2e/test-results/
retention-days: "30"
timeout-minutes: "60"
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: juhi_test
POSTGRES_PASSWORD: test_password
POSTGRES_USER: test_user
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
strategy:
fail-fast: "false"
matrix:
browser:
- chromium
...
|
full-test-suite
|
null
|
["ubuntu-latest"]
|
20237
|
2
|
1777222662
|
1777223071
|
1777219586
|
1777223071
|
|
0
|
|
0
|
Edit
Delete
|
|
23327
|
17742
|
6
|
5
|
6f30ee7954e6142227f44fbcb36106e5ce6018eb
|
0
|
full-test-suite (firefox)
|
1
|
name: E2E Tests
"on":
# PR validation name: E2E Tests
"on":
# PR validation - 运行关键测试
pull_request:
branches: [main, develop]
paths:
- 'frontend/**'
- 'backend/**'
- 'e2e/**'
- 'package.json'
- 'pnpm-lock.yaml'
# Push to main - 运行完整测试套件
push:
branches: [main]
# 每日定时全量测试 (UTC 时间 00:00 = 北京时间 08:00)
schedule:
- cron: '0 0 * * *'
# 手动触发
workflow_dispatch:
inputs:
test_suite:
description: 'Test suite to run'
required: true
default: 'all'
type: choice
options:
- all
- critical
- business-flows
- visual-regression
- performance
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
full-test-suite:
name: full-test-suite (firefox)
runs-on: ubuntu-latest
if: github.event_name == 'push' || github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.test_suite == 'all')
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup test database
run: |
cd backend
npx prisma migrate deploy
npx prisma db seed
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
- name: Build backend
run: |
cd backend
npm run build
- name: Build frontend
run: |
cd frontend
npm run build
- name: Install Playwright Browsers
run: npx playwright install --with-deps ${{ matrix.browser }}
- name: Start backend server
run: |
cd backend
npm run start &
sleep 10
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
REDIS_URL: redis://localhost:6379
NODE_ENV: test
- name: Start frontend server
run: |
cd frontend
npm run preview &
sleep 5
env:
VITE_API_URL: http://localhost:3000
- name: Run all E2E tests
run: |
cd e2e
npx playwright test --project=${{ matrix.browser }} --reporter=html,json
env:
E2E_BASE_URL: http://localhost:5173
E2E_TEST_USER: admin@juhi.com
E2E_TEST_PASSWORD: Admin@123
- if: always()
name: Upload test results
uses: actions/upload-artifact@v4
with:
name: playwright-report-${{ matrix.browser }}
path: e2e/playwright-report/
retention-days: "30"
- if: always()
name: Upload test artifacts
uses: actions/upload-artifact@v4
with:
name: test-results-${{ matrix.browser }}
path: e2e/test-results/
retention-days: "30"
timeout-minutes: "60"
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: juhi_test
POSTGRES_PASSWORD: test_password
POSTGRES_USER: test_user
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
strategy:
fail-fast: "false"
matrix:
browser:
- firefox
...
|
full-test-suite
|
null
|
["ubuntu-latest"]
|
20238
|
2
|
1777223071
|
1777223162
|
1777219586
|
1777223162
|
|
0
|
|
0
|
Edit
Delete
|
|
23328
|
17742
|
6
|
5
|
6f30ee7954e6142227f44fbcb36106e5ce6018eb
|
0
|
full-test-suite (webkit)
|
1
|
name: E2E Tests
"on":
# PR validation name: E2E Tests
"on":
# PR validation - 运行关键测试
pull_request:
branches: [main, develop]
paths:
- 'frontend/**'
- 'backend/**'
- 'e2e/**'
- 'package.json'
- 'pnpm-lock.yaml'
# Push to main - 运行完整测试套件
push:
branches: [main]
# 每日定时全量测试 (UTC 时间 00:00 = 北京时间 08:00)
schedule:
- cron: '0 0 * * *'
# 手动触发
workflow_dispatch:
inputs:
test_suite:
description: 'Test suite to run'
required: true
default: 'all'
type: choice
options:
- all
- critical
- business-flows
- visual-regression
- performance
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
full-test-suite:
name: full-test-suite (webkit)
runs-on: ubuntu-latest
if: github.event_name == 'push' || github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.test_suite == 'all')
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup test database
run: |
cd backend
npx prisma migrate deploy
npx prisma db seed
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
- name: Build backend
run: |
cd backend
npm run build
- name: Build frontend
run: |
cd frontend
npm run build
- name: Install Playwright Browsers
run: npx playwright install --with-deps ${{ matrix.browser }}
- name: Start backend server
run: |
cd backend
npm run start &
sleep 10
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
REDIS_URL: redis://localhost:6379
NODE_ENV: test
- name: Start frontend server
run: |
cd frontend
npm run preview &
sleep 5
env:
VITE_API_URL: http://localhost:3000
- name: Run all E2E tests
run: |
cd e2e
npx playwright test --project=${{ matrix.browser }} --reporter=html,json
env:
E2E_BASE_URL: http://localhost:5173
E2E_TEST_USER: admin@juhi.com
E2E_TEST_PASSWORD: Admin@123
- if: always()
name: Upload test results
uses: actions/upload-artifact@v4
with:
name: playwright-report-${{ matrix.browser }}
path: e2e/playwright-report/
retention-days: "30"
- if: always()
name: Upload test artifacts
uses: actions/upload-artifact@v4
with:
name: test-results-${{ matrix.browser }}
path: e2e/test-results/
retention-days: "30"
timeout-minutes: "60"
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: juhi_test
POSTGRES_PASSWORD: test_password
POSTGRES_USER: test_user
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
strategy:
fail-fast: "false"
matrix:
browser:
- webkit
...
|
full-test-suite
|
null
|
["ubuntu-latest"]
|
20239
|
2
|
1777223162
|
1777223571
|
1777219586
|
1777223571
|
|
0
|
|
0
|
Edit
Delete
|
|
23329
|
17742
|
6
|
5
|
6f30ee7954e6142227f44fbcb36106e5ce6018eb
|
0
|
business-flows
|
1
|
name: E2E Tests
"on":
# PR validation name: E2E Tests
"on":
# PR validation - 运行关键测试
pull_request:
branches: [main, develop]
paths:
- 'frontend/**'
- 'backend/**'
- 'e2e/**'
- 'package.json'
- 'pnpm-lock.yaml'
# Push to main - 运行完整测试套件
push:
branches: [main]
# 每日定时全量测试 (UTC 时间 00:00 = 北京时间 08:00)
schedule:
- cron: '0 0 * * *'
# 手动触发
workflow_dispatch:
inputs:
test_suite:
description: 'Test suite to run'
required: true
default: 'all'
type: choice
options:
- all
- critical
- business-flows
- visual-regression
- performance
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
business-flows:
name: business-flows
runs-on: ubuntu-latest
if: github.event_name == 'workflow_dispatch' && github.event.inputs.test_suite == 'business-flows'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup test database
run: |
cd backend
npx prisma migrate deploy
npx prisma db seed
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
- name: Build and start services
run: |
cd backend && npm run build && npm run start &
cd frontend && npm run build && npm run preview &
sleep 15
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
REDIS_URL: redis://localhost:6379
NODE_ENV: test
VITE_API_URL: http://localhost:3000
- name: Install Playwright
run: npx playwright install --with-deps chromium
- name: Run business flow tests
run: |
cd e2e
npx playwright test tests/business-flows/ --reporter=html,json
env:
E2E_BASE_URL: http://localhost:5173
E2E_TEST_USER: admin@juhi.com
E2E_TEST_PASSWORD: Admin@123
- if: always()
name: Upload test results
uses: actions/upload-artifact@v4
with:
name: business-flows-report
path: e2e/playwright-report/
retention-days: "30"
timeout-minutes: "45"
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: juhi_test
POSTGRES_PASSWORD: test_password
POSTGRES_USER: test_user
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
...
|
business-flows
|
null
|
["ubuntu-latest"]
|
20240
|
4
|
1777223571
|
1777223571
|
1777219586
|
1777223572
|
|
0
|
|
0
|
Edit
Delete
|
|
23330
|
17742
|
6
|
5
|
6f30ee7954e6142227f44fbcb36106e5ce6018eb
|
0
|
performance-benchmarks
|
1
|
name: E2E Tests
"on":
# PR validation name: E2E Tests
"on":
# PR validation - 运行关键测试
pull_request:
branches: [main, develop]
paths:
- 'frontend/**'
- 'backend/**'
- 'e2e/**'
- 'package.json'
- 'pnpm-lock.yaml'
# Push to main - 运行完整测试套件
push:
branches: [main]
# 每日定时全量测试 (UTC 时间 00:00 = 北京时间 08:00)
schedule:
- cron: '0 0 * * *'
# 手动触发
workflow_dispatch:
inputs:
test_suite:
description: 'Test suite to run'
required: true
default: 'all'
type: choice
options:
- all
- critical
- business-flows
- visual-regression
- performance
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
performance-benchmarks:
name: performance-benchmarks
runs-on: ubuntu-latest
if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.test_suite == 'performance')
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup test database
run: |
cd backend
npx prisma migrate deploy
npx prisma db seed
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
- name: Build and start services
run: |
cd backend && npm run build && npm run start &
cd frontend && npm run build && npm run preview &
sleep 15
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
REDIS_URL: redis://localhost:6379
NODE_ENV: production
VITE_API_URL: http://localhost:3000
- name: Install Playwright
run: npx playwright install --with-deps chromium
- name: Run performance tests
run: |
cd e2e
npx playwright test \
tests/flows/complete-sales-flow.spec.ts \
tests/lead-to-cash.spec.ts \
--reporter=html,json
env:
E2E_BASE_URL: http://localhost:5173
E2E_TEST_USER: admin@juhi.com
E2E_TEST_PASSWORD: Admin@123
- name: Generate performance report
run: |
cd e2e
node scripts/generate-performance-report.js
- if: always()
name: Upload performance results
uses: actions/upload-artifact@v4
with:
name: performance-report
path: |
e2e/playwright-report/
e2e/performance-results.json
retention-days: "90"
- if: github.event_name == 'schedule'
name: Comment with performance results
uses: actions/github-script@v7
with:
script: "const fs = require('fs');\nconst results = JSON.parse(fs.readFileSync('e2e/performance-results.json', 'utf8'));\n\nconst comment = `\n## \U0001F4CA 每日性能基准测试报告\n\n- **页面加载时间**: ${results.pageLoadTime}ms\n- **首次内容绘制 (FCP)**: ${results.fcp}ms\n- **最大内容绘制 (LCP)**: ${results.lcp}ms\n- **首次输入延迟 (FID)**: ${results.fid}ms\n- **累积布局偏移 (CLS)**: ${results.cls}\n\n${results.passed ? '✅ 所有性能指标达标' : '⚠️ 部分指标未达标,请关注'}\n`;\n\n// 创建 Issue 或发送通知\n// ...\n"
timeout-minutes: "30"
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: juhi_test
POSTGRES_PASSWORD: test_password
POSTGRES_USER: test_user
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
...
|
performance-benchmarks
|
null
|
["ubuntu-latest"]
|
20241
|
4
|
1777223573
|
1777223573
|
1777219586
|
1777223574
|
|
0
|
|
0
|
Edit
Delete
|
|
23331
|
17742
|
6
|
5
|
6f30ee7954e6142227f44fbcb36106e5ce6018eb
|
0
|
visual-regression
|
1
|
name: E2E Tests
"on":
# PR validation name: E2E Tests
"on":
# PR validation - 运行关键测试
pull_request:
branches: [main, develop]
paths:
- 'frontend/**'
- 'backend/**'
- 'e2e/**'
- 'package.json'
- 'pnpm-lock.yaml'
# Push to main - 运行完整测试套件
push:
branches: [main]
# 每日定时全量测试 (UTC 时间 00:00 = 北京时间 08:00)
schedule:
- cron: '0 0 * * *'
# 手动触发
workflow_dispatch:
inputs:
test_suite:
description: 'Test suite to run'
required: true
default: 'all'
type: choice
options:
- all
- critical
- business-flows
- visual-regression
- performance
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
visual-regression:
name: visual-regression
runs-on: ubuntu-latest
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.test_suite == 'visual-regression')
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: "0"
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup test database
run: |
cd backend
npx prisma migrate deploy
npx prisma db seed
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
- name: Build and start services
run: |
cd backend && npm run build && npm run start &
cd frontend && npm run build && npm run preview &
sleep 15
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
REDIS_URL: redis://localhost:6379
NODE_ENV: test
VITE_API_URL: http://localhost:3000
- name: Install Playwright
run: npx playwright install --with-deps chromium
- name: Run visual regression tests
run: |
cd e2e
npx playwright test tests/visual-regression/ --reporter=html
env:
E2E_BASE_URL: http://localhost:5173
E2E_TEST_USER: admin@juhi.com
E2E_TEST_PASSWORD: Admin@123
- if: always()
name: Upload visual diff results
uses: actions/upload-artifact@v4
with:
name: visual-regression-report
path: |
e2e/playwright-report/
e2e/test-results/
retention-days: "30"
- if: github.event_name == 'push' && github.ref == 'refs/heads/main'
name: Update baseline screenshots
run: |
cd e2e
npx playwright test tests/visual-regression/ --update-snapshots
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git add tests/visual-regression/**/*.png
git commit -m "chore: update visual regression baselines" || echo "No changes"
git push
timeout-minutes: "30"
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: juhi_test
POSTGRES_PASSWORD: test_password
POSTGRES_USER: test_user
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
...
|
visual-regression
|
null
|
["ubuntu-latest"]
|
20242
|
2
|
1777223575
|
1777223665
|
1777219586
|
1777223666
|
|
0
|
|
0
|
Edit
Delete
|
|
23333
|
17743
|
6
|
5
|
6f30ee7954e6142227f44fbcb36106e5ce6018eb
|
0
|
部署门禁
|
0
|
name: CI/CD Deploy
"on":
# test-pipeli name: CI/CD Deploy
"on":
# test-pipeline 通过后自动触发(仅 main 分支)
workflow_run:
workflows: ["Test Pipeline"]
types: [completed]
branches: [main]
# 版本标签触发完整部署
push:
tags: ['v*']
# 手动触发
workflow_dispatch:
inputs:
environment:
description: '部署环境'
required: true
default: 'staging'
type: choice
options:
- staging
- production
- aliyun
- rollback-production
- rollback-aliyun
skip_tests:
description: '跳过测试(紧急修复)'
required: false
default: false
type: boolean
version:
description: '部署版本号(留空使用自动版本)'
required: false
type: string
env:
IMAGE_PREFIX: ${{ github.repository_owner }}/juhi
NODE_VERSION: "20"
PNPM_VERSION: "8"
REGISTRY: ghcr.io
jobs:
gate:
name: 部署门禁
runs-on: ubuntu-latest
steps:
- id: check
name: 检查部署条件
run: |
SHOULD_DEPLOY="false"
IS_ROLLBACK="false"
TARGET_ENV="staging"
# 回滚请求
if [[ "${{ github.event.inputs.environment }}" == rollback-* ]]; then
IS_ROLLBACK="true"
TARGET_ENV="${{ github.event.inputs.environment }}"
SHOULD_DEPLOY="true"
# 手动触发
elif [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
TARGET_ENV="${{ github.event.inputs.environment }}"
SHOULD_DEPLOY="true"
# 版本标签
elif [ "${{ github.event_name }}" == "push" ]; then
TARGET_ENV="production"
SHOULD_DEPLOY="true"
# test-pipeline 通过后自动部署 staging
elif [ "${{ github.event_name }}" == "workflow_run" ]; then
if [ "${{ github.event.workflow_run.conclusion }}" == "success" ]; then
TARGET_ENV="staging"
SHOULD_DEPLOY="true"
else
echo "Test Pipeline 未通过,跳过部署"
fi
fi
echo "should_deploy=$SHOULD_DEPLOY" >> $GITHUB_OUTPUT
echo "is_rollback=$IS_ROLLBACK" >> $GITHUB_OUTPUT
echo "target_env=$TARGET_ENV" >> $GITHUB_OUTPUT
echo "## 部署门禁" >> $GITHUB_STEP_SUMMARY
echo "- 触发方式: ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY
echo "- 目标环境: $TARGET_ENV" >> $GITHUB_STEP_SUMMARY
echo "- 允许部署: $SHOULD_DEPLOY" >> $GITHUB_STEP_SUMMARY
echo "- 回滚模式: $IS_ROLLBACK" >> $GITHUB_STEP_SUMMARY
outputs:
is_rollback: ${{ steps.check.outputs.is_rollback }}
should_deploy: ${{ steps.check.outputs.should_deploy }}
target_env: ${{ steps.check.outputs.target_env }}
permissions:
contents: read
...
|
gate
|
null
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1777219997
|
1777219595
|
1777219997
|
|
0
|
|
0
|
Edit
Delete
|
|
23343
|
17744
|
6
|
5
|
6f30ee7954e6142227f44fbcb36106e5ce6018eb
|
0
|
检测代码变更
|
0
|
name: Test Pipeline
"on":
push:
name: Test Pipeline
"on":
push:
branches: [main, develop, 'feature/**', 'claude/**']
pull_request:
branches: [main, develop]
workflow_dispatch:
inputs:
coverage_threshold:
description: '覆盖率阈值 (%)'
required: false
default: '80'
run_ai_tests:
description: '运行 AI 模块测试'
required: false
default: 'true'
type: boolean
run_api_tests:
description: '运行 API 集成测试'
required: false
default: 'true'
type: boolean
run_security_audit:
description: '运行安全审计'
required: false
default: 'true'
type: boolean
env:
COVERAGE_THRESHOLD: ${{ github.event.inputs.coverage_threshold || '80' }}
NODE_VERSION: "20"
PNPM_VERSION: "8"
jobs:
detect-changes:
name: 检测代码变更
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- id: filter
uses: dorny/paths-filter@v3
with:
filters: |
backend:
- 'backend/**'
- 'shared/**'
frontend:
- 'frontend/**'
- 'shared/**'
shared:
- 'shared/**'
ai-modules:
- 'backend/src/modules/ai-agents/**'
- 'backend/src/modules/ai-agent-skills/**'
- 'backend/src/modules/ai-review-queue/**'
- 'backend/src/modules/ai-copilot/**'
- 'backend/src/modules/lead-scoring-ai/**'
- 'backend/src/shared/state-machines/machines/ai-*.ts'
- 'backend/src/shared/state-machines/machines/training-*.ts'
prisma:
- 'backend/prisma/**'
e2e:
- 'e2e/**'
outputs:
ai-modules: ${{ steps.filter.outputs.ai-modules }}
backend: ${{ steps.filter.outputs.backend }}
e2e: ${{ steps.filter.outputs.e2e }}
frontend: ${{ steps.filter.outputs.frontend }}
prisma: ${{ steps.filter.outputs.prisma }}
shared: ${{ steps.filter.outputs.shared }}
...
|
detect-changes
|
null
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1777219992
|
1777219595
|
1777219992
|
|
0
|
|
0
|
Edit
Delete
|
|
23344
|
17744
|
6
|
5
|
6f30ee7954e6142227f44fbcb36106e5ce6018eb
|
0
|
安装依赖
|
0
|
name: Test Pipeline
"on":
push:
name: Test Pipeline
"on":
push:
branches: [main, develop, 'feature/**', 'claude/**']
pull_request:
branches: [main, develop]
workflow_dispatch:
inputs:
coverage_threshold:
description: '覆盖率阈值 (%)'
required: false
default: '80'
run_ai_tests:
description: '运行 AI 模块测试'
required: false
default: 'true'
type: boolean
run_api_tests:
description: '运行 API 集成测试'
required: false
default: 'true'
type: boolean
run_security_audit:
description: '运行安全审计'
required: false
default: 'true'
type: boolean
env:
COVERAGE_THRESHOLD: ${{ github.event.inputs.coverage_threshold || '80' }}
NODE_VERSION: "20"
PNPM_VERSION: "8"
jobs:
setup:
name: 安装依赖
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: 安装 pnpm
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- name: 设置 Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- name: 安装依赖
run: pnpm install --frozen-lockfile
- name: 构建共享包
run: pnpm --filter @juhi/shared run build
- name: 缓存依赖
uses: actions/cache/save@v4
with:
key: pipeline-deps-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
path: |
node_modules
backend/node_modules
frontend/node_modules
shared/node_modules
shared/dist
e2e/node_modules
...
|
setup
|
null
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1777219992
|
1777219595
|
1777219992
|
|
0
|
|
0
|
Edit
Delete
|
|
23365
|
17745
|
6
|
5
|
6f30ee7954e6142227f44fbcb36106e5ce6018eb
|
0
|
生产环境健康检查
|
1
|
name: Health Check
"on":
schedule:
name: Health Check
"on":
schedule:
# 每 5 分钟检查一次
- cron: '*/5 * * * *'
workflow_dispatch:
jobs:
health-check:
name: 生产环境健康检查
runs-on: ubuntu-latest
if: github.repository == 'your-org/juhi' # 替换为实际仓库
steps:
- id: api-health
name: API 健康检查
run: |
RESPONSE=$(curl -sf https://juhi.example.com/health || echo '{"status":"error"}')
echo "response=$RESPONSE" >> $GITHUB_OUTPUT
STATUS=$(echo $RESPONSE | jq -r '.status // "error"')
if [ "$STATUS" != "ok" ]; then
echo "API 健康检查失败"
exit 1
fi
echo "API 健康检查通过"
- name: 前端可访问性检查
run: |
HTTP_STATUS=$(curl -so /dev/null -w "%{http_code}" https://juhi.example.com/)
if [ "$HTTP_STATUS" != "200" ]; then
echo "前端返回 HTTP $HTTP_STATUS"
exit 1
fi
echo "前端可访问性检查通过"
- name: SSL 证书检查
run: |
EXPIRY_DATE=$(echo | openssl s_client -servername juhi.example.com -connect juhi.example.com:443 2>/dev/null | openssl x509 -noout -enddate | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$EXPIRY_DATE" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( ($EXPIRY_EPOCH - $NOW_EPOCH) / 86400 ))
echo "SSL 证书剩余 $DAYS_LEFT 天"
if [ $DAYS_LEFT -lt 7 ]; then
echo "::warning::SSL 证书将在 $DAYS_LEFT 天后过期!"
fi
if [ $DAYS_LEFT -lt 0 ]; then
echo "SSL 证书已过期"
exit 1
fi
- name: 响应时间检查
run: |
RESPONSE_TIME=$(curl -so /dev/null -w "%{time_total}" https://juhi.example.com/health)
echo "API 响应时间: ${RESPONSE_TIME}s"
# 响应时间超过 5 秒告警
if (( $(echo "$RESPONSE_TIME > 5.0" | bc -l) )); then
echo "::warning::API 响应时间过长: ${RESPONSE_TIME}s"
fi
- if: failure()
name: Slack 通知(失败时)
uses: 8398a7/action-slack@v3
with:
fields: repo,message,commit,author,action,eventName,workflow
status: ${{ job.status }}
text: "\U0001F6A8 生产环境健康检查失败!请立即检查。"
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
...
|
health-check
|
null
|
["ubuntu-latest"]
|
20243
|
4
|
1777223667
|
1777223667
|
1777219850
|
1777223668
|
|
0
|
|
0
|
Edit
Delete
|
|
23366
|
17746
|
6
|
5
|
c04c1f61a7ca64acf31ed914678656d87ed6e35f
|
0
|
Frontend Type Check
|
0
|
name: CI
"on":
push:
branches: name: CI
"on":
push:
branches: [main]
pull_request:
branches: [main]
env:
NODE_VERSION: "20"
PNPM_VERSION: "9"
jobs:
frontend-typecheck:
name: Frontend Type Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- run: pnpm install --frozen-lockfile
- name: Build shared package
run: pnpm -C shared run build
- name: 前端类型检查(vue-tsc --noEmit)
run: pnpm --filter ./frontend run type-check
permissions:
contents: read
...
|
frontend-typecheck
|
null
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1777220519
|
1777219991
|
1777220519
|
|
0
|
|
0
|
Edit
Delete
|
|
23367
|
17746
|
6
|
5
|
c04c1f61a7ca64acf31ed914678656d87ed6e35f
|
0
|
Lint & Type Check
|
0
|
name: CI
"on":
push:
branches: name: CI
"on":
push:
branches: [main]
pull_request:
branches: [main]
env:
NODE_VERSION: "20"
PNPM_VERSION: "9"
jobs:
lint-and-typecheck:
name: Lint & Type Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- run: pnpm install --frozen-lockfile
- name: Generate Prisma Client
run: pnpm -C backend exec prisma generate
- name: Build shared package
run: pnpm -C shared run build
- name: Backend type check
run: pnpm -C backend run type-check
- name: Lint
run: pnpm -C backend run lint || true
permissions:
contents: read
...
|
lint-and-typecheck
|
null
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1777220519
|
1777219991
|
1777220519
|
|
0
|
|
0
|
Edit
Delete
|
|
23370
|
17747
|
6
|
5
|
c04c1f61a7ca64acf31ed914678656d87ed6e35f
|
0
|
pr-validation
|
1
|
name: E2E Tests
"on":
# PR validation name: E2E Tests
"on":
# PR validation - 运行关键测试
pull_request:
branches: [main, develop]
paths:
- 'frontend/**'
- 'backend/**'
- 'e2e/**'
- 'package.json'
- 'pnpm-lock.yaml'
# Push to main - 运行完整测试套件
push:
branches: [main]
# 每日定时全量测试 (UTC 时间 00:00 = 北京时间 08:00)
schedule:
- cron: '0 0 * * *'
# 手动触发
workflow_dispatch:
inputs:
test_suite:
description: 'Test suite to run'
required: true
default: 'all'
type: choice
options:
- all
- critical
- business-flows
- visual-regression
- performance
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
pr-validation:
name: pr-validation
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup test database
run: |
cd backend
npx prisma migrate deploy
npx prisma db seed
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
- name: Build backend
run: |
cd backend
npm run build
- name: Build frontend
run: |
cd frontend
npm run build
- name: Install Playwright Browsers
run: npx playwright install --with-deps chromium
- name: Start backend server
run: |
cd backend
npm run start &
sleep 10
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
REDIS_URL: redis://localhost:6379
NODE_ENV: test
- name: Start frontend server
run: |
cd frontend
npm run preview &
sleep 5
env:
VITE_API_URL: http://localhost:3000
- name: Run critical E2E tests
run: |
cd e2e
npx playwright test \
tests/auth/login.spec.ts \
tests/navigation/full-menu-click.spec.ts \
tests/multi-tenant/data-isolation.spec.ts \
tests/form-interactions/ \
--reporter=html
env:
E2E_BASE_URL: http://localhost:5173
E2E_TEST_USER: admin@juhi.com
E2E_TEST_PASSWORD: Admin@123
- if: always()
name: Upload test results
uses: actions/upload-artifact@v4
with:
name: playwright-report-pr
path: e2e/playwright-report/
retention-days: "7"
- if: always() && github.event_name == 'pull_request'
name: Comment PR with test results
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const reportPath = 'e2e/playwright-report/index.html';
const testsPassed = !fs.existsSync('e2e/test-results/');
const comment = testsPassed
? '✅ E2E 测试通过!'
: '❌ E2E 测试失败,请查看报告';
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: comment
});
timeout-minutes: "30"
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: juhi_test
POSTGRES_PASSWORD: test_password
POSTGRES_USER: test_user
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
...
|
pr-validation
|
null
|
["ubuntu-latest"]
|
20244
|
4
|
1777223669
|
1777223669
|
1777219991
|
1777223669
|
|
0
|
|
0
|
Edit
Delete
|
|
23371
|
17747
|
6
|
5
|
c04c1f61a7ca64acf31ed914678656d87ed6e35f
|
0
|
full-test-suite (chromium)
|
1
|
name: E2E Tests
"on":
# PR validation name: E2E Tests
"on":
# PR validation - 运行关键测试
pull_request:
branches: [main, develop]
paths:
- 'frontend/**'
- 'backend/**'
- 'e2e/**'
- 'package.json'
- 'pnpm-lock.yaml'
# Push to main - 运行完整测试套件
push:
branches: [main]
# 每日定时全量测试 (UTC 时间 00:00 = 北京时间 08:00)
schedule:
- cron: '0 0 * * *'
# 手动触发
workflow_dispatch:
inputs:
test_suite:
description: 'Test suite to run'
required: true
default: 'all'
type: choice
options:
- all
- critical
- business-flows
- visual-regression
- performance
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
full-test-suite:
name: full-test-suite (chromium)
runs-on: ubuntu-latest
if: github.event_name == 'push' || github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.test_suite == 'all')
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup test database
run: |
cd backend
npx prisma migrate deploy
npx prisma db seed
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
- name: Build backend
run: |
cd backend
npm run build
- name: Build frontend
run: |
cd frontend
npm run build
- name: Install Playwright Browsers
run: npx playwright install --with-deps ${{ matrix.browser }}
- name: Start backend server
run: |
cd backend
npm run start &
sleep 10
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
REDIS_URL: redis://localhost:6379
NODE_ENV: test
- name: Start frontend server
run: |
cd frontend
npm run preview &
sleep 5
env:
VITE_API_URL: http://localhost:3000
- name: Run all E2E tests
run: |
cd e2e
npx playwright test --project=${{ matrix.browser }} --reporter=html,json
env:
E2E_BASE_URL: http://localhost:5173
E2E_TEST_USER: admin@juhi.com
E2E_TEST_PASSWORD: Admin@123
- if: always()
name: Upload test results
uses: actions/upload-artifact@v4
with:
name: playwright-report-${{ matrix.browser }}
path: e2e/playwright-report/
retention-days: "30"
- if: always()
name: Upload test artifacts
uses: actions/upload-artifact@v4
with:
name: test-results-${{ matrix.browser }}
path: e2e/test-results/
retention-days: "30"
timeout-minutes: "60"
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: juhi_test
POSTGRES_PASSWORD: test_password
POSTGRES_USER: test_user
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
strategy:
fail-fast: "false"
matrix:
browser:
- chromium
...
|
full-test-suite
|
null
|
["ubuntu-latest"]
|
20245
|
2
|
1777223671
|
1777223701
|
1777219991
|
1777223701
|
|
0
|
|
0
|
Edit
Delete
|
|
23372
|
17747
|
6
|
5
|
c04c1f61a7ca64acf31ed914678656d87ed6e35f
|
0
|
full-test-suite (firefox)
|
1
|
name: E2E Tests
"on":
# PR validation name: E2E Tests
"on":
# PR validation - 运行关键测试
pull_request:
branches: [main, develop]
paths:
- 'frontend/**'
- 'backend/**'
- 'e2e/**'
- 'package.json'
- 'pnpm-lock.yaml'
# Push to main - 运行完整测试套件
push:
branches: [main]
# 每日定时全量测试 (UTC 时间 00:00 = 北京时间 08:00)
schedule:
- cron: '0 0 * * *'
# 手动触发
workflow_dispatch:
inputs:
test_suite:
description: 'Test suite to run'
required: true
default: 'all'
type: choice
options:
- all
- critical
- business-flows
- visual-regression
- performance
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
full-test-suite:
name: full-test-suite (firefox)
runs-on: ubuntu-latest
if: github.event_name == 'push' || github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.test_suite == 'all')
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup test database
run: |
cd backend
npx prisma migrate deploy
npx prisma db seed
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
- name: Build backend
run: |
cd backend
npm run build
- name: Build frontend
run: |
cd frontend
npm run build
- name: Install Playwright Browsers
run: npx playwright install --with-deps ${{ matrix.browser }}
- name: Start backend server
run: |
cd backend
npm run start &
sleep 10
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
REDIS_URL: redis://localhost:6379
NODE_ENV: test
- name: Start frontend server
run: |
cd frontend
npm run preview &
sleep 5
env:
VITE_API_URL: http://localhost:3000
- name: Run all E2E tests
run: |
cd e2e
npx playwright test --project=${{ matrix.browser }} --reporter=html,json
env:
E2E_BASE_URL: http://localhost:5173
E2E_TEST_USER: admin@juhi.com
E2E_TEST_PASSWORD: Admin@123
- if: always()
name: Upload test results
uses: actions/upload-artifact@v4
with:
name: playwright-report-${{ matrix.browser }}
path: e2e/playwright-report/
retention-days: "30"
- if: always()
name: Upload test artifacts
uses: actions/upload-artifact@v4
with:
name: test-results-${{ matrix.browser }}
path: e2e/test-results/
retention-days: "30"
timeout-minutes: "60"
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: juhi_test
POSTGRES_PASSWORD: test_password
POSTGRES_USER: test_user
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
strategy:
fail-fast: "false"
matrix:
browser:
- firefox
...
|
full-test-suite
|
null
|
["ubuntu-latest"]
|
20246
|
2
|
1777223702
|
1777223763
|
1777219991
|
1777223763
|
|
0
|
|
0
|
Edit
Delete
|
|
23373
|
17747
|
6
|
5
|
c04c1f61a7ca64acf31ed914678656d87ed6e35f
|
0
|
full-test-suite (webkit)
|
1
|
name: E2E Tests
"on":
# PR validation name: E2E Tests
"on":
# PR validation - 运行关键测试
pull_request:
branches: [main, develop]
paths:
- 'frontend/**'
- 'backend/**'
- 'e2e/**'
- 'package.json'
- 'pnpm-lock.yaml'
# Push to main - 运行完整测试套件
push:
branches: [main]
# 每日定时全量测试 (UTC 时间 00:00 = 北京时间 08:00)
schedule:
- cron: '0 0 * * *'
# 手动触发
workflow_dispatch:
inputs:
test_suite:
description: 'Test suite to run'
required: true
default: 'all'
type: choice
options:
- all
- critical
- business-flows
- visual-regression
- performance
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
full-test-suite:
name: full-test-suite (webkit)
runs-on: ubuntu-latest
if: github.event_name == 'push' || github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.test_suite == 'all')
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup test database
run: |
cd backend
npx prisma migrate deploy
npx prisma db seed
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
- name: Build backend
run: |
cd backend
npm run build
- name: Build frontend
run: |
cd frontend
npm run build
- name: Install Playwright Browsers
run: npx playwright install --with-deps ${{ matrix.browser }}
- name: Start backend server
run: |
cd backend
npm run start &
sleep 10
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
REDIS_URL: redis://localhost:6379
NODE_ENV: test
- name: Start frontend server
run: |
cd frontend
npm run preview &
sleep 5
env:
VITE_API_URL: http://localhost:3000
- name: Run all E2E tests
run: |
cd e2e
npx playwright test --project=${{ matrix.browser }} --reporter=html,json
env:
E2E_BASE_URL: http://localhost:5173
E2E_TEST_USER: admin@juhi.com
E2E_TEST_PASSWORD: Admin@123
- if: always()
name: Upload test results
uses: actions/upload-artifact@v4
with:
name: playwright-report-${{ matrix.browser }}
path: e2e/playwright-report/
retention-days: "30"
- if: always()
name: Upload test artifacts
uses: actions/upload-artifact@v4
with:
name: test-results-${{ matrix.browser }}
path: e2e/test-results/
retention-days: "30"
timeout-minutes: "60"
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: juhi_test
POSTGRES_PASSWORD: test_password
POSTGRES_USER: test_user
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
strategy:
fail-fast: "false"
matrix:
browser:
- webkit
...
|
full-test-suite
|
null
|
["ubuntu-latest"]
|
20247
|
2
|
1777223763
|
1777223793
|
1777219991
|
1777223794
|
|
0
|
|
0
|
Edit
Delete
|
|
23374
|
17747
|
6
|
5
|
c04c1f61a7ca64acf31ed914678656d87ed6e35f
|
0
|
business-flows
|
1
|
name: E2E Tests
"on":
# PR validation name: E2E Tests
"on":
# PR validation - 运行关键测试
pull_request:
branches: [main, develop]
paths:
- 'frontend/**'
- 'backend/**'
- 'e2e/**'
- 'package.json'
- 'pnpm-lock.yaml'
# Push to main - 运行完整测试套件
push:
branches: [main]
# 每日定时全量测试 (UTC 时间 00:00 = 北京时间 08:00)
schedule:
- cron: '0 0 * * *'
# 手动触发
workflow_dispatch:
inputs:
test_suite:
description: 'Test suite to run'
required: true
default: 'all'
type: choice
options:
- all
- critical
- business-flows
- visual-regression
- performance
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
business-flows:
name: business-flows
runs-on: ubuntu-latest
if: github.event_name == 'workflow_dispatch' && github.event.inputs.test_suite == 'business-flows'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup test database
run: |
cd backend
npx prisma migrate deploy
npx prisma db seed
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
- name: Build and start services
run: |
cd backend && npm run build && npm run start &
cd frontend && npm run build && npm run preview &
sleep 15
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
REDIS_URL: redis://localhost:6379
NODE_ENV: test
VITE_API_URL: http://localhost:3000
- name: Install Playwright
run: npx playwright install --with-deps chromium
- name: Run business flow tests
run: |
cd e2e
npx playwright test tests/business-flows/ --reporter=html,json
env:
E2E_BASE_URL: http://localhost:5173
E2E_TEST_USER: admin@juhi.com
E2E_TEST_PASSWORD: Admin@123
- if: always()
name: Upload test results
uses: actions/upload-artifact@v4
with:
name: business-flows-report
path: e2e/playwright-report/
retention-days: "30"
timeout-minutes: "45"
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: juhi_test
POSTGRES_PASSWORD: test_password
POSTGRES_USER: test_user
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
...
|
business-flows
|
null
|
["ubuntu-latest"]
|
20248
|
4
|
1777223794
|
1777223794
|
1777219991
|
1777223794
|
|
0
|
|
0
|
Edit
Delete
|
|
23375
|
17747
|
6
|
5
|
c04c1f61a7ca64acf31ed914678656d87ed6e35f
|
0
|
performance-benchmarks
|
1
|
name: E2E Tests
"on":
# PR validation name: E2E Tests
"on":
# PR validation - 运行关键测试
pull_request:
branches: [main, develop]
paths:
- 'frontend/**'
- 'backend/**'
- 'e2e/**'
- 'package.json'
- 'pnpm-lock.yaml'
# Push to main - 运行完整测试套件
push:
branches: [main]
# 每日定时全量测试 (UTC 时间 00:00 = 北京时间 08:00)
schedule:
- cron: '0 0 * * *'
# 手动触发
workflow_dispatch:
inputs:
test_suite:
description: 'Test suite to run'
required: true
default: 'all'
type: choice
options:
- all
- critical
- business-flows
- visual-regression
- performance
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
performance-benchmarks:
name: performance-benchmarks
runs-on: ubuntu-latest
if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.test_suite == 'performance')
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup test database
run: |
cd backend
npx prisma migrate deploy
npx prisma db seed
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
- name: Build and start services
run: |
cd backend && npm run build && npm run start &
cd frontend && npm run build && npm run preview &
sleep 15
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
REDIS_URL: redis://localhost:6379
NODE_ENV: production
VITE_API_URL: http://localhost:3000
- name: Install Playwright
run: npx playwright install --with-deps chromium
- name: Run performance tests
run: |
cd e2e
npx playwright test \
tests/flows/complete-sales-flow.spec.ts \
tests/lead-to-cash.spec.ts \
--reporter=html,json
env:
E2E_BASE_URL: http://localhost:5173
E2E_TEST_USER: admin@juhi.com
E2E_TEST_PASSWORD: Admin@123
- name: Generate performance report
run: |
cd e2e
node scripts/generate-performance-report.js
- if: always()
name: Upload performance results
uses: actions/upload-artifact@v4
with:
name: performance-report
path: |
e2e/playwright-report/
e2e/performance-results.json
retention-days: "90"
- if: github.event_name == 'schedule'
name: Comment with performance results
uses: actions/github-script@v7
with:
script: "const fs = require('fs');\nconst results = JSON.parse(fs.readFileSync('e2e/performance-results.json', 'utf8'));\n\nconst comment = `\n## \U0001F4CA 每日性能基准测试报告\n\n- **页面加载时间**: ${results.pageLoadTime}ms\n- **首次内容绘制 (FCP)**: ${results.fcp}ms\n- **最大内容绘制 (LCP)**: ${results.lcp}ms\n- **首次输入延迟 (FID)**: ${results.fid}ms\n- **累积布局偏移 (CLS)**: ${results.cls}\n\n${results.passed ? '✅ 所有性能指标达标' : '⚠️ 部分指标未达标,请关注'}\n`;\n\n// 创建 Issue 或发送通知\n// ...\n"
timeout-minutes: "30"
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: juhi_test
POSTGRES_PASSWORD: test_password
POSTGRES_USER: test_user
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
...
|
performance-benchmarks
|
null
|
["ubuntu-latest"]
|
20249
|
4
|
1777223796
|
1777223796
|
1777219991
|
1777223796
|
|
0
|
|
0
|
Edit
Delete
|
|
23376
|
17747
|
6
|
5
|
c04c1f61a7ca64acf31ed914678656d87ed6e35f
|
0
|
visual-regression
|
1
|
name: E2E Tests
"on":
# PR validation name: E2E Tests
"on":
# PR validation - 运行关键测试
pull_request:
branches: [main, develop]
paths:
- 'frontend/**'
- 'backend/**'
- 'e2e/**'
- 'package.json'
- 'pnpm-lock.yaml'
# Push to main - 运行完整测试套件
push:
branches: [main]
# 每日定时全量测试 (UTC 时间 00:00 = 北京时间 08:00)
schedule:
- cron: '0 0 * * *'
# 手动触发
workflow_dispatch:
inputs:
test_suite:
description: 'Test suite to run'
required: true
default: 'all'
type: choice
options:
- all
- critical
- business-flows
- visual-regression
- performance
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
visual-regression:
name: visual-regression
runs-on: ubuntu-latest
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.test_suite == 'visual-regression')
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: "0"
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup test database
run: |
cd backend
npx prisma migrate deploy
npx prisma db seed
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
- name: Build and start services
run: |
cd backend && npm run build && npm run start &
cd frontend && npm run build && npm run preview &
sleep 15
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
REDIS_URL: redis://localhost:6379
NODE_ENV: test
VITE_API_URL: http://localhost:3000
- name: Install Playwright
run: npx playwright install --with-deps chromium
- name: Run visual regression tests
run: |
cd e2e
npx playwright test tests/visual-regression/ --reporter=html
env:
E2E_BASE_URL: http://localhost:5173
E2E_TEST_USER: admin@juhi.com
E2E_TEST_PASSWORD: Admin@123
- if: always()
name: Upload visual diff results
uses: actions/upload-artifact@v4
with:
name: visual-regression-report
path: |
e2e/playwright-report/
e2e/test-results/
retention-days: "30"
- if: github.event_name == 'push' && github.ref == 'refs/heads/main'
name: Update baseline screenshots
run: |
cd e2e
npx playwright test tests/visual-regression/ --update-snapshots
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git add tests/visual-regression/**/*.png
git commit -m "chore: update visual regression baselines" || echo "No changes"
git push
timeout-minutes: "30"
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: juhi_test
POSTGRES_PASSWORD: test_password
POSTGRES_USER: test_user
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
...
|
visual-regression
|
null
|
["ubuntu-latest"]
|
20250
|
2
|
1777223798
|
1777223828
|
1777219991
|
1777223828
|
|
0
|
|
0
|
Edit
Delete
|
|
23378
|
17748
|
6
|
5
|
c04c1f61a7ca64acf31ed914678656d87ed6e35f
|
0
|
部署门禁
|
0
|
name: CI/CD Deploy
"on":
# test-pipeli name: CI/CD Deploy
"on":
# test-pipeline 通过后自动触发(仅 main 分支)
workflow_run:
workflows: ["Test Pipeline"]
types: [completed]
branches: [main]
# 版本标签触发完整部署
push:
tags: ['v*']
# 手动触发
workflow_dispatch:
inputs:
environment:
description: '部署环境'
required: true
default: 'staging'
type: choice
options:
- staging
- production
- aliyun
- rollback-production
- rollback-aliyun
skip_tests:
description: '跳过测试(紧急修复)'
required: false
default: false
type: boolean
version:
description: '部署版本号(留空使用自动版本)'
required: false
type: string
env:
IMAGE_PREFIX: ${{ github.repository_owner }}/juhi
NODE_VERSION: "20"
PNPM_VERSION: "8"
REGISTRY: ghcr.io
jobs:
gate:
name: 部署门禁
runs-on: ubuntu-latest
steps:
- id: check
name: 检查部署条件
run: |
SHOULD_DEPLOY="false"
IS_ROLLBACK="false"
TARGET_ENV="staging"
# 回滚请求
if [[ "${{ github.event.inputs.environment }}" == rollback-* ]]; then
IS_ROLLBACK="true"
TARGET_ENV="${{ github.event.inputs.environment }}"
SHOULD_DEPLOY="true"
# 手动触发
elif [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
TARGET_ENV="${{ github.event.inputs.environment }}"
SHOULD_DEPLOY="true"
# 版本标签
elif [ "${{ github.event_name }}" == "push" ]; then
TARGET_ENV="production"
SHOULD_DEPLOY="true"
# test-pipeline 通过后自动部署 staging
elif [ "${{ github.event_name }}" == "workflow_run" ]; then
if [ "${{ github.event.workflow_run.conclusion }}" == "success" ]; then
TARGET_ENV="staging"
SHOULD_DEPLOY="true"
else
echo "Test Pipeline 未通过,跳过部署"
fi
fi
echo "should_deploy=$SHOULD_DEPLOY" >> $GITHUB_OUTPUT
echo "is_rollback=$IS_ROLLBACK" >> $GITHUB_OUTPUT
echo "target_env=$TARGET_ENV" >> $GITHUB_OUTPUT
echo "## 部署门禁" >> $GITHUB_STEP_SUMMARY
echo "- 触发方式: ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY
echo "- 目标环境: $TARGET_ENV" >> $GITHUB_STEP_SUMMARY
echo "- 允许部署: $SHOULD_DEPLOY" >> $GITHUB_STEP_SUMMARY
echo "- 回滚模式: $IS_ROLLBACK" >> $GITHUB_STEP_SUMMARY
outputs:
is_rollback: ${{ steps.check.outputs.is_rollback }}
should_deploy: ${{ steps.check.outputs.should_deploy }}
target_env: ${{ steps.check.outputs.target_env }}
permissions:
contents: read
...
|
gate
|
null
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1777220527
|
1777220000
|
1777220527
|
|
0
|
|
0
|
Edit
Delete
|
|
23388
|
17749
|
6
|
5
|
c04c1f61a7ca64acf31ed914678656d87ed6e35f
|
0
|
检测代码变更
|
0
|
name: Test Pipeline
"on":
push:
name: Test Pipeline
"on":
push:
branches: [main, develop, 'feature/**', 'claude/**']
pull_request:
branches: [main, develop]
workflow_dispatch:
inputs:
coverage_threshold:
description: '覆盖率阈值 (%)'
required: false
default: '80'
run_ai_tests:
description: '运行 AI 模块测试'
required: false
default: 'true'
type: boolean
run_api_tests:
description: '运行 API 集成测试'
required: false
default: 'true'
type: boolean
run_security_audit:
description: '运行安全审计'
required: false
default: 'true'
type: boolean
env:
COVERAGE_THRESHOLD: ${{ github.event.inputs.coverage_threshold || '80' }}
NODE_VERSION: "20"
PNPM_VERSION: "8"
jobs:
detect-changes:
name: 检测代码变更
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- id: filter
uses: dorny/paths-filter@v3
with:
filters: |
backend:
- 'backend/**'
- 'shared/**'
frontend:
- 'frontend/**'
- 'shared/**'
shared:
- 'shared/**'
ai-modules:
- 'backend/src/modules/ai-agents/**'
- 'backend/src/modules/ai-agent-skills/**'
- 'backend/src/modules/ai-review-queue/**'
- 'backend/src/modules/ai-copilot/**'
- 'backend/src/modules/lead-scoring-ai/**'
- 'backend/src/shared/state-machines/machines/ai-*.ts'
- 'backend/src/shared/state-machines/machines/training-*.ts'
prisma:
- 'backend/prisma/**'
e2e:
- 'e2e/**'
outputs:
ai-modules: ${{ steps.filter.outputs.ai-modules }}
backend: ${{ steps.filter.outputs.backend }}
e2e: ${{ steps.filter.outputs.e2e }}
frontend: ${{ steps.filter.outputs.frontend }}
prisma: ${{ steps.filter.outputs.prisma }}
shared: ${{ steps.filter.outputs.shared }}
...
|
detect-changes
|
null
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1777220521
|
1777220000
|
1777220521
|
|
0
|
|
0
|
Edit
Delete
|
|
23389
|
17749
|
6
|
5
|
c04c1f61a7ca64acf31ed914678656d87ed6e35f
|
0
|
安装依赖
|
0
|
name: Test Pipeline
"on":
push:
name: Test Pipeline
"on":
push:
branches: [main, develop, 'feature/**', 'claude/**']
pull_request:
branches: [main, develop]
workflow_dispatch:
inputs:
coverage_threshold:
description: '覆盖率阈值 (%)'
required: false
default: '80'
run_ai_tests:
description: '运行 AI 模块测试'
required: false
default: 'true'
type: boolean
run_api_tests:
description: '运行 API 集成测试'
required: false
default: 'true'
type: boolean
run_security_audit:
description: '运行安全审计'
required: false
default: 'true'
type: boolean
env:
COVERAGE_THRESHOLD: ${{ github.event.inputs.coverage_threshold || '80' }}
NODE_VERSION: "20"
PNPM_VERSION: "8"
jobs:
setup:
name: 安装依赖
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: 安装 pnpm
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- name: 设置 Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- name: 安装依赖
run: pnpm install --frozen-lockfile
- name: 构建共享包
run: pnpm --filter @juhi/shared run build
- name: 缓存依赖
uses: actions/cache/save@v4
with:
key: pipeline-deps-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
path: |
node_modules
backend/node_modules
frontend/node_modules
shared/node_modules
shared/dist
e2e/node_modules
...
|
setup
|
null
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1777220522
|
1777220000
|
1777220522
|
|
0
|
|
0
|
Edit
Delete
|
|
23410
|
17750
|
6
|
5
|
c04c1f61a7ca64acf31ed914678656d87ed6e35f
|
0
|
生产环境健康检查
|
1
|
name: Health Check
"on":
schedule:
name: Health Check
"on":
schedule:
# 每 5 分钟检查一次
- cron: '*/5 * * * *'
workflow_dispatch:
jobs:
health-check:
name: 生产环境健康检查
runs-on: ubuntu-latest
if: github.repository == 'your-org/juhi' # 替换为实际仓库
steps:
- id: api-health
name: API 健康检查
run: |
RESPONSE=$(curl -sf https://juhi.example.com/health || echo '{"status":"error"}')
echo "response=$RESPONSE" >> $GITHUB_OUTPUT
STATUS=$(echo $RESPONSE | jq -r '.status // "error"')
if [ "$STATUS" != "ok" ]; then
echo "API 健康检查失败"
exit 1
fi
echo "API 健康检查通过"
- name: 前端可访问性检查
run: |
HTTP_STATUS=$(curl -so /dev/null -w "%{http_code}" https://juhi.example.com/)
if [ "$HTTP_STATUS" != "200" ]; then
echo "前端返回 HTTP $HTTP_STATUS"
exit 1
fi
echo "前端可访问性检查通过"
- name: SSL 证书检查
run: |
EXPIRY_DATE=$(echo | openssl s_client -servername juhi.example.com -connect juhi.example.com:443 2>/dev/null | openssl x509 -noout -enddate | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$EXPIRY_DATE" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( ($EXPIRY_EPOCH - $NOW_EPOCH) / 86400 ))
echo "SSL 证书剩余 $DAYS_LEFT 天"
if [ $DAYS_LEFT -lt 7 ]; then
echo "::warning::SSL 证书将在 $DAYS_LEFT 天后过期!"
fi
if [ $DAYS_LEFT -lt 0 ]; then
echo "SSL 证书已过期"
exit 1
fi
- name: 响应时间检查
run: |
RESPONSE_TIME=$(curl -so /dev/null -w "%{time_total}" https://juhi.example.com/health)
echo "API 响应时间: ${RESPONSE_TIME}s"
# 响应时间超过 5 秒告警
if (( $(echo "$RESPONSE_TIME > 5.0" | bc -l) )); then
echo "::warning::API 响应时间过长: ${RESPONSE_TIME}s"
fi
- if: failure()
name: Slack 通知(失败时)
uses: 8398a7/action-slack@v3
with:
fields: repo,message,commit,author,action,eventName,workflow
status: ${{ job.status }}
text: "\U0001F6A8 生产环境健康检查失败!请立即检查。"
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
...
|
health-check
|
null
|
["ubuntu-latest"]
|
20251
|
4
|
1777223830
|
1777223830
|
1777220150
|
1777223830
|
|
0
|
|
0
|
Edit
Delete
|
|
23411
|
17751
|
6
|
5
|
c04c1f61a7ca64acf31ed914678656d87ed6e35f
|
0
|
生产环境健康检查
|
1
|
name: Health Check
"on":
schedule:
name: Health Check
"on":
schedule:
# 每 5 分钟检查一次
- cron: '*/5 * * * *'
workflow_dispatch:
jobs:
health-check:
name: 生产环境健康检查
runs-on: ubuntu-latest
if: github.repository == 'your-org/juhi' # 替换为实际仓库
steps:
- id: api-health
name: API 健康检查
run: |
RESPONSE=$(curl -sf https://juhi.example.com/health || echo '{"status":"error"}')
echo "response=$RESPONSE" >> $GITHUB_OUTPUT
STATUS=$(echo $RESPONSE | jq -r '.status // "error"')
if [ "$STATUS" != "ok" ]; then
echo "API 健康检查失败"
exit 1
fi
echo "API 健康检查通过"
- name: 前端可访问性检查
run: |
HTTP_STATUS=$(curl -so /dev/null -w "%{http_code}" https://juhi.example.com/)
if [ "$HTTP_STATUS" != "200" ]; then
echo "前端返回 HTTP $HTTP_STATUS"
exit 1
fi
echo "前端可访问性检查通过"
- name: SSL 证书检查
run: |
EXPIRY_DATE=$(echo | openssl s_client -servername juhi.example.com -connect juhi.example.com:443 2>/dev/null | openssl x509 -noout -enddate | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$EXPIRY_DATE" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( ($EXPIRY_EPOCH - $NOW_EPOCH) / 86400 ))
echo "SSL 证书剩余 $DAYS_LEFT 天"
if [ $DAYS_LEFT -lt 7 ]; then
echo "::warning::SSL 证书将在 $DAYS_LEFT 天后过期!"
fi
if [ $DAYS_LEFT -lt 0 ]; then
echo "SSL 证书已过期"
exit 1
fi
- name: 响应时间检查
run: |
RESPONSE_TIME=$(curl -so /dev/null -w "%{time_total}" https://juhi.example.com/health)
echo "API 响应时间: ${RESPONSE_TIME}s"
# 响应时间超过 5 秒告警
if (( $(echo "$RESPONSE_TIME > 5.0" | bc -l) )); then
echo "::warning::API 响应时间过长: ${RESPONSE_TIME}s"
fi
- if: failure()
name: Slack 通知(失败时)
uses: 8398a7/action-slack@v3
with:
fields: repo,message,commit,author,action,eventName,workflow
status: ${{ job.status }}
text: "\U0001F6A8 生产环境健康检查失败!请立即检查。"
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
...
|
health-check
|
null
|
["ubuntu-latest"]
|
20252
|
4
|
1777223832
|
1777223832
|
1777220450
|
1777223832
|
|
0
|
|
0
|
Edit
Delete
|
|
23412
|
17752
|
6
|
5
|
a67a4f5bf6bede6341ed368fb3af3caec8dcd71c
|
0
|
Frontend Type Check
|
0
|
name: CI
"on":
push:
branches: name: CI
"on":
push:
branches: [main]
pull_request:
branches: [main]
env:
NODE_VERSION: "20"
PNPM_VERSION: "9"
jobs:
frontend-typecheck:
name: Frontend Type Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- run: pnpm install --frozen-lockfile
- name: Build shared package
run: pnpm -C shared run build
- name: 前端类型检查(vue-tsc --noEmit)
run: pnpm --filter ./frontend run type-check
permissions:
contents: read
...
|
frontend-typecheck
|
null
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1777221953
|
1777220520
|
1777221953
|
|
0
|
|
0
|
Edit
Delete
|
|
23413
|
17752
|
6
|
5
|
a67a4f5bf6bede6341ed368fb3af3caec8dcd71c
|
0
|
Lint & Type Check
|
0
|
name: CI
"on":
push:
branches: name: CI
"on":
push:
branches: [main]
pull_request:
branches: [main]
env:
NODE_VERSION: "20"
PNPM_VERSION: "9"
jobs:
lint-and-typecheck:
name: Lint & Type Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- run: pnpm install --frozen-lockfile
- name: Generate Prisma Client
run: pnpm -C backend exec prisma generate
- name: Build shared package
run: pnpm -C shared run build
- name: Backend type check
run: pnpm -C backend run type-check
- name: Lint
run: pnpm -C backend run lint
permissions:
contents: read
...
|
lint-and-typecheck
|
null
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1777221953
|
1777220520
|
1777221953
|
|
0
|
|
0
|
Edit
Delete
|
|
23416
|
17753
|
6
|
5
|
a67a4f5bf6bede6341ed368fb3af3caec8dcd71c
|
0
|
pr-validation
|
1
|
name: E2E Tests
"on":
# PR validation name: E2E Tests
"on":
# PR validation - 运行关键测试
pull_request:
branches: [main, develop]
paths:
- 'frontend/**'
- 'backend/**'
- 'e2e/**'
- 'package.json'
- 'pnpm-lock.yaml'
# Push to main - 运行完整测试套件
push:
branches: [main]
# 每日定时全量测试 (UTC 时间 00:00 = 北京时间 08:00)
schedule:
- cron: '0 0 * * *'
# 手动触发
workflow_dispatch:
inputs:
test_suite:
description: 'Test suite to run'
required: true
default: 'all'
type: choice
options:
- all
- critical
- business-flows
- visual-regression
- performance
env:
NODE_VERSION: "20"
PNPM_VERSION: "9"
jobs:
pr-validation:
name: pr-validation
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup test database
run: |
cd backend
npx prisma migrate deploy
npx prisma db seed
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
- name: Build backend
run: |
cd backend
npm run build
- name: Build frontend
run: |
cd frontend
npm run build
- name: Install Playwright Browsers
run: npx playwright install --with-deps chromium
- name: Start backend server
run: |
cd backend
npm run start &
sleep 10
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
REDIS_URL: redis://localhost:6379
NODE_ENV: test
- name: Start frontend server
run: |
cd frontend
npm run preview &
sleep 5
env:
VITE_API_URL: http://localhost:3000
- name: Run critical E2E tests
run: |
cd e2e
npx playwright test \
tests/auth/login.spec.ts \
tests/navigation/full-menu-click.spec.ts \
tests/multi-tenant/data-isolation.spec.ts \
tests/form-interactions/ \
--reporter=html
env:
E2E_BASE_URL: http://localhost:5173
E2E_TEST_USER: admin@juhi.com
E2E_TEST_PASSWORD: Admin@123
- if: always()
name: Upload test results
uses: actions/upload-artifact@v4
with:
name: playwright-report-pr
path: e2e/playwright-report/
retention-days: "7"
- if: always() && github.event_name == 'pull_request'
name: Comment PR with test results
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const reportPath = 'e2e/playwright-report/index.html';
const testsPassed = !fs.existsSync('e2e/test-results/');
const comment = testsPassed
? '✅ E2E 测试通过!'
: '❌ E2E 测试失败,请查看报告';
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: comment
});
timeout-minutes: "30"
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: juhi_test
POSTGRES_PASSWORD: test_password
POSTGRES_USER: test_user
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
...
|
pr-validation
|
null
|
["ubuntu-latest"]
|
20253
|
4
|
1777223834
|
1777223834
|
1777220521
|
1777223834
|
|
0
|
|
0
|
Edit
Delete
|
|
23417
|
17753
|
6
|
5
|
a67a4f5bf6bede6341ed368fb3af3caec8dcd71c
|
0
|
full-test-suite (chromium)
|
1
|
name: E2E Tests
"on":
# PR validation name: E2E Tests
"on":
# PR validation - 运行关键测试
pull_request:
branches: [main, develop]
paths:
- 'frontend/**'
- 'backend/**'
- 'e2e/**'
- 'package.json'
- 'pnpm-lock.yaml'
# Push to main - 运行完整测试套件
push:
branches: [main]
# 每日定时全量测试 (UTC 时间 00:00 = 北京时间 08:00)
schedule:
- cron: '0 0 * * *'
# 手动触发
workflow_dispatch:
inputs:
test_suite:
description: 'Test suite to run'
required: true
default: 'all'
type: choice
options:
- all
- critical
- business-flows
- visual-regression
- performance
env:
NODE_VERSION: "20"
PNPM_VERSION: "9"
jobs:
full-test-suite:
name: full-test-suite (chromium)
runs-on: ubuntu-latest
if: github.event_name == 'push' || github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.test_suite == 'all')
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup test database
run: |
cd backend
npx prisma migrate deploy
npx prisma db seed
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
- name: Build backend
run: |
cd backend
npm run build
- name: Build frontend
run: |
cd frontend
npm run build
- name: Install Playwright Browsers
run: npx playwright install --with-deps ${{ matrix.browser }}
- name: Start backend server
run: |
cd backend
npm run start &
sleep 10
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
REDIS_URL: redis://localhost:6379
NODE_ENV: test
- name: Start frontend server
run: |
cd frontend
npm run preview &
sleep 5
env:
VITE_API_URL: http://localhost:3000
- name: Run all E2E tests
run: |
cd e2e
npx playwright test --project=${{ matrix.browser }} --reporter=html,json
env:
E2E_BASE_URL: http://localhost:5173
E2E_TEST_USER: admin@juhi.com
E2E_TEST_PASSWORD: Admin@123
- if: always()
name: Upload test results
uses: actions/upload-artifact@v4
with:
name: playwright-report-${{ matrix.browser }}
path: e2e/playwright-report/
retention-days: "30"
- if: always()
name: Upload test artifacts
uses: actions/upload-artifact@v4
with:
name: test-results-${{ matrix.browser }}
path: e2e/test-results/
retention-days: "30"
timeout-minutes: "60"
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: juhi_test
POSTGRES_PASSWORD: test_password
POSTGRES_USER: test_user
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
strategy:
fail-fast: "false"
matrix:
browser:
- chromium
...
|
full-test-suite
|
null
|
["ubuntu-latest"]
|
20254
|
2
|
1777223836
|
1777223866
|
1777220521
|
1777223866
|
|
0
|
|
0
|
Edit
Delete
|
|
23418
|
17753
|
6
|
5
|
a67a4f5bf6bede6341ed368fb3af3caec8dcd71c
|
0
|
full-test-suite (firefox)
|
1
|
name: E2E Tests
"on":
# PR validation name: E2E Tests
"on":
# PR validation - 运行关键测试
pull_request:
branches: [main, develop]
paths:
- 'frontend/**'
- 'backend/**'
- 'e2e/**'
- 'package.json'
- 'pnpm-lock.yaml'
# Push to main - 运行完整测试套件
push:
branches: [main]
# 每日定时全量测试 (UTC 时间 00:00 = 北京时间 08:00)
schedule:
- cron: '0 0 * * *'
# 手动触发
workflow_dispatch:
inputs:
test_suite:
description: 'Test suite to run'
required: true
default: 'all'
type: choice
options:
- all
- critical
- business-flows
- visual-regression
- performance
env:
NODE_VERSION: "20"
PNPM_VERSION: "9"
jobs:
full-test-suite:
name: full-test-suite (firefox)
runs-on: ubuntu-latest
if: github.event_name == 'push' || github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.test_suite == 'all')
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup test database
run: |
cd backend
npx prisma migrate deploy
npx prisma db seed
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
- name: Build backend
run: |
cd backend
npm run build
- name: Build frontend
run: |
cd frontend
npm run build
- name: Install Playwright Browsers
run: npx playwright install --with-deps ${{ matrix.browser }}
- name: Start backend server
run: |
cd backend
npm run start &
sleep 10
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
REDIS_URL: redis://localhost:6379
NODE_ENV: test
- name: Start frontend server
run: |
cd frontend
npm run preview &
sleep 5
env:
VITE_API_URL: http://localhost:3000
- name: Run all E2E tests
run: |
cd e2e
npx playwright test --project=${{ matrix.browser }} --reporter=html,json
env:
E2E_BASE_URL: http://localhost:5173
E2E_TEST_USER: admin@juhi.com
E2E_TEST_PASSWORD: Admin@123
- if: always()
name: Upload test results
uses: actions/upload-artifact@v4
with:
name: playwright-report-${{ matrix.browser }}
path: e2e/playwright-report/
retention-days: "30"
- if: always()
name: Upload test artifacts
uses: actions/upload-artifact@v4
with:
name: test-results-${{ matrix.browser }}
path: e2e/test-results/
retention-days: "30"
timeout-minutes: "60"
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: juhi_test
POSTGRES_PASSWORD: test_password
POSTGRES_USER: test_user
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
strategy:
fail-fast: "false"
matrix:
browser:
- firefox
...
|
full-test-suite
|
null
|
["ubuntu-latest"]
|
20255
|
2
|
1777223866
|
1777223888
|
1777220521
|
1777223888
|
|
0
|
|
0
|
Edit
Delete
|
|
23419
|
17753
|
6
|
5
|
a67a4f5bf6bede6341ed368fb3af3caec8dcd71c
|
0
|
full-test-suite (webkit)
|
1
|
name: E2E Tests
"on":
# PR validation name: E2E Tests
"on":
# PR validation - 运行关键测试
pull_request:
branches: [main, develop]
paths:
- 'frontend/**'
- 'backend/**'
- 'e2e/**'
- 'package.json'
- 'pnpm-lock.yaml'
# Push to main - 运行完整测试套件
push:
branches: [main]
# 每日定时全量测试 (UTC 时间 00:00 = 北京时间 08:00)
schedule:
- cron: '0 0 * * *'
# 手动触发
workflow_dispatch:
inputs:
test_suite:
description: 'Test suite to run'
required: true
default: 'all'
type: choice
options:
- all
- critical
- business-flows
- visual-regression
- performance
env:
NODE_VERSION: "20"
PNPM_VERSION: "9"
jobs:
full-test-suite:
name: full-test-suite (webkit)
runs-on: ubuntu-latest
if: github.event_name == 'push' || github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.test_suite == 'all')
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup test database
run: |
cd backend
npx prisma migrate deploy
npx prisma db seed
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
- name: Build backend
run: |
cd backend
npm run build
- name: Build frontend
run: |
cd frontend
npm run build
- name: Install Playwright Browsers
run: npx playwright install --with-deps ${{ matrix.browser }}
- name: Start backend server
run: |
cd backend
npm run start &
sleep 10
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
REDIS_URL: redis://localhost:6379
NODE_ENV: test
- name: Start frontend server
run: |
cd frontend
npm run preview &
sleep 5
env:
VITE_API_URL: http://localhost:3000
- name: Run all E2E tests
run: |
cd e2e
npx playwright test --project=${{ matrix.browser }} --reporter=html,json
env:
E2E_BASE_URL: http://localhost:5173
E2E_TEST_USER: admin@juhi.com
E2E_TEST_PASSWORD: Admin@123
- if: always()
name: Upload test results
uses: actions/upload-artifact@v4
with:
name: playwright-report-${{ matrix.browser }}
path: e2e/playwright-report/
retention-days: "30"
- if: always()
name: Upload test artifacts
uses: actions/upload-artifact@v4
with:
name: test-results-${{ matrix.browser }}
path: e2e/test-results/
retention-days: "30"
timeout-minutes: "60"
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: juhi_test
POSTGRES_PASSWORD: test_password
POSTGRES_USER: test_user
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
strategy:
fail-fast: "false"
matrix:
browser:
- webkit
...
|
full-test-suite
|
null
|
["ubuntu-latest"]
|
20256
|
2
|
1777223889
|
1777223906
|
1777220521
|
1777223906
|
|
0
|
|
0
|
Edit
Delete
|
|
23420
|
17753
|
6
|
5
|
a67a4f5bf6bede6341ed368fb3af3caec8dcd71c
|
0
|
business-flows
|
1
|
name: E2E Tests
"on":
# PR validation name: E2E Tests
"on":
# PR validation - 运行关键测试
pull_request:
branches: [main, develop]
paths:
- 'frontend/**'
- 'backend/**'
- 'e2e/**'
- 'package.json'
- 'pnpm-lock.yaml'
# Push to main - 运行完整测试套件
push:
branches: [main]
# 每日定时全量测试 (UTC 时间 00:00 = 北京时间 08:00)
schedule:
- cron: '0 0 * * *'
# 手动触发
workflow_dispatch:
inputs:
test_suite:
description: 'Test suite to run'
required: true
default: 'all'
type: choice
options:
- all
- critical
- business-flows
- visual-regression
- performance
env:
NODE_VERSION: "20"
PNPM_VERSION: "9"
jobs:
business-flows:
name: business-flows
runs-on: ubuntu-latest
if: github.event_name == 'workflow_dispatch' && github.event.inputs.test_suite == 'business-flows'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup test database
run: |
cd backend
npx prisma migrate deploy
npx prisma db seed
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
- name: Build and start services
run: |
cd backend && npm run build && npm run start &
cd frontend && npm run build && npm run preview &
sleep 15
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/juhi_test
REDIS_URL: redis://localhost:6379
NODE_ENV: test
VITE_API_URL: http://localhost:3000
- name: Install Playwright
run: npx playwright install --with-deps chromium
- name: Run business flow tests
run: |
cd e2e
npx playwright test tests/business-flows/ --reporter=html,json
env:
E2E_BASE_URL: http://localhost:5173
E2E_TEST_USER: admin@juhi.com
E2E_TEST_PASSWORD: Admin@123
- if: always()
name: Upload test results
uses: actions/upload-artifact@v4
with:
name: business-flows-report
path: e2e/playwright-report/
retention-days: "30"
timeout-minutes: "45"
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: juhi_test
POSTGRES_PASSWORD: test_password
POSTGRES_USER: test_user
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
...
|
business-flows
|
null
|
["ubuntu-latest"]
|
20257
|
4
|
1777223907
|
1777223907
|
1777220521
|
1777223907
|
|
0
|
|
0
|
Edit
Delete
|