|
23311
|
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:
backend-build:
name: 后端构建验证
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:
node-version: ${{ env.NODE_VERSION }}
- name: 恢复依赖缓存
uses: actions/cache/restore@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
- name: 生成 Prisma Client
run: pnpm --filter juhi-api run db:generate
- name: 构建
run: pnpm --filter juhi-api run build
- name: 验证构建产物
run: |
if [ ! -d "backend/dist" ]; then
echo "后端构建产物不存在"
exit 1
fi
...
|
backend-build
|
["backend-lint"]
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1777219590
|
1777219018
|
1777219590
|
|
0
|
|
0
|
Edit
Delete
|
|
23312
|
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:
frontend-build:
name: 前端构建验证
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:
node-version: ${{ env.NODE_VERSION }}
- name: 恢复依赖缓存
uses: actions/cache/restore@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
- name: 构建
run: pnpm --filter juhi-frontend run build
- name: 验证构建产物
run: |
if [ ! -d "frontend/dist" ]; then
echo "前端构建产物不存在"
exit 1
fi
- name: 上传构建产物
uses: actions/upload-artifact@v4
with:
name: frontend-dist
path: frontend/dist
retention-days: "7"
...
|
frontend-build
|
["frontend-lint"]
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1777219590
|
1777219018
|
1777219590
|
|
0
|
|
0
|
Edit
Delete
|
|
23313
|
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:
tenant-security-audit:
name: 多租户安全审计
runs-on: ubuntu-latest
if: github.event.inputs.run_security_audit != 'false'
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: 恢复依赖缓存
uses: actions/cache/restore@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
- name: 生成 Prisma Client
run: pnpm --filter juhi-api run db:generate
- name: 运行多租户安全审计(阻塞性)
run: pnpm --filter juhi-api run audit:tenant
- if: always()
name: 上传审计报告
uses: actions/upload-artifact@v4
with:
name: tenant-security-report
path: reports/
retention-days: "30"
...
|
tenant-security-audit
|
["backend-lint"]
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1777219590
|
1777219018
|
1777219590
|
|
0
|
|
0
|
Edit
Delete
|
|
23314
|
17738
|
6
|
5
|
bd8162cf2c08d175a1cab0f69d42b648bb825c10
|
0
|
Kafka 事件一致性审计
|
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:
kafka-event-audit:
name: Kafka 事件一致性审计
runs-on: ubuntu-latest
if: github.event.inputs.run_security_audit != 'false'
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: 恢复依赖缓存
uses: actions/cache/restore@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
- name: 生成 Prisma Client
run: pnpm --filter juhi-api run db:generate
- name: 运行 Kafka 事件审计(阻塞性)
run: cd backend && npx tsx scripts/audit-kafka-events.ts --ci
- if: always()
name: 上传审计报告
uses: actions/upload-artifact@v4
with:
name: kafka-audit-report
path: backend/kafka-audit-report.json
retention-days: "30"
...
|
kafka-event-audit
|
["backend-lint"]
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1777219591
|
1777219018
|
1777219591
|
|
0
|
|
0
|
Edit
Delete
|
|
23315
|
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:
dependency-security-scan:
name: 依赖安全扫描
runs-on: ubuntu-latest
if: |
contains(github.event.head_commit.modified, 'package.json') ||
contains(github.event.head_commit.modified, 'pnpm-lock.yaml') ||
github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: 恢复依赖缓存
uses: actions/cache/restore@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
- name: pnpm 依赖安全扫描(阻塞性)
run: pnpm audit --audit-level high
...
|
dependency-security-scan
|
["setup"]
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1777219591
|
1777219018
|
1777219591
|
|
0
|
|
0
|
Edit
Delete
|
|
23316
|
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:
governance-baseline:
name: 治理基线采集
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: "0"
- uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: 恢复依赖缓存
uses: actions/cache/restore@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
- name: 生成 Prisma Client
run: pnpm --filter juhi-api run db:generate
- name: 审计门禁基线采集
run: pnpm --filter juhi-api run audit:baseline:capture -- --label pipeline-${{ github.run_id }}
continue-on-error: true
- if: always()
name: 生成治理摘要
run: |
mkdir -p reports/audit-baseline/latest
if [ -f reports/audit-baseline/latest/summary.json ]; then
pnpm --filter juhi-api run governance:summary -- \
--module workspace-baseline \
--stage Pipeline \
--scope workspace \
--classification workspace-baseline \
--summary-json reports/audit-baseline/latest/summary.json \
--output reports/audit-baseline/latest/governance-summary.md
fi
- if: always()
name: 写入审计摘要
run: |
if [ -f reports/audit-baseline/latest/summary.md ]; then
cat reports/audit-baseline/latest/summary.md >> $GITHUB_STEP_SUMMARY
fi
if [ -f reports/audit-baseline/latest/governance-summary.md ]; then
echo "" >> $GITHUB_STEP_SUMMARY
cat reports/audit-baseline/latest/governance-summary.md >> $GITHUB_STEP_SUMMARY
fi
- if: always()
name: 上传审计基线
uses: actions/upload-artifact@v4
with:
name: governance-baseline
path: reports/audit-baseline/latest
retention-days: "30"
...
|
governance-baseline
|
["backend-lint"]
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1777219591
|
1777219018
|
1777219591
|
|
0
|
|
0
|
Edit
Delete
|
|
23317
|
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:
coverage-report:
name: 覆盖率报告合并
runs-on: ubuntu-latest
if: always() && !cancelled()
steps:
- uses: actions/checkout@v4
- name: 下载所有覆盖率报告
uses: actions/download-artifact@v4
with:
merge-multiple: "true"
path: coverage-reports
pattern: '*-coverage'
- name: 上传到 Codecov
uses: codecov/codecov-action@v4
with:
fail_ci_if_error: "false"
files: coverage-reports/**/*.info
verbose: "true"
...
|
coverage-report
|
["backend-unit-test","frontend-unit-te ["backend-unit-test","frontend-unit-test","shared-check"]...
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1777219591
|
1777219018
|
1777219591
|
|
0
|
|
0
|
Edit
Delete
|
|
23318
|
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:
quality-gate:
name: 质量门禁
runs-on: ubuntu-latest
if: always()
steps:
- name: 生成流水线报告
run: |
echo "## Test Pipeline 结果" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**分支**: \`${{ github.ref_name }}\`" >> $GITHUB_STEP_SUMMARY
echo "**提交**: \`${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY
echo "**触发者**: ${{ github.actor }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 静态检查 + 构建" >> $GITHUB_STEP_SUMMARY
echo "| 检查项 | 状态 |" >> $GITHUB_STEP_SUMMARY
echo "|--------|------|" >> $GITHUB_STEP_SUMMARY
echo "| 后端构建 | ${{ needs.backend-build.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY
echo "| 前端构建 | ${{ needs.frontend-build.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 测试" >> $GITHUB_STEP_SUMMARY
echo "| 测试套件 | 状态 |" >> $GITHUB_STEP_SUMMARY
echo "|----------|------|" >> $GITHUB_STEP_SUMMARY
echo "| 后端单元测试 (4 分片) | ${{ needs.backend-unit-test.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY
echo "| 后端 API 集成测试 | ${{ needs.backend-api-test.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY
echo "| 前端单元测试 | ${{ needs.frontend-unit-test.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY
echo "| 状态机测试 | ${{ needs.state-machine-test.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY
echo "| AI 模块单元测试 | ${{ needs.ai-unit-test.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY
echo "| AI 模块 API 测试 | ${{ needs.ai-api-test.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 安全审计" >> $GITHUB_STEP_SUMMARY
echo "| 审计项 | 状态 |" >> $GITHUB_STEP_SUMMARY
echo "|--------|------|" >> $GITHUB_STEP_SUMMARY
echo "| 多租户安全审计 | ${{ needs.tenant-security-audit.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY
echo "| Kafka 事件审计 | ${{ needs.kafka-event-audit.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY
echo "| 依赖安全扫描 | ${{ needs.dependency-security-scan.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY
echo "| 治理基线 | ${{ needs.governance-baseline.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY
- name: 门禁裁决
run: |
FAILED=0
# 构建必须通过
for job in "${{ needs.backend-build.result }}" "${{ needs.frontend-build.result }}"; do
if [ "$job" == "failure" ]; then
echo "构建失败"
FAILED=1
fi
done
# 单元测试必须通过
for job in "${{ needs.backend-unit-test.result }}" "${{ needs.frontend-unit-test.result }}"; do
if [ "$job" == "failure" ]; then
echo "单元测试失败"
FAILED=1
fi
done
# 状态机测试必须通过
if [ "${{ needs.state-machine-test.result }}" == "failure" ]; then
echo "状态机测试失败"
FAILED=1
fi
# AI 模块测试失败视为阻塞
if [ "${{ needs.ai-unit-test.result }}" == "failure" ]; then
echo "AI 模块单元测试失败"
FAILED=1
fi
# 多租户安全审计(红线 1)
if [ "${{ needs.tenant-security-audit.result }}" == "failure" ]; then
echo "多租户安全审计失败 - CRITICAL 数据安全风险"
FAILED=1
fi
# Kafka 事件审计(红线 3)
if [ "${{ needs.kafka-event-audit.result }}" == "failure" ]; then
echo "Kafka 事件审计失败 - 审计链断裂风险"
FAILED=1
fi
# 依赖安全扫描
if [ "${{ needs.dependency-security-scan.result }}" == "failure" ]; then
echo "依赖安全扫描失败 - 存在 high/critical 漏洞"
FAILED=1
fi
if [ $FAILED -eq 1 ]; then
echo ""
echo "质量门禁未通过"
exit 1
fi
echo "质量门禁通过"
- if: github.event_name == 'pull_request' && always()
name: PR 评论质量报告
uses: actions/github-script@v7
with:
script: |
const results = {
backendBuild: '${{ needs.backend-build.result }}',
frontendBuild: '${{ needs.frontend-build.result }}',
backendUnit: '${{ needs.backend-unit-test.result }}',
backendApi: '${{ needs.backend-api-test.result }}',
frontendUnit: '${{ needs.frontend-unit-test.result }}',
stateMachine: '${{ needs.state-machine-test.result }}',
aiUnit: '${{ needs.ai-unit-test.result }}',
aiApi: '${{ needs.ai-api-test.result }}',
tenantAudit: '${{ needs.tenant-security-audit.result }}',
kafkaAudit: '${{ needs.kafka-event-audit.result }}',
depScan: '${{ needs.dependency-security-scan.result }}',
};
const icon = (r) => r === 'success' ? '✅' : r === 'failure' ? '❌' : '⏭️';
const allPassed = !Object.values(results).some(r => r === 'failure');
const body = [
`## ${allPassed ? '✅' : '❌'} Test Pipeline 质量门禁`,
'',
'| 检查项 | 状态 |',
'|--------|------|',
`| 后端构建 | ${icon(results.backendBuild)} |`,
`| 前端构建 | ${icon(results.frontendBuild)} |`,
`| 后端单元测试 | ${icon(results.backendUnit)} |`,
`| 后端 API 测试 | ${icon(results.backendApi)} |`,
`| 前端单元测试 | ${icon(results.frontendUnit)} |`,
`| 状态机测试 | ${icon(results.stateMachine)} |`,
`| AI 单元测试 | ${icon(results.aiUnit)} |`,
`| AI API 测试 | ${icon(results.aiApi)} |`,
`| 多租户审计 | ${icon(results.tenantAudit)} |`,
`| Kafka 审计 | ${icon(results.kafkaAudit)} |`,
`| 依赖扫描 | ${icon(results.depScan)} |`,
'',
allPassed ? '**所有门禁通过,可以合并!**' : '**门禁未通过,请修复问题后重新提交。**',
].join('\n');
const { data: comments } = await github.rest.issues.listComments({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo
});
const existing = comments.find(c =>
c.user.type === 'Bot' && c.body.includes('Test Pipeline 质量门禁')
);
if (existing) {
await github.rest.issues.updateComment({
comment_id: existing.id,
owner: context.repo.owner,
repo: context.repo.repo,
body: body
});
} else {
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: body
});
}
...
|
quality-gate
|
["backend-unit-test","backend-api-test ["backend-unit-test","backend-api-test","backend-build","frontend-unit-test","frontend-build","state-machine-test","ai-unit-test","ai-api-test","tenant-security-audit","kafka-event-audit","dependency-security-scan","governance-baseline"]...
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1777219591
|
1777219018
|
1777219591
|
|
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
|
|
32563
|
23964
|
80
|
5
|
bda8349f3440331dc74da6a4a41880607201d63d
|
0
|
Static governance
|
1
|
name: Governance
"on":
pull_request:
name: Governance
"on":
pull_request:
push:
branches:
- main
env:
DIGITAL_EMPLOYEE_OS_ROOT: ${{ github.workspace }}/digital-employee-os
DIGITAL_EMPLOYEE_OS_SHA: 5cb2f1cc2f9953d47fa0bcfac541400d01603f78
jobs:
static-governance:
name: Static governance
runs-on: ubuntu-latest
steps:
- name: Checkout device cloud with history
uses: actions/checkout@v4
with:
fetch-depth: "0"
path: device-cloud
- name: Checkout pinned Digital Employee OS
uses: actions/checkout@v4
with:
fetch-depth: "1"
path: digital-employee-os
ref: ${{ env.DIGITAL_EMPLOYEE_OS_SHA }}
repository: laoluojuhai/digital-employee-os
token: ${{ secrets.OS_REPO_TOKEN }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "22"
- name: Enable pnpm
run: |
corepack enable
corepack prepare pnpm@9.15.9 --activate
- name: Install device-cloud dependencies
run: pnpm install --frozen-lockfile
- name: Install pinned OS dependencies
run: pnpm install --frozen-lockfile
working-directory: digital-employee-os
- name: Build OS contracts and generate device-cloud Prisma clients
run: |
pnpm --dir "$DIGITAL_EMPLOYEE_OS_ROOT" --filter @repo/contracts build
pnpm prisma:generate
- name: Run static governance gate
run: pnpm check
defaults:
run:
working-directory: device-cloud
...
|
static-governance
|
null
|
["ubuntu-latest"]
|
28207
|
2
|
1789441278
|
1789441286
|
1789440869
|
1789441286
|
|
0
|
|
0
|
Edit
Delete
|
|
32564
|
23964
|
80
|
5
|
bda8349f3440331dc74da6a4a41880607201d63d
|
0
|
Runtime, UI, and OS-host acceptance
|
0
|
name: Governance
"on":
pull_request:
name: Governance
"on":
pull_request:
push:
branches:
- main
env:
DIGITAL_EMPLOYEE_OS_ROOT: ${{ github.workspace }}/digital-employee-os
DIGITAL_EMPLOYEE_OS_SHA: 5cb2f1cc2f9953d47fa0bcfac541400d01603f78
jobs:
runtime-acceptance:
name: Runtime, UI, and OS-host acceptance
runs-on: ubuntu-latest
env:
DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/device_cloud_ci?schema=public
REDIS_URL: redis://127.0.0.1:6379
steps:
- name: Checkout device cloud with history
uses: actions/checkout@v4
with:
fetch-depth: "0"
path: device-cloud
- name: Checkout pinned Digital Employee OS
uses: actions/checkout@v4
with:
fetch-depth: "1"
path: digital-employee-os
ref: ${{ env.DIGITAL_EMPLOYEE_OS_SHA }}
repository: laoluojuhai/digital-employee-os
token: ${{ secrets.OS_REPO_TOKEN }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "22"
- name: Enable pnpm
run: |
corepack enable
corepack prepare pnpm@9.15.9 --activate
- name: Install device-cloud dependencies
run: pnpm install --frozen-lockfile
- name: Install pinned OS dependencies
run: pnpm install --frozen-lockfile
working-directory: digital-employee-os
- name: Generate Prisma clients
run: |
pnpm prisma:generate
pnpm --dir "$DIGITAL_EMPLOYEE_OS_ROOT" prisma:generate
- name: Run real DB and Redis acceptance
run: pnpm check:runtime
- name: Install Playwright browser
run: pnpm --filter web exec playwright install --with-deps chromium
- name: Run UI acceptance
run: pnpm check:ui
- name: Create isolated OS-product database
run: psql -h 127.0.0.1 -U postgres -d postgres -v ON_ERROR_STOP=1 -c 'CREATE DATABASE device_cloud_os_product_ci'
env:
PGPASSWORD: postgres
- name: Run OS-host acceptance
run: pnpm check:os-product:runtime
env:
DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/device_cloud_os_product_ci?schema=public
- name: Prove release evidence matches current source
run: pnpm check:evidence
services:
postgres:
image: postgres:16
env:
POSTGRES_DB: device_cloud_ci
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
ports:
- 5432:5432
options: --health-cmd "pg_isready -U postgres -d device_cloud_ci" --health-interval 5s --health-timeout 5s --health-retries 20
redis:
image: redis:7
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 5s --health-timeout 5s --health-retries 20
defaults:
run:
working-directory: device-cloud
...
|
runtime-acceptance
|
["static-governance"]
|
["ubuntu-latest"]
|
0
|
4
|
0
|
0
|
1789440869
|
1789441287
|
|
1
|
|
0
|
Edit
Delete
|
|
31857
|
23775
|
57
|
5
|
bdd85f7670b0e415d30614a16d6c1b2ca5f16a5e
|
0
|
Static governance
|
1
|
name: Governance
"on":
pull_request:
name: Governance
"on":
pull_request:
push:
branches:
- main
jobs:
static-governance:
name: Static governance
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: .node-version
- name: Enable pnpm
run: |
corepack enable
corepack prepare pnpm@9.15.9 --activate
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Generate Prisma clients
run: pnpm prisma:generate
- name: Run static governance gate
run: pnpm check
- name: Runtime and development dependency audit
run: pnpm check:deps
permissions:
contents: read
...
|
static-governance
|
null
|
["ubuntu-latest"]
|
27651
|
2
|
1788949348
|
1788949438
|
1788949346
|
1788949438
|
|
0
|
|
0
|
Edit
Delete
|
|
32031
|
23875
|
116
|
5
|
be7334ad46324e54bebe2397365086896b6ad42b
|
0
|
Public static checks (no private packages)
|
0
|
name: Platform
"on":
pull_request:
name: Platform
"on":
pull_request:
push:
branches: [main]
workflow_dispatch:
env:
CANDIDATE_TAG: ${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
PNPM_VERSION: 9.15.9
jobs:
public-static:
name: Public static checks (no private packages)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: "0"
- name: Enable pnpm
run: |
corepack enable
corepack prepare pnpm@${PNPM_VERSION} --activate
- uses: actions/setup-node@v4
with:
node-version: "20"
- name: Governance checks that need no installed dependency
run: |
pnpm check:module-imports
pnpm check:catalog
pnpm check:migration-decs
pnpm check:caddy
pnpm test
pnpm contracts:check:local
- name: Pack publishable packages (contracts + governance; no publish)
run: |
rm -rf dist-artifacts
pnpm --dir governance pack --pack-destination ../dist-artifacts
pnpm --dir contracts pack --pack-destination ../dist-artifacts
ls -la dist-artifacts
- if: always()
name: Upload package tarballs
uses: actions/upload-artifact@v4
with:
if-no-files-found: error
name: platform-packages-${{ env.CANDIDATE_TAG }}
path: dist-artifacts/*.tgz
retention-days: "30"
- if: always()
name: Upload public static evidence
uses: actions/upload-artifact@v4
with:
if-no-files-found: warn
name: platform-public-static-${{ env.CANDIDATE_TAG }}
path: reports/*.latest.json
retention-days: "30"
timeout-minutes: "15"
permissions:
contents: read
...
|
public-static
|
null
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1789199753
|
1789199626
|
1789199753
|
|
0
|
|
0
|
Edit
Delete
|
|
32032
|
23875
|
116
|
5
|
be7334ad46324e54bebe2397365086896b6ad42b
|
0
|
Static governance (repo root, private packages)
|
0
|
name: Platform
"on":
pull_request:
name: Platform
"on":
pull_request:
push:
branches: [main]
workflow_dispatch:
env:
CANDIDATE_TAG: ${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
PNPM_VERSION: 9.15.9
jobs:
static:
name: Static governance (repo root, private packages)
runs-on: ubuntu-latest
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: "0"
- name: Enable pnpm
run: |
corepack enable
corepack prepare pnpm@${PNPM_VERSION} --activate
- uses: actions/setup-node@v4
with:
cache: pnpm
cache-dependency-path: runtime/pnpm-lock.yaml
node-version: "20"
- name: Private registry auth (temporary userconfig)
uses: ./.github/actions/private-npm
with:
token: ${{ secrets.GITEA_NPM_TOKEN }}
- name: Install runtime workspace (frozen lockfile)
run: pnpm --dir runtime install --frozen-lockfile
- name: Generate Prisma clients
run: pnpm --dir runtime prisma:generate
- name: Repo-root governance gate
run: pnpm check
- name: Source tree must stay clean apart from latest reports (no tracked .npmrc mutation)
run: |
git status --porcelain | grep -vE '\.latest\.json$' && { echo "::error::静态门禁修改了报告以外的跟踪文件"; exit 1; } || echo "clean apart from latest reports"
- if: always()
name: Upload static evidence
uses: actions/upload-artifact@v4
with:
if-no-files-found: warn
name: platform-static-${{ env.CANDIDATE_TAG }}
path: |
reports/*.latest.json
runtime/reports/*.latest.json
!runtime/reports/runtime-acceptance.latest.json
!runtime/reports/conformance-differential.latest.json
!runtime/reports/ui-acceptance.latest.json
retention-days: "30"
timeout-minutes: "30"
permissions:
contents: read
...
|
static
|
null
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1789199753
|
1789199626
|
1789199753
|
|
0
|
|
0
|
Edit
Delete
|
|
32033
|
23875
|
116
|
5
|
be7334ad46324e54bebe2397365086896b6ad42b
|
0
|
Unit tests without DB (modules / packages / govern Unit tests without DB (modules / packages / governance)...
|
0
|
name: Platform
"on":
pull_request:
name: Platform
"on":
pull_request:
push:
branches: [main]
workflow_dispatch:
env:
CANDIDATE_TAG: ${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
PNPM_VERSION: 9.15.9
jobs:
unit:
name: Unit tests without DB (modules / packages / governance)
runs-on: ubuntu-latest
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: "0"
- name: Enable pnpm
run: |
corepack enable
corepack prepare pnpm@${PNPM_VERSION} --activate
- uses: actions/setup-node@v4
with:
cache: pnpm
cache-dependency-path: runtime/pnpm-lock.yaml
node-version: "20"
- name: Private registry auth (temporary userconfig)
uses: ./.github/actions/private-npm
with:
token: ${{ secrets.GITEA_NPM_TOKEN }}
- name: Install runtime workspace (frozen lockfile)
run: pnpm --dir runtime install --frozen-lockfile
- name: Module / package unit tests
run: pnpm --dir runtime exec turbo run test --filter='./modules/*' --filter='./packages/*' --force
- name: Governance package tests
run: pnpm governance:test
timeout-minutes: "20"
permissions:
contents: read
...
|
unit
|
["static"]
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1789199753
|
1789199626
|
1789199753
|
|
0
|
|
0
|
Edit
Delete
|
|
32034
|
23875
|
116
|
5
|
be7334ad46324e54bebe2397365086896b6ad42b
|
0
|
Runtime and UI acceptance (real PostgreSQL + Redis Runtime and UI acceptance (real PostgreSQL + Redis)...
|
0
|
name: Platform
"on":
pull_request:
name: Platform
"on":
pull_request:
push:
branches: [main]
workflow_dispatch:
env:
CANDIDATE_TAG: ${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
PNPM_VERSION: 9.15.9
jobs:
runtime:
name: Runtime and UI acceptance (real PostgreSQL + Redis)
runs-on: ubuntu-latest
env:
# 库名前缀由 runtime/package.json name 派生(G15 基座守卫:enterprise_platform*)
DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/enterprise_platform_ci?schema=public
REDIS_URL: redis://127.0.0.1:6379
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: "0"
- name: Enable pnpm
run: |
corepack enable
corepack prepare pnpm@${PNPM_VERSION} --activate
- uses: actions/setup-node@v4
with:
cache: pnpm
cache-dependency-path: runtime/pnpm-lock.yaml
node-version: "20"
- name: Private registry auth (temporary userconfig)
uses: ./.github/actions/private-npm
with:
token: ${{ secrets.GITEA_NPM_TOKEN }}
- name: Install runtime workspace (frozen lockfile)
run: pnpm --dir runtime install --frozen-lockfile
- name: Generate Prisma clients
run: pnpm --dir runtime prisma:generate
- name: Real DB and Redis acceptance (deploy → RLS roles → differential → tests incl. 7 profile boots)
run: pnpm runtime:check:runtime
- name: Install Playwright browser
run: pnpm --dir runtime --filter web exec playwright install --with-deps chromium
- name: UI acceptance
run: pnpm runtime:check:ui
- if: always()
name: Upload runtime and UI evidence
uses: actions/upload-artifact@v4
with:
if-no-files-found: warn
name: platform-runtime-${{ env.CANDIDATE_TAG }}
path: |
runtime/reports/runtime-acceptance.latest.json
runtime/reports/conformance-differential.latest.json
runtime/reports/ui-acceptance.latest.json
retention-days: "30"
timeout-minutes: "50"
services:
postgres:
image: postgres:16
env:
POSTGRES_DB: enterprise_platform_ci
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
ports:
- 5432:5432
options: --health-cmd "pg_isready -U postgres -d enterprise_platform_ci" --health-interval 5s --health-timeout 5s --health-retries 20
redis:
image: redis:7
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 5s --health-timeout 5s --health-retries 20
permissions:
contents: read
...
|
runtime
|
["static"]
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1789199753
|
1789199626
|
1789199753
|
|
0
|
|
0
|
Edit
Delete
|
|
32035
|
23875
|
116
|
5
|
be7334ad46324e54bebe2397365086896b6ad42b
|
0
|
M1 identity transitional workspace (IdP static → E M1 identity transitional workspace (IdP static → E2 → governance)...
|
0
|
name: Platform
"on":
pull_request:
name: Platform
"on":
pull_request:
push:
branches: [main]
workflow_dispatch:
env:
CANDIDATE_TAG: ${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
PNPM_VERSION: 9.15.9
jobs:
identity:
name: M1 identity transitional workspace (IdP static → E2 → governance)
runs-on: ubuntu-latest
env:
# 库名前缀由 identity/package.json name 派生(enterprise_idp*);Redis 必须 db 0(老框架 C22)
DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/enterprise_idp_ci?schema=public
REDIS_URL: redis://127.0.0.1:6379/0
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: "0"
- name: Enable pnpm
run: |
corepack enable
corepack prepare pnpm@${PNPM_VERSION} --activate
- uses: actions/setup-node@v4
with:
cache: pnpm
cache-dependency-path: identity/pnpm-lock.yaml
node-version: "20"
- name: Private registry auth (temporary userconfig; identity 目前无私包依赖,保持同一配置步骤以便后续 pin)
uses: ./.github/actions/private-npm
with:
token: ${{ secrets.GITEA_NPM_TOKEN }}
- name: Install identity workspace (frozen lockfile)
run: pnpm --dir identity install --frozen-lockfile
- name: Generate Prisma clients
run: pnpm --dir identity prisma:generate
- name: IdP static sub-checks (independent of runtime / UI reports)
run: pnpm identity:check:static
- name: IdP real DB acceptance (255 tests, serialized)
run: pnpm identity:check:runtime
- name: Install Playwright browser
run: pnpm --dir identity --filter web exec playwright install --with-deps chromium
- name: IdP UI acceptance (12 cases)
run: pnpm identity:check:ui
- name: IdP final governance aggregation (same checkout, fresh runtime / UI reports)
run: pnpm identity:check:governance
- if: always()
name: Upload identity evidence
uses: actions/upload-artifact@v4
with:
if-no-files-found: warn
name: platform-identity-${{ env.CANDIDATE_TAG }}
path: identity/reports/*.latest.json
retention-days: "30"
timeout-minutes: "50"
services:
postgres:
image: postgres:16
env:
POSTGRES_DB: enterprise_idp_ci
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
ports:
- 5432:5432
options: --health-cmd "pg_isready -U postgres -d enterprise_idp_ci" --health-interval 5s --health-timeout 5s --health-retries 20
redis:
image: redis:7
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 5s --health-timeout 5s --health-retries 20
permissions:
contents: read
...
|
identity
|
["static"]
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1789199753
|
1789199626
|
1789199753
|
|
0
|
|
0
|
Edit
Delete
|
|
32036
|
23875
|
116
|
5
|
be7334ad46324e54bebe2397365086896b6ad42b
|
0
|
Release candidate verification + manifest
|
0
|
name: Platform
"on":
pull_request:
name: Platform
"on":
pull_request:
push:
branches: [main]
workflow_dispatch:
env:
CANDIDATE_TAG: ${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
PNPM_VERSION: 9.15.9
jobs:
candidate:
name: Release candidate verification + manifest
runs-on: ubuntu-latest
if: always()
steps:
- uses: actions/checkout@v4
with:
fetch-depth: "0"
- name: Enable pnpm
run: |
corepack enable
corepack prepare pnpm@${PNPM_VERSION} --activate
- uses: actions/setup-node@v4
with:
node-version: "20"
- name: Download static evidence
uses: actions/download-artifact@v4
with:
name: platform-static-${{ env.CANDIDATE_TAG }}
path: candidate/static
continue-on-error: true
- name: Download runtime evidence
uses: actions/download-artifact@v4
with:
name: platform-runtime-${{ env.CANDIDATE_TAG }}
path: candidate/runtime
continue-on-error: true
- name: Download identity evidence
uses: actions/download-artifact@v4
with:
name: platform-identity-${{ env.CANDIDATE_TAG }}
path: candidate/identity
continue-on-error: true
- id: verify
name: Verify candidate (per-report sourceSha / dirty / status / digest; missing → ineligible)
run: node governance/verify-candidate.mjs --candidate candidate --sha "${{ github.sha }}" --run-id "${{ github.run_id }}" --attempt "${{ github.run_attempt }}" --out reports/release-candidate.latest.json
continue-on-error: true
- name: Release Manifest (known facts, status partial until all deliverables exist)
run: node governance/release-manifest.mjs
- if: always()
name: Upload candidate evidence
uses: actions/upload-artifact@v4
with:
if-no-files-found: error
name: platform-candidate-${{ env.CANDIDATE_TAG }}
path: |
reports/release-candidate.latest.json
reports/release-manifest.latest.json
retention-days: "30"
- if: always()
name: Propagate upstream results and candidate eligibility
run: |
echo "public-static=${{ needs.public-static.result }} static=${{ needs.static.result }} runtime=${{ needs.runtime.result }} identity=${{ needs.identity.result }} verify=${{ steps.verify.outcome }}"
test "${{ needs.public-static.result }}" = "success"
test "${{ needs.static.result }}" = "success"
test "${{ needs.unit.result }}" = "success"
test "${{ needs.runtime.result }}" = "success"
test "${{ needs.identity.result }}" = "success"
test "${{ steps.verify.outcome }}" = "success"
timeout-minutes: "10"
permissions:
contents: read
...
|
candidate
|
["public-static","static","uni ["public-static","static","unit","runtime","identity"]...
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1789199753
|
1789199626
|
1789199753
|
|
0
|
|
0
|
Edit
Delete
|
|
31013
|
23470
|
51
|
5
|
bef7116148fe61f8cba8207bd921d296cb01883d
|
0
|
后端 · lint + 类型 + 无DB单测(含契约)
|
1
|
name: CI
"on":
push:
branches: name: CI
"on":
push:
branches: [main]
pull_request:
branches: [main]
jobs:
server-checks:
name: 后端 · lint + 类型 + 无DB单测(含契约)
runs-on: ubuntu-latest
env:
# 无DB单测会 import config/env(zod 启动校验,仅校验格式不连库):提供合法 DATABASE_URL/JWT_SECRET 让校验通过。
# 纯函数单测(leads.canTransition / platforms.platformConnectionTruth 等)不发查询,无需真实 DB 服务。
DATABASE_URL: postgresql://bentong:bentong@localhost:5432/bentong_test?schema=public
JWT_SECRET: ci-jwt-secret-not-for-prod
USE_MOCK_ADAPTERS: 'true'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
cache: npm
cache-dependency-path: server/package-lock.json
node-version: "20"
- run: npm ci
- run: npx prisma generate
- run: npm run lint
- run: npm run build
- run: npm run test:unit
defaults:
run:
working-directory: server
...
|
server-checks
|
null
|
["ubuntu-latest"]
|
27124
|
1
|
1785482826
|
1785482877
|
1785482825
|
1785482878
|
|
0
|
|
0
|
Edit
Delete
|
|
31014
|
23470
|
51
|
5
|
bef7116148fe61f8cba8207bd921d296cb01883d
|
0
|
后端 · 真实DB集成测试(多租户/RBAC/并发/状态机/归因)
|
1
|
name: CI
"on":
push:
branches: name: CI
"on":
push:
branches: [main]
pull_request:
branches: [main]
jobs:
server-integration:
name: 后端 · 真实DB集成测试(多租户/RBAC/并发/状态机/归因)
runs-on: ubuntu-latest
env:
# global-setup 会用这个串跑 prisma migrate deploy + seed,再运行 vitest。
TEST_DATABASE_URL: postgresql://bentong:bentong@localhost:5432/bentong_test?schema=public
DATABASE_URL: postgresql://bentong:bentong@localhost:5432/bentong_test?schema=public
# CI 专用占位密钥(仅满足 env.ts 的 zod 校验,绝非生产值)。
JWT_SECRET: ci-jwt-secret-not-for-prod
JWT_REFRESH_SECRET: ci-jwt-refresh-secret-not-for-prod
# 用 Mock 适配层:CI 不打外部 LLM/平台,确定性、零外部依赖。
USE_MOCK_ADAPTERS: 'true'
NODE_ENV: test
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
cache: npm
cache-dependency-path: server/package-lock.json
node-version: "20"
- run: npm ci
- run: npx prisma generate
- run: npm run test:integration
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_DB: bentong_test
POSTGRES_PASSWORD: bentong
POSTGRES_USER: bentong
ports:
- 5432:5432
options: --health-cmd "pg_isready -U bentong -d bentong_test" --health-interval 5s --health-timeout 5s --health-retries 20
defaults:
run:
working-directory: server
...
|
server-integration
|
null
|
["ubuntu-latest"]
|
27125
|
2
|
1785482878
|
1785482968
|
1785482825
|
1785482968
|
|
0
|
|
0
|
Edit
Delete
|
|
31015
|
23470
|
51
|
5
|
bef7116148fe61f8cba8207bd921d296cb01883d
|
0
|
前端 · 静态契约 + 屏幕治理 + lint + AOT构建
|
1
|
name: CI
"on":
push:
branches: name: CI
"on":
push:
branches: [main]
pull_request:
branches: [main]
jobs:
frontend-static:
name: 前端 · 静态契约 + 屏幕治理 + lint + AOT构建
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
cache: npm
cache-dependency-path: 团购本地生活AI运营App/package-lock.json
node-version: "20"
- run: npm ci
- run: npm run vendor
- run: npm test
- run: npm run lint
- run: npm run build
defaults:
run:
working-directory: 团购本地生活AI运营App
...
|
frontend-static
|
null
|
["ubuntu-latest"]
|
27126
|
2
|
1785482968
|
1785482998
|
1785482825
|
1785482999
|
|
0
|
|
0
|
Edit
Delete
|
|
2094
|
1374
|
6
|
5
|
bf252f4083bdceceec242d821c3d37825d51f29f
|
0
|
检测代码变更
|
1
|
name: CI
"on":
push:
branches: name: CI
"on":
push:
branches: [main, develop, 'feature/**', 'claude/**']
pull_request:
branches: [main, develop]
env:
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/**'
mobile:
- 'mobile/**'
- 'shared/**'
shared:
- 'shared/**'
workflows:
- '.github/workflows/**'
outputs:
backend: ${{ steps.filter.outputs.backend }}
frontend: ${{ steps.filter.outputs.frontend }}
mobile: ${{ steps.filter.outputs.mobile }}
shared: ${{ steps.filter.outputs.shared }}
workflows: ${{ steps.filter.outputs.workflows }}
...
|
detect-changes
|
null
|
["ubuntu-latest"]
|
1349
|
2
|
1772247215
|
1772247306
|
1772247215
|
1772247306
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
2095
|
1374
|
6
|
5
|
bf252f4083bdceceec242d821c3d37825d51f29f
|
0
|
安装依赖
|
1
|
name: CI
"on":
push:
branches: name: CI
"on":
push:
branches: [main, develop, 'feature/**', 'claude/**']
pull_request:
branches: [main, develop]
env:
NODE_VERSION: "20"
PNPM_VERSION: "8"
jobs:
setup:
name: 安装依赖
runs-on: ubuntu-latest
steps:
- name: 检出代码
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: 缓存 node_modules
uses: actions/cache/save@v4
with:
key: deps-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
path: |
node_modules
backend/node_modules
frontend/node_modules
mobile/node_modules
shared/node_modules
shared/dist
e2e/node_modules
...
|
setup
|
null
|
["ubuntu-latest"]
|
1350
|
2
|
1772247306
|
1772247369
|
1772247215
|
1772247370
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
2096
|
1374
|
6
|
5
|
bf252f4083bdceceec242d821c3d37825d51f29f
|
0
|
共享包检查
|
0
|
name: CI
"on":
push:
branches: name: CI
"on":
push:
branches: [main, develop, 'feature/**', 'claude/**']
pull_request:
branches: [main, develop]
env:
NODE_VERSION: "20"
PNPM_VERSION: "8"
jobs:
shared-check:
name: 共享包检查
runs-on: ubuntu-latest
if: needs.detect-changes.outputs.shared == 'true'
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:
node-version: ${{ env.NODE_VERSION }}
- name: 恢复依赖缓存
uses: actions/cache/restore@v4
with:
key: deps-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
path: |
node_modules
backend/node_modules
frontend/node_modules
mobile/node_modules
shared/node_modules
shared/dist
- name: TypeScript 类型检查
run: pnpm --filter @juhi/shared run build
- name: 运行共享层单元测试
run: pnpm --filter @juhi/shared run test:coverage
- name: 上传共享层覆盖率报告
uses: codecov/codecov-action@v4
with:
fail_ci_if_error: "false"
files: shared/coverage/lcov.info
flags: shared
- name: 验证导出
run: |
cd shared
node -e "import('./dist/index.js').then(m => console.log('✅ 共享包导出验证通过'))"
...
|
shared-check
|
["setup","detect-changes"]
|
["ubuntu-latest"]
|
0
|
4
|
0
|
0
|
1772247215
|
1772247371
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
2097
|
1374
|
6
|
5
|
bf252f4083bdceceec242d821c3d37825d51f29f
|
0
|
后端代码检查
|
0
|
name: CI
"on":
push:
branches: name: CI
"on":
push:
branches: [main, develop, 'feature/**', 'claude/**']
pull_request:
branches: [main, develop]
env:
NODE_VERSION: "20"
PNPM_VERSION: "8"
jobs:
backend-lint:
name: 后端代码检查
runs-on: ubuntu-latest
if: needs.detect-changes.outputs.backend == 'true'
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:
node-version: ${{ env.NODE_VERSION }}
- name: 恢复依赖缓存
uses: actions/cache/restore@v4
with:
key: deps-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
path: |
node_modules
backend/node_modules
frontend/node_modules
mobile/node_modules
shared/node_modules
shared/dist
- name: 生成 Prisma Client
run: pnpm --filter juhi-api run db:generate
- name: ESLint 检查
run: pnpm --filter juhi-api run lint
- name: TypeScript 类型检查
run: pnpm --filter juhi-api run type-check
- name: API 路由契约检查
run: pnpm --filter juhi-api run audit:route-contract
- name: 状态机集成审计
run: pnpm --filter juhi-api run audit:state-machines
- name: 事件发布覆盖审计
run: pnpm --filter juhi-api run audit:events
- name: 审计基线门禁(防回退)
run: pnpm --filter juhi-api run audit:gate
...
|
backend-lint
|
["setup","detect-changes"]
|
["ubuntu-latest"]
|
0
|
4
|
0
|
0
|
1772247215
|
1772247371
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
2098
|
1374
|
6
|
5
|
bf252f4083bdceceec242d821c3d37825d51f29f
|
0
|
后端单元测试
|
0
|
name: CI
"on":
push:
branches: name: CI
"on":
push:
branches: [main, develop, 'feature/**', 'claude/**']
pull_request:
branches: [main, develop]
env:
NODE_VERSION: "20"
PNPM_VERSION: "8"
jobs:
backend-test:
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:
node-version: ${{ env.NODE_VERSION }}
- name: 恢复依赖缓存
uses: actions/cache/restore@v4
with:
key: deps-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
path: |
node_modules
backend/node_modules
frontend/node_modules
mobile/node_modules
shared/node_modules
shared/dist
- name: 生成 Prisma Client
run: pnpm --filter juhi-api run db:generate
- name: 运行单元测试
run: pnpm --filter juhi-api run test:ci
env:
DATABASE_URL: postgresql://test:test@localhost:5432/juhi_test
REDIS_URL: redis://localhost:6379
JWT_SECRET: test-secret-key
NODE_ENV: test
- name: 上传覆盖率报告
uses: codecov/codecov-action@v4
with:
fail_ci_if_error: "false"
files: backend/coverage/lcov.info
flags: backend
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_DB: juhi_test
POSTGRES_PASSWORD: test
POSTGRES_USER: test
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
redis:
image: redis:7-alpine
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
...
|
backend-test
|
["backend-lint"]
|
["ubuntu-latest"]
|
0
|
4
|
0
|
0
|
1772247215
|
1772247371
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
2099
|
1374
|
6
|
5
|
bf252f4083bdceceec242d821c3d37825d51f29f
|
0
|
后端构建检查
|
0
|
name: CI
"on":
push:
branches: name: CI
"on":
push:
branches: [main, develop, 'feature/**', 'claude/**']
pull_request:
branches: [main, develop]
env:
NODE_VERSION: "20"
PNPM_VERSION: "8"
jobs:
backend-build:
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:
node-version: ${{ env.NODE_VERSION }}
- name: 恢复依赖缓存
uses: actions/cache/restore@v4
with:
key: deps-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
path: |
node_modules
backend/node_modules
frontend/node_modules
mobile/node_modules
shared/node_modules
shared/dist
- name: 生成 Prisma Client
run: pnpm --filter juhi-api run db:generate
- name: 构建
run: pnpm --filter juhi-api run build
- name: 验证构建产物
run: |
if [ ! -d "backend/dist" ]; then
echo "❌ 构建产物不存在"
exit 1
fi
echo "✅ 后端构建验证通过"
...
|
backend-build
|
["backend-lint"]
|
["ubuntu-latest"]
|
0
|
4
|
0
|
0
|
1772247215
|
1772247371
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
2100
|
1374
|
6
|
5
|
bf252f4083bdceceec242d821c3d37825d51f29f
|
0
|
前端代码检查
|
0
|
name: CI
"on":
push:
branches: name: CI
"on":
push:
branches: [main, develop, 'feature/**', 'claude/**']
pull_request:
branches: [main, develop]
env:
NODE_VERSION: "20"
PNPM_VERSION: "8"
jobs:
frontend-lint:
name: 前端代码检查
runs-on: ubuntu-latest
if: needs.detect-changes.outputs.frontend == 'true'
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:
node-version: ${{ env.NODE_VERSION }}
- name: 恢复依赖缓存
uses: actions/cache/restore@v4
with:
key: deps-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
path: |
node_modules
backend/node_modules
frontend/node_modules
mobile/node_modules
shared/node_modules
shared/dist
- name: ESLint 检查
run: pnpm --filter juhi-frontend run lint
- name: 数组守卫检查
run: pnpm --filter juhi-frontend run check:array-guard
- name: TypeScript 类型检查
run: pnpm --filter juhi-frontend run type-check
...
|
frontend-lint
|
["setup","detect-changes"]
|
["ubuntu-latest"]
|
0
|
4
|
0
|
0
|
1772247215
|
1772247371
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
2101
|
1374
|
6
|
5
|
bf252f4083bdceceec242d821c3d37825d51f29f
|
0
|
前端构建检查
|
0
|
name: CI
"on":
push:
branches: name: CI
"on":
push:
branches: [main, develop, 'feature/**', 'claude/**']
pull_request:
branches: [main, develop]
env:
NODE_VERSION: "20"
PNPM_VERSION: "8"
jobs:
frontend-build:
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:
node-version: ${{ env.NODE_VERSION }}
- name: 恢复依赖缓存
uses: actions/cache/restore@v4
with:
key: deps-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
path: |
node_modules
backend/node_modules
frontend/node_modules
mobile/node_modules
shared/node_modules
shared/dist
- name: 构建
run: pnpm --filter juhi-frontend run build
- name: 验证构建产物
run: |
if [ ! -d "frontend/dist" ]; then
echo "❌ 构建产物不存在"
exit 1
fi
echo "✅ 前端构建验证通过"
- name: 上传构建产物
uses: actions/upload-artifact@v4
with:
name: frontend-dist
path: frontend/dist
retention-days: "7"
...
|
frontend-build
|
["frontend-lint"]
|
["ubuntu-latest"]
|
0
|
4
|
0
|
0
|
1772247215
|
1772247371
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
2102
|
1374
|
6
|
5
|
bf252f4083bdceceec242d821c3d37825d51f29f
|
0
|
移动端检查
|
0
|
name: CI
"on":
push:
branches: name: CI
"on":
push:
branches: [main, develop, 'feature/**', 'claude/**']
pull_request:
branches: [main, develop]
env:
NODE_VERSION: "20"
PNPM_VERSION: "8"
jobs:
mobile-check:
name: 移动端检查
runs-on: ubuntu-latest
if: needs.detect-changes.outputs.mobile == 'true'
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:
node-version: ${{ env.NODE_VERSION }}
- name: 恢复依赖缓存
uses: actions/cache/restore@v4
with:
key: deps-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
path: |
node_modules
backend/node_modules
frontend/node_modules
mobile/node_modules
shared/node_modules
shared/dist
- name: ESLint 检查
run: pnpm --filter juhi-mobile run lint || true
- name: TypeScript 类型检查
run: pnpm --filter juhi-mobile run type-check || true
...
|
mobile-check
|
["setup","detect-changes"]
|
["ubuntu-latest"]
|
0
|
4
|
0
|
0
|
1772247215
|
1772247371
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
2103
|
1374
|
6
|
5
|
bf252f4083bdceceec242d821c3d37825d51f29f
|
0
|
多租户安全审计
|
0
|
name: CI
"on":
push:
branches: name: CI
"on":
push:
branches: [main, develop, 'feature/**', 'claude/**']
pull_request:
branches: [main, develop]
env:
NODE_VERSION: "20"
PNPM_VERSION: "8"
jobs:
security-audit:
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:
node-version: ${{ env.NODE_VERSION }}
- name: 恢复依赖缓存
uses: actions/cache/restore@v4
with:
key: deps-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
path: |
node_modules
backend/node_modules
frontend/node_modules
mobile/node_modules
shared/node_modules
shared/dist
- name: 生成 Prisma Client
run: pnpm --filter juhi-api run db:generate
- name: 运行多租户安全审计(阻塞性)
run: pnpm --filter juhi-api run audit:tenant
- if: always()
name: 保存安全审计报告
run: |
pnpm --filter juhi-api run audit:tenant:fix --dry-run > security-audit-report.txt 2>&1 || true
- if: always()
name: 上传安全审计报告
uses: actions/upload-artifact@v4
with:
name: security-audit-report
path: security-audit-report.txt
retention-days: "30"
...
|
security-audit
|
["backend-lint"]
|
["ubuntu-latest"]
|
0
|
4
|
0
|
0
|
1772247215
|
1772247371
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
2104
|
1374
|
6
|
5
|
bf252f4083bdceceec242d821c3d37825d51f29f
|
0
|
Kafka 事件一致性审计
|
0
|
name: CI
"on":
push:
branches: name: CI
"on":
push:
branches: [main, develop, 'feature/**', 'claude/**']
pull_request:
branches: [main, develop]
env:
NODE_VERSION: "20"
PNPM_VERSION: "8"
jobs:
kafka-audit:
name: Kafka 事件一致性审计
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:
node-version: ${{ env.NODE_VERSION }}
- name: 恢复依赖缓存
uses: actions/cache/restore@v4
with:
key: deps-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
path: |
node_modules
backend/node_modules
frontend/node_modules
mobile/node_modules
shared/node_modules
shared/dist
- name: 生成 Prisma Client
run: pnpm --filter juhi-api run db:generate
- name: 运行 Kafka 事件一致性审计
run: |
cd backend
npx tsx scripts/audit-kafka-events.ts --ci
continue-on-error: true
- if: always()
name: 保存审计报告
run: |
cd backend
npx tsx scripts/audit-kafka-events.ts --json > kafka-audit-report.json || true
- if: always()
name: 上传审计报告
uses: actions/upload-artifact@v4
with:
name: kafka-audit-report
path: backend/kafka-audit-report.json
retention-days: "30"
...
|
kafka-audit
|
["backend-lint"]
|
["ubuntu-latest"]
|
0
|
4
|
0
|
0
|
1772247215
|
1772247371
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
2105
|
1374
|
6
|
5
|
bf252f4083bdceceec242d821c3d37825d51f29f
|
0
|
依赖安全审计
|
0
|
name: CI
"on":
push:
branches: name: CI
"on":
push:
branches: [main, develop, 'feature/**', 'claude/**']
pull_request:
branches: [main, develop]
env:
NODE_VERSION: "20"
PNPM_VERSION: "8"
jobs:
dependency-audit:
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:
node-version: ${{ env.NODE_VERSION }}
- name: pnpm 依赖审计
run: pnpm audit --audit-level high || true
...
|
dependency-audit
|
["setup"]
|
["ubuntu-latest"]
|
0
|
4
|
0
|
0
|
1772247215
|
1772247371
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
2106
|
1374
|
6
|
5
|
bf252f4083bdceceec242d821c3d37825d51f29f
|
0
|
Docker 镜像构建
|
0
|
name: CI
"on":
push:
branches: name: CI
"on":
push:
branches: [main, develop, 'feature/**', 'claude/**']
pull_request:
branches: [main, develop]
env:
NODE_VERSION: "20"
PNPM_VERSION: "8"
jobs:
docker-build:
name: Docker 镜像构建
runs-on: ubuntu-latest
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop')
steps:
- uses: actions/checkout@v4
- name: 设置 Docker Buildx
uses: docker/setup-buildx-action@v3
- name: 构建后端镜像
uses: docker/build-push-action@v5
with:
cache-from: type=gha
cache-to: type=gha,mode=max
context: ./backend
push: "false"
tags: juhi-api:${{ github.sha }}
- name: 构建前端镜像
uses: docker/build-push-action@v5
with:
cache-from: type=gha
cache-to: type=gha,mode=max
context: ./frontend
push: "false"
tags: juhi-frontend:${{ github.sha }}
...
|
docker-build
|
["backend-build","frontend-build"]
|
["ubuntu-latest"]
|
0
|
4
|
0
|
0
|
1772247215
|
1772247371
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
2107
|
1374
|
6
|
5
|
bf252f4083bdceceec242d821c3d37825d51f29f
|
0
|
E2E 测试
|
0
|
name: CI
"on":
push:
branches: name: CI
"on":
push:
branches: [main, develop, 'feature/**', 'claude/**']
pull_request:
branches: [main, develop]
env:
NODE_VERSION: "20"
PNPM_VERSION: "8"
jobs:
e2e-test:
name: E2E 测试
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
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:
node-version: ${{ env.NODE_VERSION }}
- name: 恢复依赖缓存
uses: actions/cache/restore@v4
with:
key: deps-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
path: |
node_modules
backend/node_modules
frontend/node_modules
mobile/node_modules
shared/node_modules
shared/dist
- name: 生成 Prisma Client
run: pnpm --filter juhi-api run db:generate
- name: 初始化测试数据库
run: |
cd backend
npx prisma migrate deploy
env:
DATABASE_URL: postgresql://test:test@localhost:5432/juhi_e2e
- name: 启动后端服务
run: |
cd backend
pnpm run dev &
sleep 15
env:
DATABASE_URL: postgresql://test:test@localhost:5432/juhi_e2e
REDIS_URL: redis://localhost:6379
JWT_SECRET: e2e-test-secret-key
NODE_ENV: test
PORT: 3000
- name: 后端健康检查
run: |
curl -f http://localhost:3000/health || exit 1
- name: 安装 Playwright
run: |
cd e2e
npx playwright install --with-deps chromium
- name: 运行 E2E 测试
run: |
cd e2e
pnpm run test || true
env:
E2E_BASE_URL: http://localhost:5173
E2E_API_URL: http://localhost:3000
E2E_TEST_USER: admin@juhi.com
E2E_TEST_PASSWORD: Admin@123
- if: always()
name: 上传测试报告
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: e2e/playwright-report
retention-days: "7"
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_DB: juhi_e2e
POSTGRES_PASSWORD: test
POSTGRES_USER: test
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
redis:
image: redis:7-alpine
ports:
- 6379:6379
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
...
|
e2e-test
|
["backend-build","frontend-build"]
|
["ubuntu-latest"]
|
0
|
4
|
0
|
0
|
1772247215
|
1772247371
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
2108
|
1374
|
6
|
5
|
bf252f4083bdceceec242d821c3d37825d51f29f
|
0
|
触发专用测试套件
|
0
|
name: CI
"on":
push:
branches: name: CI
"on":
push:
branches: [main, develop, 'feature/**', 'claude/**']
pull_request:
branches: [main, develop]
env:
NODE_VERSION: "20"
PNPM_VERSION: "8"
jobs:
trigger-test-suite:
name: 触发专用测试套件
runs-on: ubuntu-latest
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop')
steps:
- name: "\U0001F4DD 触发测试套件信息"
run: "echo \"## \U0001F9EA 专用测试工作流\" >> $GITHUB_STEP_SUMMARY\necho \"\" >> $GITHUB_STEP_SUMMARY\necho \"以下专用测试工作流已自动触发:\" >> $GITHUB_STEP_SUMMARY\necho \"\" >> $GITHUB_STEP_SUMMARY\necho \"- **Test Suite** (test.yml): 单元测试、API 测试、状态机测试\" >> $GITHUB_STEP_SUMMARY\necho \"- **E2E Tests** (e2e.yml): 端到端测试、业务流程测试\" >> $GITHUB_STEP_SUMMARY\necho \"\" >> $GITHUB_STEP_SUMMARY\necho \"查看 Actions 页面了解详细测试结果。\" >> $GITHUB_STEP_SUMMARY\n"
...
|
trigger-test-suite
|
["backend-build","frontend-build"]
|
["ubuntu-latest"]
|
0
|
4
|
0
|
0
|
1772247215
|
1772247371
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
2109
|
1374
|
6
|
5
|
bf252f4083bdceceec242d821c3d37825d51f29f
|
0
|
代码质量门控
|
1
|
name: CI
"on":
push:
branches: name: CI
"on":
push:
branches: [main, develop, 'feature/**', 'claude/**']
pull_request:
branches: [main, develop]
env:
NODE_VERSION: "20"
PNPM_VERSION: "8"
jobs:
quality-gate:
name: 代码质量门控
runs-on: ubuntu-latest
if: always()
steps:
- name: 检查所有任务状态
run: "echo \"## \U0001F4CA CI 验证结果\" >> $GITHUB_STEP_SUMMARY\necho \"\" >> $GITHUB_STEP_SUMMARY\necho \"| 检查项 | 状态 |\" >> $GITHUB_STEP_SUMMARY\necho \"|--------|------|\" >> $GITHUB_STEP_SUMMARY\n\n# 后端测试\nif [ \"${{ needs.backend-test.result }}\" == \"success\" ]; then\n echo \"| ✅ 后端测试 | 通过 |\" >> $GITHUB_STEP_SUMMARY\nelif [ \"${{ needs.backend-test.result }}\" == \"skipped\" ]; then\n echo \"| ⏭️ 后端测试 | 跳过 |\" >> $GITHUB_STEP_SUMMARY\nelse\n echo \"| ❌ 后端测试 | 失败 |\" >> $GITHUB_STEP_SUMMARY\nfi\n\n# 后端构建\nif [ \"${{ needs.backend-build.result }}\" == \"success\" ]; then\n echo \"| ✅ 后端构建 | 通过 |\" >> $GITHUB_STEP_SUMMARY\nelif [ \"${{ needs.backend-build.result }}\" == \"skipped\" ]; then\n echo \"| ⏭️ 后端构建 | 跳过 |\" >> $GITHUB_STEP_SUMMARY\nelse\n echo \"| ❌ 后端构建 | 失败 |\" >> $GITHUB_STEP_SUMMARY\nfi\n\n# 前端构建\nif [ \"${{ needs.frontend-build.result }}\" == \"success\" ]; then\n echo \"| ✅ 前端构建 | 通过 |\" >> $GITHUB_STEP_SUMMARY\nelif [ \"${{ needs.frontend-build.result }}\" == \"skipped\" ]; then\n echo \"| ⏭️ 前端构建 | 跳过 |\" >> $GITHUB_STEP_SUMMARY\nelse\n echo \"| ❌ 前端构建 | 失败 |\" >> $GITHUB_STEP_SUMMARY\nfi\n\n# 安全审计(阻塞性 - 2026-02-22 升级)\nif [ \"${{ needs.security-audit.result }}\" == \"success\" ]; then\n echo \"| ✅ 安全审计 | 通过 |\" >> $GITHUB_STEP_SUMMARY\nelif [ \"${{ needs.security-audit.result }}\" == \"skipped\" ]; then\n echo \"| ⏭️ 安全审计 | 跳过 |\" >> $GITHUB_STEP_SUMMARY\nelse\n echo \"| ❌ 安全审计 | 失败(多租户隔离 CRITICAL 问题) |\" >> $GITHUB_STEP_SUMMARY\nfi\n\n# Kafka 审计\nif [ \"${{ needs.kafka-audit.result }}\" == \"success\" ]; then\n echo \"| ✅ Kafka 审计 | 通过 |\" >> $GITHUB_STEP_SUMMARY\nelif [ \"${{ needs.kafka-audit.result }}\" == \"skipped\" ]; then\n echo \"| ⏭️ Kafka 审计 | 跳过 |\" >> $GITHUB_STEP_SUMMARY\nelse\n echo \"| ⚠️ Kafka 审计 | 警告 |\" >> $GITHUB_STEP_SUMMARY\nfi\n"
- name: 验证门控
run: "BACKEND_TEST=\"${{ needs.backend-test.result }}\"\nBACKEND_BUILD=\"${{ needs.backend-build.result }}\"\nFRONTEND_BUILD=\"${{ needs.frontend-build.result }}\"\nSECURITY_AUDIT=\"${{ needs.security-audit.result }}\"\n\n# 跳过的任务视为通过\nif [ \"$BACKEND_BUILD\" == \"failure\" ] || [ \"$FRONTEND_BUILD\" == \"failure\" ]; then\n echo \"❌ 构建失败,代码质量门控未通过\"\n exit 1\nfi\n\nif [ \"$BACKEND_TEST\" == \"failure\" ]; then\n echo \"❌ 测试失败,代码质量门控未通过\"\n exit 1\nfi\n\n# \U0001F512 安全升级(2026-02-22):安全审计失败也阻断 CI\n# 多租户隔离是 P0 红线,CRITICAL 级别问题不允许合并\nif [ \"$SECURITY_AUDIT\" == \"failure\" ]; then\n echo \"❌ 多租户安全审计失败,存在 CRITICAL 级别数据安全风险,代码质量门控未通过\"\n echo \"请运行 'cd backend && npm run audit:tenant' 查看详情,并运行 'npm run audit:tenant:fix' 自动修复\"\n exit 1\nfi\n\necho \"✅ 代码质量门控通过(含安全审计)\"\n"
...
|
quality-gate
|
["backend-test","backend-build", ["backend-test","backend-build","frontend-build","security-audit","kafka-audit"]...
|
["ubuntu-latest"]
|
1352
|
1
|
1772247372
|
1772247372
|
1772247215
|
1772247372
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
2110
|
1374
|
6
|
5
|
bf252f4083bdceceec242d821c3d37825d51f29f
|
0
|
CI 完成通知
|
1
|
name: CI
"on":
push:
branches: name: CI
"on":
push:
branches: [main, develop, 'feature/**', 'claude/**']
pull_request:
branches: [main, develop]
env:
NODE_VERSION: "20"
PNPM_VERSION: "8"
jobs:
ci-complete:
name: CI 完成通知
runs-on: ubuntu-latest
if: always()
steps:
- name: "\U0001F4E2 生成 CI 完成报告"
run: "echo \"## \U0001F389 CI 流程完成\" >> $GITHUB_STEP_SUMMARY\necho \"\" >> $GITHUB_STEP_SUMMARY\necho \"**分支**: ${{ github.ref_name }}\" >> $GITHUB_STEP_SUMMARY\necho \"**提交**: ${{ github.sha }}\" >> $GITHUB_STEP_SUMMARY\necho \"**触发者**: ${{ github.actor }}\" >> $GITHUB_STEP_SUMMARY\necho \"\" >> $GITHUB_STEP_SUMMARY\n\necho \"### \U0001F4CB 任务汇总\" >> $GITHUB_STEP_SUMMARY\necho \"\" >> $GITHUB_STEP_SUMMARY\n\n# 质量门控\nif [ \"${{ needs.quality-gate.result }}\" == \"success\" ]; then\n echo \"- ✅ 代码质量门控: 通过\" >> $GITHUB_STEP_SUMMARY\nelse\n echo \"- ❌ 代码质量门控: 失败\" >> $GITHUB_STEP_SUMMARY\nfi\n\n# E2E 测试\nif [ \"${{ needs.e2e-test.result }}\" == \"success\" ]; then\n echo \"- ✅ E2E 测试: 通过\" >> $GITHUB_STEP_SUMMARY\nelif [ \"${{ needs.e2e-test.result }}\" == \"skipped\" ]; then\n echo \"- ⏭️ E2E 测试: 跳过\" >> $GITHUB_STEP_SUMMARY\nelse\n echo \"- ❌ E2E 测试: 失败\" >> $GITHUB_STEP_SUMMARY\nfi\n\n# Docker 构建\nif [ \"${{ needs.docker-build.result }}\" == \"success\" ]; then\n echo \"- ✅ Docker 镜像构建: 通过\" >> $GITHUB_STEP_SUMMARY\nelif [ \"${{ needs.docker-build.result }}\" == \"skipped\" ]; then\n echo \"- ⏭️ Docker 镜像构建: 跳过\" >> $GITHUB_STEP_SUMMARY\nelse\n echo \"- ❌ Docker 镜像构建: 失败\" >> $GITHUB_STEP_SUMMARY\nfi\n\necho \"\" >> $GITHUB_STEP_SUMMARY\necho \"---\" >> $GITHUB_STEP_SUMMARY\necho \"\" >> $GITHUB_STEP_SUMMARY\necho \"### \U0001F517 相关链接\" >> $GITHUB_STEP_SUMMARY\necho \"\" >> $GITHUB_STEP_SUMMARY\necho \"- [查看完整测试报告](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})\" >> $GITHUB_STEP_SUMMARY\necho \"- [查看覆盖率报告](https://codecov.io/gh/${{ github.repository }})\" >> $GITHUB_STEP_SUMMARY\n"
...
|
ci-complete
|
["quality-gate","e2e-test","do ["quality-gate","e2e-test","docker-build"]...
|
["ubuntu-latest"]
|
1353
|
1
|
1772247376
|
1772247376
|
1772247215
|
1772247376
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
2111
|
1375
|
6
|
5
|
bf252f4083bdceceec242d821c3d37825d51f29f
|
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/v1/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/v1/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"]
|
1351
|
4
|
1772247370
|
1772247370
|
1772247312
|
1772247370
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
2112
|
1376
|
6
|
5
|
bf252f4083bdceceec242d821c3d37825d51f29f
|
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/v1/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/v1/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"]
|
1354
|
4
|
1772247612
|
1772247612
|
1772247612
|
1772247612
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
2116
|
1378
|
6
|
5
|
bf252f4083bdceceec242d821c3d37825d51f29f
|
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/v1/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/v1/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"]
|
1356
|
4
|
1772247913
|
1772247913
|
1772247912
|
1772247913
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
2124
|
1381
|
6
|
5
|
bf252f4083bdceceec242d821c3d37825d51f29f
|
0
|
生产环境健康检查
|
0
|
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/v1/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/v1/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"]
|
0
|
3
|
0
|
1772248512
|
1772248212
|
1772248512
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
2125
|
1382
|
6
|
5
|
bf252f4083bdceceec242d821c3d37825d51f29f
|
0
|
生产环境健康检查
|
0
|
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/v1/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/v1/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"]
|
0
|
3
|
0
|
1772248812
|
1772248512
|
1772248812
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
2126
|
1383
|
6
|
5
|
bf252f4083bdceceec242d821c3d37825d51f29f
|
0
|
生产环境健康检查
|
0
|
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/v1/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/v1/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"]
|
0
|
3
|
0
|
1772249112
|
1772248812
|
1772249112
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
2127
|
1384
|
6
|
5
|
bf252f4083bdceceec242d821c3d37825d51f29f
|
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/v1/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/v1/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"]
|
1360
|
4
|
1772249372
|
1772249372
|
1772249112
|
1772249373
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
2128
|
1385
|
6
|
5
|
bf252f4083bdceceec242d821c3d37825d51f29f
|
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/v1/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/v1/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"]
|
1361
|
4
|
1772249412
|
1772249412
|
1772249412
|
1772249413
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
2129
|
1386
|
6
|
5
|
bf252f4083bdceceec242d821c3d37825d51f29f
|
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/v1/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/v1/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"]
|
1362
|
4
|
1772249712
|
1772249712
|
1772249712
|
1772249713
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
2130
|
1387
|
6
|
5
|
bf252f4083bdceceec242d821c3d37825d51f29f
|
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/v1/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/v1/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"]
|
1363
|
4
|
1772250012
|
1772250012
|
1772250012
|
1772250013
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
2131
|
1388
|
6
|
5
|
bf252f4083bdceceec242d821c3d37825d51f29f
|
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/v1/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/v1/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"]
|
1364
|
4
|
1772250312
|
1772250312
|
1772250312
|
1772250313
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|