|
25289
|
18598
|
6
|
5
|
468c7319f39e251cdf8eb96c3aa63f4db200a7db
|
0
|
部署到 Production
|
1
|
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: "9"
REGISTRY: ghcr.io
jobs:
deploy-production:
name: 部署到 Production
runs-on: ubuntu-latest
if: >-
startsWith(github.ref, 'refs/tags/v') || needs.gate.outputs.target_env == 'production'
steps:
- uses: actions/checkout@v4
- name: 配置 SSH
uses: webfactory/ssh-agent@v0.8.0
with:
ssh-private-key: ${{ secrets.PRODUCTION_SSH_KEY }}
- name: 配置 SSH Known Hosts
run: |
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "${{ secrets.PRODUCTION_SSH_KNOWN_HOSTS }}" >> ~/.ssh/known_hosts
chmod 644 ~/.ssh/known_hosts
- name: 同步部署文件
run: |
rsync -avz --delete \
docker-compose.prod.yml \
scripts/ \
deploy/ \
$USER@$HOST:$DEPLOY_PATH/
env:
HOST: ${{ secrets.PRODUCTION_HOST }}
USER: ${{ secrets.PRODUCTION_USER }}
DEPLOY_PATH: /opt/juhi
- name: 数据库备份
run: |
ssh $USER@$HOST << 'EOF'
cd /opt/juhi
BACKUP_DIR="/opt/juhi/backups"
mkdir -p "$BACKUP_DIR"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/pre_deploy_${TIMESTAMP}.sql"
docker compose -f docker-compose.prod.yml exec -T postgres \
pg_dump -U "${DB_USER:-juhi}" -d "${DB_NAME:-juhi_revops}" -Fc > "$BACKUP_FILE"
if [ $? -eq 0 ]; then
echo "备份完成: $(du -h "$BACKUP_FILE" | cut -f1)"
else
echo "备份失败"
exit 1
fi
find "$BACKUP_DIR" -name "pre_deploy_*.sql" -mtime +30 -delete 2>/dev/null || true
EOF
env:
HOST: ${{ secrets.PRODUCTION_HOST }}
USER: ${{ secrets.PRODUCTION_USER }}
- name: 部署到 Production
run: |
# 通过 ssh 环境变量传递,避免 heredoc 展开泄露 secret
ssh -o SendEnv=DEPLOY_PATH,API_IMAGE,FRONTEND_IMAGE,VERSION $USER@$HOST << 'EOF'
cd "$DEPLOY_PATH"
export API_IMAGE="$API_IMAGE"
export FRONTEND_IMAGE="$FRONTEND_IMAGE"
docker compose -f docker-compose.prod.yml pull api frontend
# 数据库迁移
echo "==> 数据库迁移..."
docker compose -f docker-compose.prod.yml --profile migrate run --rm migrate
if [ $? -ne 0 ]; then
echo "数据库迁移失败"
exit 1
fi
# 记录部署历史
CURRENT_API=$(docker inspect --format='{{.Config.Image}}' juhi-api 2>/dev/null || echo "none")
CURRENT_FE=$(docker inspect --format='{{.Config.Image}}' juhi-frontend 2>/dev/null || echo "none")
echo "$(date -Iseconds)|${CURRENT_API}|${CURRENT_FE}" >> .deploy-history
tail -20 .deploy-history > .deploy-history.tmp && mv .deploy-history.tmp .deploy-history
# 蓝绿部署
docker compose -f docker-compose.prod.yml up -d --no-deps --scale api=2 api
RETRY=0
MAX_RETRY=15
until curl -sf http://localhost:3000/health > /dev/null 2>&1; do
RETRY=$((RETRY + 1))
if [ $RETRY -ge $MAX_RETRY ]; then
echo "健康检查超时,回滚到单实例"
docker compose -f docker-compose.prod.yml up -d --no-deps --scale api=1 api
exit 1
fi
sleep 5
done
docker compose -f docker-compose.prod.yml up -d --no-deps --scale api=1 api
docker compose -f docker-compose.prod.yml up -d --no-deps frontend
docker compose -f docker-compose.prod.yml exec -T nginx nginx -s reload 2>/dev/null || true
docker image prune -f
echo "$VERSION" > .deployed_version
echo "==> Production 部署完成: $VERSION"
EOF
env:
HOST: ${{ secrets.PRODUCTION_HOST }}
USER: ${{ secrets.PRODUCTION_USER }}
DEPLOY_PATH: /opt/juhi
API_IMAGE: ${{ needs.build-and-push.outputs.api-image }}
FRONTEND_IMAGE: ${{ needs.build-and-push.outputs.frontend-image }}
VERSION: ${{ needs.build-and-push.outputs.version }}
- name: Production 部署验证
run: |
ssh $USER@$HOST << 'EOF'
cd /opt/juhi
if [ -f "./scripts/post-deploy-verify.sh" ]; then
chmod +x ./scripts/post-deploy-verify.sh
./scripts/post-deploy-verify.sh --quick || exit 1
else
curl -sf http://localhost:3000/health || exit 1
UNHEALTHY=$(docker compose -f docker-compose.prod.yml ps --format json | grep -c '"unhealthy"' || true)
if [ "$UNHEALTHY" -gt 0 ]; then
docker compose -f docker-compose.prod.yml ps
exit 1
fi
echo "Production 验证通过"
fi
EOF
env:
HOST: ${{ secrets.PRODUCTION_HOST }}
USER: ${{ secrets.PRODUCTION_USER }}
- if: startsWith(github.ref, 'refs/tags/v')
name: 创建 GitHub Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: "true"
make_latest: "true"
timeout-minutes: "20"
permissions:
contents: write # 创建 GitHub Release 需要 write 权限
...
|
deploy-production
|
["gate","build-and-push","depl ["gate","build-and-push","deploy-staging"]...
|
["ubuntu-latest"]
|
21637
|
4
|
1777440718
|
1777440718
|
1777440703
|
1777440719
|
|
1
|
|
0
|
Edit
Delete
|
|
29526
|
22450
|
6
|
5
|
cf8ca525a24f7e8f7f02481cf1c6e72f68c3788a
|
0
|
部署到 Production
|
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: "9"
REGISTRY: ghcr.io
jobs:
deploy-production:
name: 部署到 Production
runs-on: ubuntu-latest
if: >-
startsWith(github.ref, 'refs/tags/v') || needs.gate.outputs.target_env == 'production'
steps:
- uses: actions/checkout@v4
- name: 配置 SSH
uses: webfactory/ssh-agent@v0.8.0
with:
ssh-private-key: ${{ secrets.PRODUCTION_SSH_KEY }}
- name: 配置 SSH Known Hosts
run: |
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "${{ secrets.PRODUCTION_SSH_KNOWN_HOSTS }}" >> ~/.ssh/known_hosts
chmod 644 ~/.ssh/known_hosts
- name: 同步部署文件
run: |
rsync -avz --delete \
docker-compose.prod.yml \
scripts/ \
deploy/ \
$USER@$HOST:$DEPLOY_PATH/
env:
HOST: ${{ secrets.PRODUCTION_HOST }}
USER: ${{ secrets.PRODUCTION_USER }}
DEPLOY_PATH: /opt/juhi
- name: 数据库备份
run: |
ssh $USER@$HOST << 'EOF'
cd /opt/juhi
BACKUP_DIR="/opt/juhi/backups"
mkdir -p "$BACKUP_DIR"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/pre_deploy_${TIMESTAMP}.sql"
docker compose -f docker-compose.prod.yml exec -T postgres \
pg_dump -U "${DB_USER:-juhi}" -d "${DB_NAME:-juhi_revops}" -Fc > "$BACKUP_FILE"
if [ $? -eq 0 ]; then
echo "备份完成: $(du -h "$BACKUP_FILE" | cut -f1)"
else
echo "备份失败"
exit 1
fi
find "$BACKUP_DIR" -name "pre_deploy_*.sql" -mtime +30 -delete 2>/dev/null || true
EOF
env:
HOST: ${{ secrets.PRODUCTION_HOST }}
USER: ${{ secrets.PRODUCTION_USER }}
- name: 部署到 Production
run: |
# 通过 ssh 环境变量传递,避免 heredoc 展开泄露 secret
ssh -o SendEnv=DEPLOY_PATH,API_IMAGE,FRONTEND_IMAGE,VERSION $USER@$HOST << 'EOF'
cd "$DEPLOY_PATH"
export API_IMAGE="$API_IMAGE"
export FRONTEND_IMAGE="$FRONTEND_IMAGE"
docker compose -f docker-compose.prod.yml pull api frontend
# 数据库迁移
echo "==> 数据库迁移..."
docker compose -f docker-compose.prod.yml --profile migrate run --rm migrate
if [ $? -ne 0 ]; then
echo "数据库迁移失败"
exit 1
fi
# 记录部署历史
CURRENT_API=$(docker inspect --format='{{.Config.Image}}' juhi-api 2>/dev/null || echo "none")
CURRENT_FE=$(docker inspect --format='{{.Config.Image}}' juhi-frontend 2>/dev/null || echo "none")
echo "$(date -Iseconds)|${CURRENT_API}|${CURRENT_FE}" >> .deploy-history
tail -20 .deploy-history > .deploy-history.tmp && mv .deploy-history.tmp .deploy-history
# 蓝绿部署
docker compose -f docker-compose.prod.yml up -d --no-deps --scale api=2 api
RETRY=0
MAX_RETRY=15
until curl -sf http://localhost:3000/health > /dev/null 2>&1; do
RETRY=$((RETRY + 1))
if [ $RETRY -ge $MAX_RETRY ]; then
echo "健康检查超时,回滚到单实例"
docker compose -f docker-compose.prod.yml up -d --no-deps --scale api=1 api
exit 1
fi
sleep 5
done
docker compose -f docker-compose.prod.yml up -d --no-deps --scale api=1 api
docker compose -f docker-compose.prod.yml up -d --no-deps frontend
docker compose -f docker-compose.prod.yml exec -T nginx nginx -s reload 2>/dev/null || true
docker image prune -f
echo "$VERSION" > .deployed_version
echo "==> Production 部署完成: $VERSION"
EOF
env:
HOST: ${{ secrets.PRODUCTION_HOST }}
USER: ${{ secrets.PRODUCTION_USER }}
DEPLOY_PATH: /opt/juhi
API_IMAGE: ${{ needs.build-and-push.outputs.api-image }}
FRONTEND_IMAGE: ${{ needs.build-and-push.outputs.frontend-image }}
VERSION: ${{ needs.build-and-push.outputs.version }}
- name: Production 部署验证
run: |
ssh $USER@$HOST << 'EOF'
cd /opt/juhi
if [ -f "./scripts/post-deploy-verify.sh" ]; then
chmod +x ./scripts/post-deploy-verify.sh
./scripts/post-deploy-verify.sh --quick || exit 1
else
curl -sf http://localhost:3000/health || exit 1
UNHEALTHY=$(docker compose -f docker-compose.prod.yml ps --format json | grep -c '"unhealthy"' || true)
if [ "$UNHEALTHY" -gt 0 ]; then
docker compose -f docker-compose.prod.yml ps
exit 1
fi
echo "Production 验证通过"
fi
EOF
env:
HOST: ${{ secrets.PRODUCTION_HOST }}
USER: ${{ secrets.PRODUCTION_USER }}
- if: startsWith(github.ref, 'refs/tags/v')
name: 创建 GitHub Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: "true"
make_latest: "true"
timeout-minutes: "20"
permissions:
contents: write # 创建 GitHub Release 需要 write 权限
...
|
deploy-production
|
["gate","build-and-push","depl ["gate","build-and-push","deploy-staging"]...
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1778609625
|
1778609520
|
1778609625
|
|
0
|
|
0
|
Edit
Delete
|
|
29540
|
22452
|
6
|
5
|
cf8ca525a24f7e8f7f02481cf1c6e72f68c3788a
|
0
|
部署到 Production
|
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: "9"
REGISTRY: ghcr.io
jobs:
deploy-production:
name: 部署到 Production
runs-on: ubuntu-latest
if: >-
startsWith(github.ref, 'refs/tags/v') || needs.gate.outputs.target_env == 'production'
steps:
- uses: actions/checkout@v4
- name: 配置 SSH
uses: webfactory/ssh-agent@v0.8.0
with:
ssh-private-key: ${{ secrets.PRODUCTION_SSH_KEY }}
- name: 配置 SSH Known Hosts
run: |
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "${{ secrets.PRODUCTION_SSH_KNOWN_HOSTS }}" >> ~/.ssh/known_hosts
chmod 644 ~/.ssh/known_hosts
- name: 同步部署文件
run: |
rsync -avz --delete \
docker-compose.prod.yml \
scripts/ \
deploy/ \
$USER@$HOST:$DEPLOY_PATH/
env:
HOST: ${{ secrets.PRODUCTION_HOST }}
USER: ${{ secrets.PRODUCTION_USER }}
DEPLOY_PATH: /opt/juhi
- name: 数据库备份
run: |
ssh $USER@$HOST << 'EOF'
cd /opt/juhi
BACKUP_DIR="/opt/juhi/backups"
mkdir -p "$BACKUP_DIR"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/pre_deploy_${TIMESTAMP}.sql"
docker compose -f docker-compose.prod.yml exec -T postgres \
pg_dump -U "${DB_USER:-juhi}" -d "${DB_NAME:-juhi_revops}" -Fc > "$BACKUP_FILE"
if [ $? -eq 0 ]; then
echo "备份完成: $(du -h "$BACKUP_FILE" | cut -f1)"
else
echo "备份失败"
exit 1
fi
find "$BACKUP_DIR" -name "pre_deploy_*.sql" -mtime +30 -delete 2>/dev/null || true
EOF
env:
HOST: ${{ secrets.PRODUCTION_HOST }}
USER: ${{ secrets.PRODUCTION_USER }}
- name: 部署到 Production
run: |
# 通过 ssh 环境变量传递,避免 heredoc 展开泄露 secret
ssh -o SendEnv=DEPLOY_PATH,API_IMAGE,FRONTEND_IMAGE,VERSION $USER@$HOST << 'EOF'
cd "$DEPLOY_PATH"
export API_IMAGE="$API_IMAGE"
export FRONTEND_IMAGE="$FRONTEND_IMAGE"
docker compose -f docker-compose.prod.yml pull api frontend
# 数据库迁移
echo "==> 数据库迁移..."
docker compose -f docker-compose.prod.yml --profile migrate run --rm migrate
if [ $? -ne 0 ]; then
echo "数据库迁移失败"
exit 1
fi
# 记录部署历史
CURRENT_API=$(docker inspect --format='{{.Config.Image}}' juhi-api 2>/dev/null || echo "none")
CURRENT_FE=$(docker inspect --format='{{.Config.Image}}' juhi-frontend 2>/dev/null || echo "none")
echo "$(date -Iseconds)|${CURRENT_API}|${CURRENT_FE}" >> .deploy-history
tail -20 .deploy-history > .deploy-history.tmp && mv .deploy-history.tmp .deploy-history
# 蓝绿部署
docker compose -f docker-compose.prod.yml up -d --no-deps --scale api=2 api
RETRY=0
MAX_RETRY=15
until curl -sf http://localhost:3000/health > /dev/null 2>&1; do
RETRY=$((RETRY + 1))
if [ $RETRY -ge $MAX_RETRY ]; then
echo "健康检查超时,回滚到单实例"
docker compose -f docker-compose.prod.yml up -d --no-deps --scale api=1 api
exit 1
fi
sleep 5
done
docker compose -f docker-compose.prod.yml up -d --no-deps --scale api=1 api
docker compose -f docker-compose.prod.yml up -d --no-deps frontend
docker compose -f docker-compose.prod.yml exec -T nginx nginx -s reload 2>/dev/null || true
docker image prune -f
echo "$VERSION" > .deployed_version
echo "==> Production 部署完成: $VERSION"
EOF
env:
HOST: ${{ secrets.PRODUCTION_HOST }}
USER: ${{ secrets.PRODUCTION_USER }}
DEPLOY_PATH: /opt/juhi
API_IMAGE: ${{ needs.build-and-push.outputs.api-image }}
FRONTEND_IMAGE: ${{ needs.build-and-push.outputs.frontend-image }}
VERSION: ${{ needs.build-and-push.outputs.version }}
- name: Production 部署验证
run: |
ssh $USER@$HOST << 'EOF'
cd /opt/juhi
if [ -f "./scripts/post-deploy-verify.sh" ]; then
chmod +x ./scripts/post-deploy-verify.sh
./scripts/post-deploy-verify.sh --quick || exit 1
else
curl -sf http://localhost:3000/health || exit 1
UNHEALTHY=$(docker compose -f docker-compose.prod.yml ps --format json | grep -c '"unhealthy"' || true)
if [ "$UNHEALTHY" -gt 0 ]; then
docker compose -f docker-compose.prod.yml ps
exit 1
fi
echo "Production 验证通过"
fi
EOF
env:
HOST: ${{ secrets.PRODUCTION_HOST }}
USER: ${{ secrets.PRODUCTION_USER }}
- if: startsWith(github.ref, 'refs/tags/v')
name: 创建 GitHub Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: "true"
make_latest: "true"
timeout-minutes: "20"
permissions:
contents: write # 创建 GitHub Release 需要 write 权限
...
|
deploy-production
|
["gate","build-and-push","depl ["gate","build-and-push","deploy-staging"]...
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1778609633
|
1778609627
|
1778609633
|
|
0
|
|
0
|
Edit
Delete
|
|
29550
|
22453
|
6
|
5
|
cf8ca525a24f7e8f7f02481cf1c6e72f68c3788a
|
0
|
部署到 Production
|
1
|
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: "9"
REGISTRY: ghcr.io
jobs:
deploy-production:
name: 部署到 Production
runs-on: ubuntu-latest
if: >-
startsWith(github.ref, 'refs/tags/v') || needs.gate.outputs.target_env == 'production'
steps:
- uses: actions/checkout@v4
- name: 配置 SSH
uses: webfactory/ssh-agent@v0.8.0
with:
ssh-private-key: ${{ secrets.PRODUCTION_SSH_KEY }}
- name: 配置 SSH Known Hosts
run: |
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "${{ secrets.PRODUCTION_SSH_KNOWN_HOSTS }}" >> ~/.ssh/known_hosts
chmod 644 ~/.ssh/known_hosts
- name: 同步部署文件
run: |
rsync -avz --delete \
docker-compose.prod.yml \
scripts/ \
deploy/ \
$USER@$HOST:$DEPLOY_PATH/
env:
HOST: ${{ secrets.PRODUCTION_HOST }}
USER: ${{ secrets.PRODUCTION_USER }}
DEPLOY_PATH: /opt/juhi
- name: 数据库备份
run: |
ssh $USER@$HOST << 'EOF'
cd /opt/juhi
BACKUP_DIR="/opt/juhi/backups"
mkdir -p "$BACKUP_DIR"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/pre_deploy_${TIMESTAMP}.sql"
docker compose -f docker-compose.prod.yml exec -T postgres \
pg_dump -U "${DB_USER:-juhi}" -d "${DB_NAME:-juhi_revops}" -Fc > "$BACKUP_FILE"
if [ $? -eq 0 ]; then
echo "备份完成: $(du -h "$BACKUP_FILE" | cut -f1)"
else
echo "备份失败"
exit 1
fi
find "$BACKUP_DIR" -name "pre_deploy_*.sql" -mtime +30 -delete 2>/dev/null || true
EOF
env:
HOST: ${{ secrets.PRODUCTION_HOST }}
USER: ${{ secrets.PRODUCTION_USER }}
- name: 部署到 Production
run: |
# 通过 ssh 环境变量传递,避免 heredoc 展开泄露 secret
ssh -o SendEnv=DEPLOY_PATH,API_IMAGE,FRONTEND_IMAGE,VERSION $USER@$HOST << 'EOF'
cd "$DEPLOY_PATH"
export API_IMAGE="$API_IMAGE"
export FRONTEND_IMAGE="$FRONTEND_IMAGE"
docker compose -f docker-compose.prod.yml pull api frontend
# 数据库迁移
echo "==> 数据库迁移..."
docker compose -f docker-compose.prod.yml --profile migrate run --rm migrate
if [ $? -ne 0 ]; then
echo "数据库迁移失败"
exit 1
fi
# 记录部署历史
CURRENT_API=$(docker inspect --format='{{.Config.Image}}' juhi-api 2>/dev/null || echo "none")
CURRENT_FE=$(docker inspect --format='{{.Config.Image}}' juhi-frontend 2>/dev/null || echo "none")
echo "$(date -Iseconds)|${CURRENT_API}|${CURRENT_FE}" >> .deploy-history
tail -20 .deploy-history > .deploy-history.tmp && mv .deploy-history.tmp .deploy-history
# 蓝绿部署
docker compose -f docker-compose.prod.yml up -d --no-deps --scale api=2 api
RETRY=0
MAX_RETRY=15
until curl -sf http://localhost:3000/health > /dev/null 2>&1; do
RETRY=$((RETRY + 1))
if [ $RETRY -ge $MAX_RETRY ]; then
echo "健康检查超时,回滚到单实例"
docker compose -f docker-compose.prod.yml up -d --no-deps --scale api=1 api
exit 1
fi
sleep 5
done
docker compose -f docker-compose.prod.yml up -d --no-deps --scale api=1 api
docker compose -f docker-compose.prod.yml up -d --no-deps frontend
docker compose -f docker-compose.prod.yml exec -T nginx nginx -s reload 2>/dev/null || true
docker image prune -f
echo "$VERSION" > .deployed_version
echo "==> Production 部署完成: $VERSION"
EOF
env:
HOST: ${{ secrets.PRODUCTION_HOST }}
USER: ${{ secrets.PRODUCTION_USER }}
DEPLOY_PATH: /opt/juhi
API_IMAGE: ${{ needs.build-and-push.outputs.api-image }}
FRONTEND_IMAGE: ${{ needs.build-and-push.outputs.frontend-image }}
VERSION: ${{ needs.build-and-push.outputs.version }}
- name: Production 部署验证
run: |
ssh $USER@$HOST << 'EOF'
cd /opt/juhi
if [ -f "./scripts/post-deploy-verify.sh" ]; then
chmod +x ./scripts/post-deploy-verify.sh
./scripts/post-deploy-verify.sh --quick || exit 1
else
curl -sf http://localhost:3000/health || exit 1
UNHEALTHY=$(docker compose -f docker-compose.prod.yml ps --format json | grep -c '"unhealthy"' || true)
if [ "$UNHEALTHY" -gt 0 ]; then
docker compose -f docker-compose.prod.yml ps
exit 1
fi
echo "Production 验证通过"
fi
EOF
env:
HOST: ${{ secrets.PRODUCTION_HOST }}
USER: ${{ secrets.PRODUCTION_USER }}
- if: startsWith(github.ref, 'refs/tags/v')
name: 创建 GitHub Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: "true"
make_latest: "true"
timeout-minutes: "20"
permissions:
contents: write # 创建 GitHub Release 需要 write 权限
...
|
deploy-production
|
["gate","build-and-push","depl ["gate","build-and-push","deploy-staging"]...
|
["ubuntu-latest"]
|
25799
|
4
|
1778609650
|
1778609650
|
1778609635
|
1778609650
|
|
1
|
|
0
|
Edit
Delete
|
|
29738
|
22522
|
6
|
5
|
534799e0b1707368df37aff7b3df545a6ca1064a
|
0
|
部署到 Production
|
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: "9"
REGISTRY: ghcr.io
jobs:
deploy-production:
name: 部署到 Production
runs-on: ubuntu-latest
if: >-
startsWith(github.ref, 'refs/tags/v') || needs.gate.outputs.target_env == 'production'
steps:
- uses: actions/checkout@v4
- name: 配置 SSH
uses: webfactory/ssh-agent@v0.8.0
with:
ssh-private-key: ${{ secrets.PRODUCTION_SSH_KEY }}
- name: 配置 SSH Known Hosts
run: |
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "${{ secrets.PRODUCTION_SSH_KNOWN_HOSTS }}" >> ~/.ssh/known_hosts
chmod 644 ~/.ssh/known_hosts
- name: 同步部署文件
run: |
rsync -avz --delete \
docker-compose.prod.yml \
scripts/ \
deploy/ \
$USER@$HOST:$DEPLOY_PATH/
env:
HOST: ${{ secrets.PRODUCTION_HOST }}
USER: ${{ secrets.PRODUCTION_USER }}
DEPLOY_PATH: /opt/juhi
- name: 数据库备份
run: |
ssh $USER@$HOST << 'EOF'
cd /opt/juhi
BACKUP_DIR="/opt/juhi/backups"
mkdir -p "$BACKUP_DIR"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/pre_deploy_${TIMESTAMP}.sql"
docker compose -f docker-compose.prod.yml exec -T postgres \
pg_dump -U "${DB_USER:-juhi}" -d "${DB_NAME:-juhi_revops}" -Fc > "$BACKUP_FILE"
if [ $? -eq 0 ]; then
echo "备份完成: $(du -h "$BACKUP_FILE" | cut -f1)"
else
echo "备份失败"
exit 1
fi
find "$BACKUP_DIR" -name "pre_deploy_*.sql" -mtime +30 -delete 2>/dev/null || true
EOF
env:
HOST: ${{ secrets.PRODUCTION_HOST }}
USER: ${{ secrets.PRODUCTION_USER }}
- name: 部署到 Production
run: |
# 通过 ssh 环境变量传递,避免 heredoc 展开泄露 secret
ssh -o SendEnv=DEPLOY_PATH,API_IMAGE,FRONTEND_IMAGE,VERSION $USER@$HOST << 'EOF'
cd "$DEPLOY_PATH"
export API_IMAGE="$API_IMAGE"
export FRONTEND_IMAGE="$FRONTEND_IMAGE"
docker compose -f docker-compose.prod.yml pull api frontend
# 数据库迁移
echo "==> 数据库迁移..."
docker compose -f docker-compose.prod.yml --profile migrate run --rm migrate
if [ $? -ne 0 ]; then
echo "数据库迁移失败"
exit 1
fi
# 记录部署历史
CURRENT_API=$(docker inspect --format='{{.Config.Image}}' juhi-api 2>/dev/null || echo "none")
CURRENT_FE=$(docker inspect --format='{{.Config.Image}}' juhi-frontend 2>/dev/null || echo "none")
echo "$(date -Iseconds)|${CURRENT_API}|${CURRENT_FE}" >> .deploy-history
tail -20 .deploy-history > .deploy-history.tmp && mv .deploy-history.tmp .deploy-history
# 蓝绿部署
docker compose -f docker-compose.prod.yml up -d --no-deps --scale api=2 api
RETRY=0
MAX_RETRY=15
until curl -sf http://localhost:3000/health > /dev/null 2>&1; do
RETRY=$((RETRY + 1))
if [ $RETRY -ge $MAX_RETRY ]; then
echo "健康检查超时,回滚到单实例"
docker compose -f docker-compose.prod.yml up -d --no-deps --scale api=1 api
exit 1
fi
sleep 5
done
docker compose -f docker-compose.prod.yml up -d --no-deps --scale api=1 api
docker compose -f docker-compose.prod.yml up -d --no-deps frontend
docker compose -f docker-compose.prod.yml exec -T nginx nginx -s reload 2>/dev/null || true
docker image prune -f
echo "$VERSION" > .deployed_version
echo "==> Production 部署完成: $VERSION"
EOF
env:
HOST: ${{ secrets.PRODUCTION_HOST }}
USER: ${{ secrets.PRODUCTION_USER }}
DEPLOY_PATH: /opt/juhi
API_IMAGE: ${{ needs.build-and-push.outputs.api-image }}
FRONTEND_IMAGE: ${{ needs.build-and-push.outputs.frontend-image }}
VERSION: ${{ needs.build-and-push.outputs.version }}
- name: Production 部署验证
run: |
ssh $USER@$HOST << 'EOF'
cd /opt/juhi
if [ -f "./scripts/post-deploy-verify.sh" ]; then
chmod +x ./scripts/post-deploy-verify.sh
./scripts/post-deploy-verify.sh --quick || exit 1
else
curl -sf http://localhost:3000/health || exit 1
UNHEALTHY=$(docker compose -f docker-compose.prod.yml ps --format json | grep -c '"unhealthy"' || true)
if [ "$UNHEALTHY" -gt 0 ]; then
docker compose -f docker-compose.prod.yml ps
exit 1
fi
echo "Production 验证通过"
fi
EOF
env:
HOST: ${{ secrets.PRODUCTION_HOST }}
USER: ${{ secrets.PRODUCTION_USER }}
- if: startsWith(github.ref, 'refs/tags/v')
name: 创建 GitHub Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: "true"
make_latest: "true"
timeout-minutes: "20"
permissions:
contents: write # 创建 GitHub Release 需要 write 权限
...
|
deploy-production
|
["gate","build-and-push","depl ["gate","build-and-push","deploy-staging"]...
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1778643745
|
1778639391
|
1778643745
|
|
0
|
|
0
|
Edit
Delete
|
|
29760
|
22532
|
6
|
5
|
534799e0b1707368df37aff7b3df545a6ca1064a
|
0
|
部署到 Production
|
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: "9"
REGISTRY: ghcr.io
jobs:
deploy-production:
name: 部署到 Production
runs-on: ubuntu-latest
if: >-
startsWith(github.ref, 'refs/tags/v') || needs.gate.outputs.target_env == 'production'
steps:
- uses: actions/checkout@v4
- name: 配置 SSH
uses: webfactory/ssh-agent@v0.8.0
with:
ssh-private-key: ${{ secrets.PRODUCTION_SSH_KEY }}
- name: 配置 SSH Known Hosts
run: |
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "${{ secrets.PRODUCTION_SSH_KNOWN_HOSTS }}" >> ~/.ssh/known_hosts
chmod 644 ~/.ssh/known_hosts
- name: 同步部署文件
run: |
rsync -avz --delete \
docker-compose.prod.yml \
scripts/ \
deploy/ \
$USER@$HOST:$DEPLOY_PATH/
env:
HOST: ${{ secrets.PRODUCTION_HOST }}
USER: ${{ secrets.PRODUCTION_USER }}
DEPLOY_PATH: /opt/juhi
- name: 数据库备份
run: |
ssh $USER@$HOST << 'EOF'
cd /opt/juhi
BACKUP_DIR="/opt/juhi/backups"
mkdir -p "$BACKUP_DIR"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/pre_deploy_${TIMESTAMP}.sql"
docker compose -f docker-compose.prod.yml exec -T postgres \
pg_dump -U "${DB_USER:-juhi}" -d "${DB_NAME:-juhi_revops}" -Fc > "$BACKUP_FILE"
if [ $? -eq 0 ]; then
echo "备份完成: $(du -h "$BACKUP_FILE" | cut -f1)"
else
echo "备份失败"
exit 1
fi
find "$BACKUP_DIR" -name "pre_deploy_*.sql" -mtime +30 -delete 2>/dev/null || true
EOF
env:
HOST: ${{ secrets.PRODUCTION_HOST }}
USER: ${{ secrets.PRODUCTION_USER }}
- name: 部署到 Production
run: |
# 通过 ssh 环境变量传递,避免 heredoc 展开泄露 secret
ssh -o SendEnv=DEPLOY_PATH,API_IMAGE,FRONTEND_IMAGE,VERSION $USER@$HOST << 'EOF'
cd "$DEPLOY_PATH"
export API_IMAGE="$API_IMAGE"
export FRONTEND_IMAGE="$FRONTEND_IMAGE"
docker compose -f docker-compose.prod.yml pull api frontend
# 数据库迁移
echo "==> 数据库迁移..."
docker compose -f docker-compose.prod.yml --profile migrate run --rm migrate
if [ $? -ne 0 ]; then
echo "数据库迁移失败"
exit 1
fi
# 记录部署历史
CURRENT_API=$(docker inspect --format='{{.Config.Image}}' juhi-api 2>/dev/null || echo "none")
CURRENT_FE=$(docker inspect --format='{{.Config.Image}}' juhi-frontend 2>/dev/null || echo "none")
echo "$(date -Iseconds)|${CURRENT_API}|${CURRENT_FE}" >> .deploy-history
tail -20 .deploy-history > .deploy-history.tmp && mv .deploy-history.tmp .deploy-history
# 蓝绿部署
docker compose -f docker-compose.prod.yml up -d --no-deps --scale api=2 api
RETRY=0
MAX_RETRY=15
until curl -sf http://localhost:3000/health > /dev/null 2>&1; do
RETRY=$((RETRY + 1))
if [ $RETRY -ge $MAX_RETRY ]; then
echo "健康检查超时,回滚到单实例"
docker compose -f docker-compose.prod.yml up -d --no-deps --scale api=1 api
exit 1
fi
sleep 5
done
docker compose -f docker-compose.prod.yml up -d --no-deps --scale api=1 api
docker compose -f docker-compose.prod.yml up -d --no-deps frontend
docker compose -f docker-compose.prod.yml exec -T nginx nginx -s reload 2>/dev/null || true
docker image prune -f
echo "$VERSION" > .deployed_version
echo "==> Production 部署完成: $VERSION"
EOF
env:
HOST: ${{ secrets.PRODUCTION_HOST }}
USER: ${{ secrets.PRODUCTION_USER }}
DEPLOY_PATH: /opt/juhi
API_IMAGE: ${{ needs.build-and-push.outputs.api-image }}
FRONTEND_IMAGE: ${{ needs.build-and-push.outputs.frontend-image }}
VERSION: ${{ needs.build-and-push.outputs.version }}
- name: Production 部署验证
run: |
ssh $USER@$HOST << 'EOF'
cd /opt/juhi
if [ -f "./scripts/post-deploy-verify.sh" ]; then
chmod +x ./scripts/post-deploy-verify.sh
./scripts/post-deploy-verify.sh --quick || exit 1
else
curl -sf http://localhost:3000/health || exit 1
UNHEALTHY=$(docker compose -f docker-compose.prod.yml ps --format json | grep -c '"unhealthy"' || true)
if [ "$UNHEALTHY" -gt 0 ]; then
docker compose -f docker-compose.prod.yml ps
exit 1
fi
echo "Production 验证通过"
fi
EOF
env:
HOST: ${{ secrets.PRODUCTION_HOST }}
USER: ${{ secrets.PRODUCTION_USER }}
- if: startsWith(github.ref, 'refs/tags/v')
name: 创建 GitHub Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: "true"
make_latest: "true"
timeout-minutes: "20"
permissions:
contents: write # 创建 GitHub Release 需要 write 权限
...
|
deploy-production
|
["gate","build-and-push","depl ["gate","build-and-push","deploy-staging"]...
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1778643758
|
1778643746
|
1778643758
|
|
0
|
|
0
|
Edit
Delete
|
|
29770
|
22533
|
6
|
5
|
534799e0b1707368df37aff7b3df545a6ca1064a
|
0
|
部署到 Production
|
1
|
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: "9"
REGISTRY: ghcr.io
jobs:
deploy-production:
name: 部署到 Production
runs-on: ubuntu-latest
if: >-
startsWith(github.ref, 'refs/tags/v') || needs.gate.outputs.target_env == 'production'
steps:
- uses: actions/checkout@v4
- name: 配置 SSH
uses: webfactory/ssh-agent@v0.8.0
with:
ssh-private-key: ${{ secrets.PRODUCTION_SSH_KEY }}
- name: 配置 SSH Known Hosts
run: |
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "${{ secrets.PRODUCTION_SSH_KNOWN_HOSTS }}" >> ~/.ssh/known_hosts
chmod 644 ~/.ssh/known_hosts
- name: 同步部署文件
run: |
rsync -avz --delete \
docker-compose.prod.yml \
scripts/ \
deploy/ \
$USER@$HOST:$DEPLOY_PATH/
env:
HOST: ${{ secrets.PRODUCTION_HOST }}
USER: ${{ secrets.PRODUCTION_USER }}
DEPLOY_PATH: /opt/juhi
- name: 数据库备份
run: |
ssh $USER@$HOST << 'EOF'
cd /opt/juhi
BACKUP_DIR="/opt/juhi/backups"
mkdir -p "$BACKUP_DIR"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/pre_deploy_${TIMESTAMP}.sql"
docker compose -f docker-compose.prod.yml exec -T postgres \
pg_dump -U "${DB_USER:-juhi}" -d "${DB_NAME:-juhi_revops}" -Fc > "$BACKUP_FILE"
if [ $? -eq 0 ]; then
echo "备份完成: $(du -h "$BACKUP_FILE" | cut -f1)"
else
echo "备份失败"
exit 1
fi
find "$BACKUP_DIR" -name "pre_deploy_*.sql" -mtime +30 -delete 2>/dev/null || true
EOF
env:
HOST: ${{ secrets.PRODUCTION_HOST }}
USER: ${{ secrets.PRODUCTION_USER }}
- name: 部署到 Production
run: |
# 通过 ssh 环境变量传递,避免 heredoc 展开泄露 secret
ssh -o SendEnv=DEPLOY_PATH,API_IMAGE,FRONTEND_IMAGE,VERSION $USER@$HOST << 'EOF'
cd "$DEPLOY_PATH"
export API_IMAGE="$API_IMAGE"
export FRONTEND_IMAGE="$FRONTEND_IMAGE"
docker compose -f docker-compose.prod.yml pull api frontend
# 数据库迁移
echo "==> 数据库迁移..."
docker compose -f docker-compose.prod.yml --profile migrate run --rm migrate
if [ $? -ne 0 ]; then
echo "数据库迁移失败"
exit 1
fi
# 记录部署历史
CURRENT_API=$(docker inspect --format='{{.Config.Image}}' juhi-api 2>/dev/null || echo "none")
CURRENT_FE=$(docker inspect --format='{{.Config.Image}}' juhi-frontend 2>/dev/null || echo "none")
echo "$(date -Iseconds)|${CURRENT_API}|${CURRENT_FE}" >> .deploy-history
tail -20 .deploy-history > .deploy-history.tmp && mv .deploy-history.tmp .deploy-history
# 蓝绿部署
docker compose -f docker-compose.prod.yml up -d --no-deps --scale api=2 api
RETRY=0
MAX_RETRY=15
until curl -sf http://localhost:3000/health > /dev/null 2>&1; do
RETRY=$((RETRY + 1))
if [ $RETRY -ge $MAX_RETRY ]; then
echo "健康检查超时,回滚到单实例"
docker compose -f docker-compose.prod.yml up -d --no-deps --scale api=1 api
exit 1
fi
sleep 5
done
docker compose -f docker-compose.prod.yml up -d --no-deps --scale api=1 api
docker compose -f docker-compose.prod.yml up -d --no-deps frontend
docker compose -f docker-compose.prod.yml exec -T nginx nginx -s reload 2>/dev/null || true
docker image prune -f
echo "$VERSION" > .deployed_version
echo "==> Production 部署完成: $VERSION"
EOF
env:
HOST: ${{ secrets.PRODUCTION_HOST }}
USER: ${{ secrets.PRODUCTION_USER }}
DEPLOY_PATH: /opt/juhi
API_IMAGE: ${{ needs.build-and-push.outputs.api-image }}
FRONTEND_IMAGE: ${{ needs.build-and-push.outputs.frontend-image }}
VERSION: ${{ needs.build-and-push.outputs.version }}
- name: Production 部署验证
run: |
ssh $USER@$HOST << 'EOF'
cd /opt/juhi
if [ -f "./scripts/post-deploy-verify.sh" ]; then
chmod +x ./scripts/post-deploy-verify.sh
./scripts/post-deploy-verify.sh --quick || exit 1
else
curl -sf http://localhost:3000/health || exit 1
UNHEALTHY=$(docker compose -f docker-compose.prod.yml ps --format json | grep -c '"unhealthy"' || true)
if [ "$UNHEALTHY" -gt 0 ]; then
docker compose -f docker-compose.prod.yml ps
exit 1
fi
echo "Production 验证通过"
fi
EOF
env:
HOST: ${{ secrets.PRODUCTION_HOST }}
USER: ${{ secrets.PRODUCTION_USER }}
- if: startsWith(github.ref, 'refs/tags/v')
name: 创建 GitHub Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: "true"
make_latest: "true"
timeout-minutes: "20"
permissions:
contents: write # 创建 GitHub Release 需要 write 权限
...
|
deploy-production
|
["gate","build-and-push","depl ["gate","build-and-push","deploy-staging"]...
|
["ubuntu-latest"]
|
25963
|
4
|
1778643775
|
1778643775
|
1778643760
|
1778643775
|
|
1
|
|
0
|
Edit
Delete
|
|
103
|
44
|
6
|
5
|
89209aba1afe21133399ce0db0a47c2729c9a4b4
|
0
|
质量门禁检查
|
0
|
name: 深度完整全面自动化测试
"on":
push:
name: 深度完整全面自动化测试
"on":
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
schedule:
# 每天凌晨 2 点运行完整测试
- cron: '0 2 * * *'
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
quality-gate:
name: 质量门禁检查
runs-on: ubuntu-latest
if: always()
steps:
- name: 下载所有测试结果
uses: actions/download-artifact@v3
- name: 汇总测试结果
run: |
echo "## 测试汇总报告" > summary.md
echo "" >> summary.md
echo "### 单元测试" >> summary.md
# 解析单元测试结果
echo "### 集成测试" >> summary.md
# 解析集成测试结果
echo "### E2E 测试" >> summary.md
# 解析 E2E 测试结果
- name: 检查所有测试是否通过
run: |
if [[ "${{ needs.unit-tests.result }}" != "success" ]]; then
echo "❌ 单元测试失败"
exit 1
fi
if [[ "${{ needs.integration-tests.result }}" != "success" ]]; then
echo "❌ 集成测试失败"
exit 1
fi
if [[ "${{ needs.e2e-tests.result }}" != "success" ]]; then
echo "❌ E2E 测试失败"
exit 1
fi
echo "✅ 所有测试通过,质量门禁检查成功"
- if: failure()
name: 发送通知
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: 测试失败!请查看详情。
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
...
|
quality-gate
|
["unit-tests","integration-tests", ["unit-tests","integration-tests","e2e-tests"]...
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1771862496
|
1771862389
|
1771862496
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
139
|
48
|
6
|
5
|
a7c22fc0f5e2d113210bb58646dcdbf7381be245
|
0
|
质量门禁检查
|
0
|
name: 深度完整全面自动化测试
"on":
push:
name: 深度完整全面自动化测试
"on":
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
schedule:
# 每天凌晨 2 点运行完整测试
- cron: '0 2 * * *'
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
quality-gate:
name: 质量门禁检查
runs-on: ubuntu-latest
if: always()
steps:
- name: 下载所有测试结果
uses: actions/download-artifact@v3
- name: 汇总测试结果
run: |
echo "## 测试汇总报告" > summary.md
echo "" >> summary.md
echo "### 单元测试" >> summary.md
# 解析单元测试结果
echo "### 集成测试" >> summary.md
# 解析集成测试结果
echo "### E2E 测试" >> summary.md
# 解析 E2E 测试结果
- name: 检查所有测试是否通过
run: |
if [[ "${{ needs.unit-tests.result }}" != "success" ]]; then
echo "❌ 单元测试失败"
exit 1
fi
if [[ "${{ needs.integration-tests.result }}" != "success" ]]; then
echo "❌ 集成测试失败"
exit 1
fi
if [[ "${{ needs.e2e-tests.result }}" != "success" ]]; then
echo "❌ E2E 测试失败"
exit 1
fi
echo "✅ 所有测试通过,质量门禁检查成功"
- if: failure()
name: 发送通知
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: 测试失败!请查看详情。
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
...
|
quality-gate
|
["unit-tests","integration-tests", ["unit-tests","integration-tests","e2e-tests"]...
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1771862495
|
1771862412
|
1771862495
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
173
|
50
|
6
|
5
|
e15e357a819170a89dbcfd25fe33ea58972ae0ff
|
0
|
质量门禁检查
|
0
|
name: 深度完整全面自动化测试
"on":
push:
name: 深度完整全面自动化测试
"on":
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
schedule:
# 每天凌晨 2 点运行完整测试
- cron: '0 2 * * *'
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
quality-gate:
name: 质量门禁检查
runs-on: ubuntu-latest
if: always()
steps:
- name: 下载所有测试结果
uses: actions/download-artifact@v3
- name: 汇总测试结果
run: |
echo "## 测试汇总报告" > summary.md
echo "" >> summary.md
echo "### 单元测试" >> summary.md
# 解析单元测试结果
echo "### 集成测试" >> summary.md
# 解析集成测试结果
echo "### E2E 测试" >> summary.md
# 解析 E2E 测试结果
- name: 检查所有测试是否通过
run: |
if [[ "${{ needs.unit-tests.result }}" != "success" ]]; then
echo "❌ 单元测试失败"
exit 1
fi
if [[ "${{ needs.integration-tests.result }}" != "success" ]]; then
echo "❌ 集成测试失败"
exit 1
fi
if [[ "${{ needs.e2e-tests.result }}" != "success" ]]; then
echo "❌ E2E 测试失败"
exit 1
fi
echo "✅ 所有测试通过,质量门禁检查成功"
- if: failure()
name: 发送通知
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: 测试失败!请查看详情。
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
...
|
quality-gate
|
["unit-tests","integration-tests", ["unit-tests","integration-tests","e2e-tests"]...
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1771862519
|
1771862496
|
1771862519
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
207
|
52
|
6
|
5
|
c8adeb7142f7f1b57a10bfffc3de856524997227
|
0
|
质量门禁检查
|
0
|
name: 深度完整全面自动化测试
"on":
push:
name: 深度完整全面自动化测试
"on":
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
schedule:
# 每天凌晨 2 点运行完整测试
- cron: '0 2 * * *'
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
quality-gate:
name: 质量门禁检查
runs-on: ubuntu-latest
if: always()
steps:
- name: 下载所有测试结果
uses: actions/download-artifact@v3
- name: 汇总测试结果
run: |
echo "## 测试汇总报告" > summary.md
echo "" >> summary.md
echo "### 单元测试" >> summary.md
# 解析单元测试结果
echo "### 集成测试" >> summary.md
# 解析集成测试结果
echo "### E2E 测试" >> summary.md
# 解析 E2E 测试结果
- name: 检查所有测试是否通过
run: |
if [[ "${{ needs.unit-tests.result }}" != "success" ]]; then
echo "❌ 单元测试失败"
exit 1
fi
if [[ "${{ needs.integration-tests.result }}" != "success" ]]; then
echo "❌ 集成测试失败"
exit 1
fi
if [[ "${{ needs.e2e-tests.result }}" != "success" ]]; then
echo "❌ E2E 测试失败"
exit 1
fi
echo "✅ 所有测试通过,质量门禁检查成功"
- if: failure()
name: 发送通知
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: 测试失败!请查看详情。
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
...
|
quality-gate
|
["unit-tests","integration-tests", ["unit-tests","integration-tests","e2e-tests"]...
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1771862652
|
1771862519
|
1771862652
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
241
|
54
|
6
|
5
|
7145a8553a880d8a94356baf7bcb4e2df4631e61
|
0
|
质量门禁检查
|
0
|
name: 深度完整全面自动化测试
"on":
push:
name: 深度完整全面自动化测试
"on":
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
schedule:
# 每天凌晨 2 点运行完整测试
- cron: '0 2 * * *'
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
quality-gate:
name: 质量门禁检查
runs-on: ubuntu-latest
if: always()
steps:
- name: 下载所有测试结果
uses: actions/download-artifact@v3
- name: 汇总测试结果
run: |
echo "## 测试汇总报告" > summary.md
echo "" >> summary.md
echo "### 单元测试" >> summary.md
# 解析单元测试结果
echo "### 集成测试" >> summary.md
# 解析集成测试结果
echo "### E2E 测试" >> summary.md
# 解析 E2E 测试结果
- name: 检查所有测试是否通过
run: |
if [[ "${{ needs.unit-tests.result }}" != "success" ]]; then
echo "❌ 单元测试失败"
exit 1
fi
if [[ "${{ needs.integration-tests.result }}" != "success" ]]; then
echo "❌ 集成测试失败"
exit 1
fi
if [[ "${{ needs.e2e-tests.result }}" != "success" ]]; then
echo "❌ E2E 测试失败"
exit 1
fi
echo "✅ 所有测试通过,质量门禁检查成功"
- if: failure()
name: 发送通知
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: 测试失败!请查看详情。
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
...
|
quality-gate
|
["unit-tests","integration-tests", ["unit-tests","integration-tests","e2e-tests"]...
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1771862765
|
1771862652
|
1771862765
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
276
|
57
|
6
|
5
|
343b38f6bfbbd7d44a1387e1351700df68013555
|
0
|
质量门禁检查
|
0
|
name: 深度完整全面自动化测试
"on":
push:
name: 深度完整全面自动化测试
"on":
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
schedule:
# 每天凌晨 2 点运行完整测试
- cron: '0 2 * * *'
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
quality-gate:
name: 质量门禁检查
runs-on: ubuntu-latest
if: always()
steps:
- name: 下载所有测试结果
uses: actions/download-artifact@v3
- name: 汇总测试结果
run: |
echo "## 测试汇总报告" > summary.md
echo "" >> summary.md
echo "### 单元测试" >> summary.md
# 解析单元测试结果
echo "### 集成测试" >> summary.md
# 解析集成测试结果
echo "### E2E 测试" >> summary.md
# 解析 E2E 测试结果
- name: 检查所有测试是否通过
run: |
if [[ "${{ needs.unit-tests.result }}" != "success" ]]; then
echo "❌ 单元测试失败"
exit 1
fi
if [[ "${{ needs.integration-tests.result }}" != "success" ]]; then
echo "❌ 集成测试失败"
exit 1
fi
if [[ "${{ needs.e2e-tests.result }}" != "success" ]]; then
echo "❌ E2E 测试失败"
exit 1
fi
echo "✅ 所有测试通过,质量门禁检查成功"
- if: failure()
name: 发送通知
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: 测试失败!请查看详情。
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
...
|
quality-gate
|
["unit-tests","integration-tests", ["unit-tests","integration-tests","e2e-tests"]...
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1771862814
|
1771862765
|
1771862814
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
310
|
59
|
6
|
5
|
18a5bb9c67c571fc2cad6ffe61e70633bcec36a8
|
0
|
质量门禁检查
|
0
|
name: 深度完整全面自动化测试
"on":
push:
name: 深度完整全面自动化测试
"on":
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
schedule:
# 每天凌晨 2 点运行完整测试
- cron: '0 2 * * *'
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
quality-gate:
name: 质量门禁检查
runs-on: ubuntu-latest
if: always()
steps:
- name: 下载所有测试结果
uses: actions/download-artifact@v3
- name: 汇总测试结果
run: |
echo "## 测试汇总报告" > summary.md
echo "" >> summary.md
echo "### 单元测试" >> summary.md
# 解析单元测试结果
echo "### 集成测试" >> summary.md
# 解析集成测试结果
echo "### E2E 测试" >> summary.md
# 解析 E2E 测试结果
- name: 检查所有测试是否通过
run: |
if [[ "${{ needs.unit-tests.result }}" != "success" ]]; then
echo "❌ 单元测试失败"
exit 1
fi
if [[ "${{ needs.integration-tests.result }}" != "success" ]]; then
echo "❌ 集成测试失败"
exit 1
fi
if [[ "${{ needs.e2e-tests.result }}" != "success" ]]; then
echo "❌ E2E 测试失败"
exit 1
fi
echo "✅ 所有测试通过,质量门禁检查成功"
- if: failure()
name: 发送通知
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: 测试失败!请查看详情。
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
...
|
quality-gate
|
["unit-tests","integration-tests", ["unit-tests","integration-tests","e2e-tests"]...
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1771862907
|
1771862814
|
1771862907
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
344
|
61
|
6
|
5
|
f8a1588d13917a22d3710214cee7468768c4490e
|
0
|
质量门禁检查
|
0
|
name: 深度完整全面自动化测试
"on":
push:
name: 深度完整全面自动化测试
"on":
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
schedule:
# 每天凌晨 2 点运行完整测试
- cron: '0 2 * * *'
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
quality-gate:
name: 质量门禁检查
runs-on: ubuntu-latest
if: always()
steps:
- name: 下载所有测试结果
uses: actions/download-artifact@v3
- name: 汇总测试结果
run: |
echo "## 测试汇总报告" > summary.md
echo "" >> summary.md
echo "### 单元测试" >> summary.md
# 解析单元测试结果
echo "### 集成测试" >> summary.md
# 解析集成测试结果
echo "### E2E 测试" >> summary.md
# 解析 E2E 测试结果
- name: 检查所有测试是否通过
run: |
if [[ "${{ needs.unit-tests.result }}" != "success" ]]; then
echo "❌ 单元测试失败"
exit 1
fi
if [[ "${{ needs.integration-tests.result }}" != "success" ]]; then
echo "❌ 集成测试失败"
exit 1
fi
if [[ "${{ needs.e2e-tests.result }}" != "success" ]]; then
echo "❌ E2E 测试失败"
exit 1
fi
echo "✅ 所有测试通过,质量门禁检查成功"
- if: failure()
name: 发送通知
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: 测试失败!请查看详情。
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
...
|
quality-gate
|
["unit-tests","integration-tests", ["unit-tests","integration-tests","e2e-tests"]...
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1771862931
|
1771862907
|
1771862931
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
378
|
63
|
6
|
5
|
4e9a26e0e1c15538f2a6f033748340bb62b02af2
|
0
|
质量门禁检查
|
0
|
name: 深度完整全面自动化测试
"on":
push:
name: 深度完整全面自动化测试
"on":
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
schedule:
# 每天凌晨 2 点运行完整测试
- cron: '0 2 * * *'
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
quality-gate:
name: 质量门禁检查
runs-on: ubuntu-latest
if: always()
steps:
- name: 下载所有测试结果
uses: actions/download-artifact@v3
- name: 汇总测试结果
run: |
echo "## 测试汇总报告" > summary.md
echo "" >> summary.md
echo "### 单元测试" >> summary.md
# 解析单元测试结果
echo "### 集成测试" >> summary.md
# 解析集成测试结果
echo "### E2E 测试" >> summary.md
# 解析 E2E 测试结果
- name: 检查所有测试是否通过
run: |
if [[ "${{ needs.unit-tests.result }}" != "success" ]]; then
echo "❌ 单元测试失败"
exit 1
fi
if [[ "${{ needs.integration-tests.result }}" != "success" ]]; then
echo "❌ 集成测试失败"
exit 1
fi
if [[ "${{ needs.e2e-tests.result }}" != "success" ]]; then
echo "❌ E2E 测试失败"
exit 1
fi
echo "✅ 所有测试通过,质量门禁检查成功"
- if: failure()
name: 发送通知
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: 测试失败!请查看详情。
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
...
|
quality-gate
|
["unit-tests","integration-tests", ["unit-tests","integration-tests","e2e-tests"]...
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1771862946
|
1771862931
|
1771862946
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
412
|
65
|
6
|
5
|
546659ec60e857bdf77619972d1b3e4333b621ac
|
0
|
质量门禁检查
|
0
|
name: 深度完整全面自动化测试
"on":
push:
name: 深度完整全面自动化测试
"on":
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
schedule:
# 每天凌晨 2 点运行完整测试
- cron: '0 2 * * *'
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
quality-gate:
name: 质量门禁检查
runs-on: ubuntu-latest
if: always()
steps:
- name: 下载所有测试结果
uses: actions/download-artifact@v3
- name: 汇总测试结果
run: |
echo "## 测试汇总报告" > summary.md
echo "" >> summary.md
echo "### 单元测试" >> summary.md
# 解析单元测试结果
echo "### 集成测试" >> summary.md
# 解析集成测试结果
echo "### E2E 测试" >> summary.md
# 解析 E2E 测试结果
- name: 检查所有测试是否通过
run: |
if [[ "${{ needs.unit-tests.result }}" != "success" ]]; then
echo "❌ 单元测试失败"
exit 1
fi
if [[ "${{ needs.integration-tests.result }}" != "success" ]]; then
echo "❌ 集成测试失败"
exit 1
fi
if [[ "${{ needs.e2e-tests.result }}" != "success" ]]; then
echo "❌ E2E 测试失败"
exit 1
fi
echo "✅ 所有测试通过,质量门禁检查成功"
- if: failure()
name: 发送通知
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: 测试失败!请查看详情。
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
...
|
quality-gate
|
["unit-tests","integration-tests", ["unit-tests","integration-tests","e2e-tests"]...
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1771863033
|
1771862946
|
1771863033
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
447
|
68
|
6
|
5
|
14c4cca40c3b21a5247fd9117fc9af829acc8ea7
|
0
|
质量门禁检查
|
0
|
name: 深度完整全面自动化测试
"on":
push:
name: 深度完整全面自动化测试
"on":
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
schedule:
# 每天凌晨 2 点运行完整测试
- cron: '0 2 * * *'
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
quality-gate:
name: 质量门禁检查
runs-on: ubuntu-latest
if: always()
steps:
- name: 下载所有测试结果
uses: actions/download-artifact@v3
- name: 汇总测试结果
run: |
echo "## 测试汇总报告" > summary.md
echo "" >> summary.md
echo "### 单元测试" >> summary.md
# 解析单元测试结果
echo "### 集成测试" >> summary.md
# 解析集成测试结果
echo "### E2E 测试" >> summary.md
# 解析 E2E 测试结果
- name: 检查所有测试是否通过
run: |
if [[ "${{ needs.unit-tests.result }}" != "success" ]]; then
echo "❌ 单元测试失败"
exit 1
fi
if [[ "${{ needs.integration-tests.result }}" != "success" ]]; then
echo "❌ 集成测试失败"
exit 1
fi
if [[ "${{ needs.e2e-tests.result }}" != "success" ]]; then
echo "❌ E2E 测试失败"
exit 1
fi
echo "✅ 所有测试通过,质量门禁检查成功"
- if: failure()
name: 发送通知
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: 测试失败!请查看详情。
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
...
|
quality-gate
|
["unit-tests","integration-tests", ["unit-tests","integration-tests","e2e-tests"]...
|
["ubuntu-latest"]
|
0
|
3
|
0
|
1771863142
|
1771863033
|
1771863142
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
481
|
70
|
6
|
5
|
07ea713c1feff2e0f7d2b91d5ca68f2e0140a963
|
0
|
质量门禁检查
|
1
|
name: 深度完整全面自动化测试
"on":
push:
name: 深度完整全面自动化测试
"on":
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
schedule:
# 每天凌晨 2 点运行完整测试
- cron: '0 2 * * *'
env:
NODE_VERSION: "18"
PNPM_VERSION: "8"
jobs:
quality-gate:
name: 质量门禁检查
runs-on: ubuntu-latest
if: always()
steps:
- name: 下载所有测试结果
uses: actions/download-artifact@v3
- name: 汇总测试结果
run: |
echo "## 测试汇总报告" > summary.md
echo "" >> summary.md
echo "### 单元测试" >> summary.md
# 解析单元测试结果
echo "### 集成测试" >> summary.md
# 解析集成测试结果
echo "### E2E 测试" >> summary.md
# 解析 E2E 测试结果
- name: 检查所有测试是否通过
run: |
if [[ "${{ needs.unit-tests.result }}" != "success" ]]; then
echo "❌ 单元测试失败"
exit 1
fi
if [[ "${{ needs.integration-tests.result }}" != "success" ]]; then
echo "❌ 集成测试失败"
exit 1
fi
if [[ "${{ needs.e2e-tests.result }}" != "success" ]]; then
echo "❌ E2E 测试失败"
exit 1
fi
echo "✅ 所有测试通过,质量门禁检查成功"
- if: failure()
name: 发送通知
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: 测试失败!请查看详情。
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
...
|
quality-gate
|
["unit-tests","integration-tests", ["unit-tests","integration-tests","e2e-tests"]...
|
["ubuntu-latest"]
|
73
|
2
|
1771866218
|
1771866248
|
1771863142
|
1771866248
|
NULL
|
NULL
|
|
0
|
Edit
Delete
|
|
7837
|
6703
|
6
|
5
|
e112d45af414e4862c0328abad7c4df74d3c1dbf
|
0
|
质量门禁
|
1
|
name: Deploy
"on":
push:
branc name: Deploy
"on":
push:
branches: [main]
tags: ['v*']
workflow_dispatch:
inputs:
environment:
description: '部署环境'
required: true
default: 'staging'
type: choice
options:
- staging
- production
- rollback
skip_tests:
description: '跳过测试(紧急修复时使用)'
required: false
default: false
type: boolean
env:
IMAGE_PREFIX: ${{ github.repository_owner }}/juhi
NODE_VERSION: "20"
PNPM_VERSION: "8"
REGISTRY: ghcr.io
jobs:
quality-check:
name: 质量门禁
runs-on: ubuntu-latest
if: >-
!(github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'rollback') && !(github.event_name == 'workflow_dispatch' && github.event.inputs.skip_tests == 'true')
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 安装 pnpm
uses: pnpm/action-setup@v2
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: npx tsc --noEmit
working-directory: backend
- name: 前端类型检查
run: npx vue-tsc --noEmit
working-directory: frontend
- name: 后端测试
run: npm run test:run -- --reporter=default --reporter=junit --outputFile=test-results.xml
working-directory: backend
env:
NODE_ENV: test
- if: always()
name: 上传测试结果
uses: actions/upload-artifact@v4
with:
name: test-results
path: backend/test-results.xml
retention-days: "7"
timeout-minutes: "15"
...
|
quality-check
|
null
|
["ubuntu-latest"]
|
6748
|
2
|
1773832272
|
1773832296
|
1773831749
|
1773832296
|
|
0
|
|
0
|
Edit
Delete
|
|
7899
|
6713
|
6
|
5
|
b7eb415d7a9689f1efec941bcb2dcd7d098e9c28
|
0
|
质量门禁
|
1
|
name: Deploy
"on":
push:
branc name: Deploy
"on":
push:
branches: [main]
tags: ['v*']
workflow_dispatch:
inputs:
environment:
description: '部署环境'
required: true
default: 'staging'
type: choice
options:
- staging
- production
- rollback
skip_tests:
description: '跳过测试(紧急修复时使用)'
required: false
default: false
type: boolean
env:
IMAGE_PREFIX: ${{ github.repository_owner }}/juhi
NODE_VERSION: "20"
PNPM_VERSION: "8"
REGISTRY: ghcr.io
jobs:
quality-check:
name: 质量门禁
runs-on: ubuntu-latest
if: >-
!(github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'rollback') && !(github.event_name == 'workflow_dispatch' && github.event.inputs.skip_tests == 'true')
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 安装 pnpm
uses: pnpm/action-setup@v2
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: npx tsc --noEmit
working-directory: backend
- name: 前端类型检查
run: npx vue-tsc --noEmit
working-directory: frontend
- name: 后端测试
run: npm run test:run -- --reporter=default --reporter=junit --outputFile=test-results.xml
working-directory: backend
env:
NODE_ENV: test
- if: always()
name: 上传测试结果
uses: actions/upload-artifact@v4
with:
name: test-results
path: backend/test-results.xml
retention-days: "7"
timeout-minutes: "15"
...
|
quality-check
|
null
|
["ubuntu-latest"]
|
6776
|
2
|
1773832902
|
1773832925
|
1773832744
|
1773832925
|
|
0
|
|
0
|
Edit
Delete
|
|
8262
|
6996
|
6
|
5
|
bd59e5501292cb061719f669c7a7b7afd4f1a0b7
|
0
|
质量门禁
|
1
|
name: Deploy
"on":
push:
branc name: Deploy
"on":
push:
branches: [main]
tags: ['v*']
workflow_dispatch:
inputs:
environment:
description: '部署环境'
required: true
default: 'staging'
type: choice
options:
- staging
- production
- rollback
skip_tests:
description: '跳过测试(紧急修复时使用)'
required: false
default: false
type: boolean
env:
IMAGE_PREFIX: ${{ github.repository_owner }}/juhi
NODE_VERSION: "20"
PNPM_VERSION: "8"
REGISTRY: ghcr.io
jobs:
quality-check:
name: 质量门禁
runs-on: ubuntu-latest
if: >-
!(github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'rollback') && !(github.event_name == 'workflow_dispatch' && github.event.inputs.skip_tests == 'true')
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 安装 pnpm
uses: pnpm/action-setup@v2
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: npx tsc --noEmit
working-directory: backend
- name: 前端类型检查
run: npx vue-tsc --noEmit
working-directory: frontend
- name: 后端测试
run: npm run test:run -- --reporter=default --reporter=junit --outputFile=test-results.xml
working-directory: backend
env:
NODE_ENV: test
- if: always()
name: 上传测试结果
uses: actions/upload-artifact@v4
with:
name: test-results
path: backend/test-results.xml
retention-days: "7"
timeout-minutes: "15"
...
|
quality-check
|
null
|
["ubuntu-latest"]
|
7129
|
2
|
1773912956
|
1773912981
|
1773912912
|
1773912982
|
|
0
|
|
0
|
Edit
Delete
|
|
8320
|
7004
|
6
|
5
|
01a8ea9191c28b210d2abb3db3cbc65db42fc801
|
0
|
质量门禁
|
1
|
name: Deploy
"on":
push:
branc name: Deploy
"on":
push:
branches: [main]
tags: ['v*']
workflow_dispatch:
inputs:
environment:
description: '部署环境'
required: true
default: 'staging'
type: choice
options:
- staging
- production
- rollback
skip_tests:
description: '跳过测试(紧急修复时使用)'
required: false
default: false
type: boolean
env:
IMAGE_PREFIX: ${{ github.repository_owner }}/juhi
NODE_VERSION: "20"
PNPM_VERSION: "8"
REGISTRY: ghcr.io
jobs:
quality-check:
name: 质量门禁
runs-on: ubuntu-latest
if: >-
!(github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'rollback') && !(github.event_name == 'workflow_dispatch' && github.event.inputs.skip_tests == 'true')
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 安装 pnpm
uses: pnpm/action-setup@v2
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: npx tsc --noEmit
working-directory: backend
- name: 前端类型检查
run: npx vue-tsc --noEmit
working-directory: frontend
- name: 后端测试
run: npm run test:run -- --reporter=default --reporter=junit --outputFile=test-results.xml
working-directory: backend
env:
NODE_ENV: test
- if: always()
name: 上传测试结果
uses: actions/upload-artifact@v4
with:
name: test-results
path: backend/test-results.xml
retention-days: "7"
timeout-minutes: "15"
...
|
quality-check
|
null
|
["ubuntu-latest"]
|
7144
|
2
|
1773913869
|
1773913899
|
1773913345
|
1773913900
|
|
0
|
|
0
|
Edit
Delete
|
|
8455
|
7069
|
6
|
5
|
9771b23ae7aaee164f15f9c4ccbdb18eb68fc71f
|
0
|
质量门禁
|
1
|
name: Deploy
"on":
push:
branc name: Deploy
"on":
push:
branches: [main]
tags: ['v*']
workflow_dispatch:
inputs:
environment:
description: '部署环境'
required: true
default: 'staging'
type: choice
options:
- staging
- production
- rollback
skip_tests:
description: '跳过测试(紧急修复时使用)'
required: false
default: false
type: boolean
env:
IMAGE_PREFIX: ${{ github.repository_owner }}/juhi
NODE_VERSION: "20"
PNPM_VERSION: "8"
REGISTRY: ghcr.io
jobs:
quality-check:
name: 质量门禁
runs-on: ubuntu-latest
if: >-
!(github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'rollback') && !(github.event_name == 'workflow_dispatch' && github.event.inputs.skip_tests == 'true')
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 安装 pnpm
uses: pnpm/action-setup@v2
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: npx tsc --noEmit
working-directory: backend
- name: 前端类型检查
run: npx vue-tsc --noEmit
working-directory: frontend
- name: 后端测试
run: npm run test:run -- --reporter=default --reporter=junit --outputFile=test-results.xml
working-directory: backend
env:
NODE_ENV: test
- if: always()
name: 上传测试结果
uses: actions/upload-artifact@v4
with:
name: test-results
path: backend/test-results.xml
retention-days: "7"
timeout-minutes: "15"
...
|
quality-check
|
null
|
["ubuntu-latest"]
|
7267
|
2
|
1773929539
|
1773929562
|
1773929408
|
1773929562
|
|
0
|
|
0
|
Edit
Delete
|
|
8564
|
7114
|
6
|
5
|
979d9c81063fbda12f1445bf80b0c0027b0fbac2
|
0
|
质量门禁
|
1
|
name: Deploy
"on":
push:
branc name: Deploy
"on":
push:
branches: [main]
tags: ['v*']
workflow_dispatch:
inputs:
environment:
description: '部署环境'
required: true
default: 'staging'
type: choice
options:
- staging
- production
- rollback
skip_tests:
description: '跳过测试(紧急修复时使用)'
required: false
default: false
type: boolean
env:
IMAGE_PREFIX: ${{ github.repository_owner }}/juhi
NODE_VERSION: "20"
PNPM_VERSION: "8"
REGISTRY: ghcr.io
jobs:
quality-check:
name: 质量门禁
runs-on: ubuntu-latest
if: >-
!(github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'rollback') && !(github.event_name == 'workflow_dispatch' && github.event.inputs.skip_tests == 'true')
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 安装 pnpm
uses: pnpm/action-setup@v2
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: npx tsc --noEmit
working-directory: backend
- name: 前端类型检查
run: npx vue-tsc --noEmit
working-directory: frontend
- name: 后端测试
run: npm run test:run -- --reporter=default --reporter=junit --outputFile=test-results.xml
working-directory: backend
env:
NODE_ENV: test
- if: always()
name: 上传测试结果
uses: actions/upload-artifact@v4
with:
name: test-results
path: backend/test-results.xml
retention-days: "7"
timeout-minutes: "15"
...
|
quality-check
|
null
|
["ubuntu-latest"]
|
7366
|
2
|
1773939901
|
1773939923
|
1773939780
|
1773939923
|
|
0
|
|
0
|
Edit
Delete
|
|
8753
|
7232
|
6
|
5
|
0900b15d607e5c78f97fec16a73357ad4f814390
|
0
|
质量门禁
|
1
|
name: Deploy
"on":
push:
branc name: Deploy
"on":
push:
branches: [main]
tags: ['v*']
workflow_dispatch:
inputs:
environment:
description: '部署环境'
required: true
default: 'staging'
type: choice
options:
- staging
- production
- rollback
skip_tests:
description: '跳过测试(紧急修复时使用)'
required: false
default: false
type: boolean
env:
IMAGE_PREFIX: ${{ github.repository_owner }}/juhi
NODE_VERSION: "20"
PNPM_VERSION: "8"
REGISTRY: ghcr.io
jobs:
quality-check:
name: 质量门禁
runs-on: ubuntu-latest
if: >-
!(github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'rollback') && !(github.event_name == 'workflow_dispatch' && github.event.inputs.skip_tests == 'true')
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 安装 pnpm
uses: pnpm/action-setup@v2
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: npx tsc --noEmit
working-directory: backend
- name: 前端类型检查
run: npx vue-tsc --noEmit
working-directory: frontend
- name: 后端测试
run: npm run test:run -- --reporter=default --reporter=junit --outputFile=test-results.xml
working-directory: backend
env:
NODE_ENV: test
- if: always()
name: 上传测试结果
uses: actions/upload-artifact@v4
with:
name: test-results
path: backend/test-results.xml
retention-days: "7"
timeout-minutes: "15"
...
|
quality-check
|
null
|
["ubuntu-latest"]
|
7545
|
2
|
1774221109
|
1774221133
|
1774220985
|
1774221134
|
|
0
|
|
0
|
Edit
Delete
|
|
8829
|
7249
|
6
|
5
|
80c2bdb2f93a19aa53d16ded06387b70c6084bf8
|
0
|
质量门禁
|
1
|
name: Deploy
"on":
push:
branc name: Deploy
"on":
push:
branches: [main]
tags: ['v*']
workflow_dispatch:
inputs:
environment:
description: '部署环境'
required: true
default: 'staging'
type: choice
options:
- staging
- production
- rollback
skip_tests:
description: '跳过测试(紧急修复时使用)'
required: false
default: false
type: boolean
env:
IMAGE_PREFIX: ${{ github.repository_owner }}/juhi
NODE_VERSION: "20"
PNPM_VERSION: "8"
REGISTRY: ghcr.io
jobs:
quality-check:
name: 质量门禁
runs-on: ubuntu-latest
if: >-
!(github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'rollback') && !(github.event_name == 'workflow_dispatch' && github.event.inputs.skip_tests == 'true')
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 安装 pnpm
uses: pnpm/action-setup@v2
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: npx tsc --noEmit
working-directory: backend
- name: 前端类型检查
run: npx vue-tsc --noEmit
working-directory: frontend
- name: 后端测试
run: npm run test:run -- --reporter=default --reporter=junit --outputFile=test-results.xml
working-directory: backend
env:
NODE_ENV: test
- if: always()
name: 上传测试结果
uses: actions/upload-artifact@v4
with:
name: test-results
path: backend/test-results.xml
retention-days: "7"
timeout-minutes: "15"
...
|
quality-check
|
null
|
["ubuntu-latest"]
|
7613
|
2
|
1774223053
|
1774223075
|
1774223019
|
1774223075
|
|
0
|
|
0
|
Edit
Delete
|
|
8900
|
7261
|
6
|
5
|
df7b6f46170cb46db4771dd1e07991b0b13d90a0
|
0
|
质量门禁
|
1
|
name: Deploy
"on":
push:
branc name: Deploy
"on":
push:
branches: [main]
tags: ['v*']
workflow_dispatch:
inputs:
environment:
description: '部署环境'
required: true
default: 'staging'
type: choice
options:
- staging
- production
- rollback
skip_tests:
description: '跳过测试(紧急修复时使用)'
required: false
default: false
type: boolean
env:
IMAGE_PREFIX: ${{ github.repository_owner }}/juhi
NODE_VERSION: "20"
PNPM_VERSION: "8"
REGISTRY: ghcr.io
jobs:
quality-check:
name: 质量门禁
runs-on: ubuntu-latest
if: >-
!(github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'rollback') && !(github.event_name == 'workflow_dispatch' && github.event.inputs.skip_tests == 'true')
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 安装 pnpm
uses: pnpm/action-setup@v2
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: npx tsc --noEmit
working-directory: backend
- name: 前端类型检查
run: npx vue-tsc --noEmit
working-directory: frontend
- name: 后端测试
run: npm run test:run -- --reporter=default --reporter=junit --outputFile=test-results.xml
working-directory: backend
env:
NODE_ENV: test
- if: always()
name: 上传测试结果
uses: actions/upload-artifact@v4
with:
name: test-results
path: backend/test-results.xml
retention-days: "7"
timeout-minutes: "15"
...
|
quality-check
|
null
|
["ubuntu-latest"]
|
7674
|
2
|
1774223547
|
1774223569
|
1774223517
|
1774223569
|
|
0
|
|
0
|
Edit
Delete
|
|
9004
|
7291
|
6
|
5
|
339ea969ec2633756fb1a155d47723d0f61396a4
|
0
|
质量门禁
|
1
|
name: Deploy
"on":
push:
branc name: Deploy
"on":
push:
branches: [main]
tags: ['v*']
workflow_dispatch:
inputs:
environment:
description: '部署环境'
required: true
default: 'staging'
type: choice
options:
- staging
- production
- rollback
skip_tests:
description: '跳过测试(紧急修复时使用)'
required: false
default: false
type: boolean
env:
IMAGE_PREFIX: ${{ github.repository_owner }}/juhi
NODE_VERSION: "20"
PNPM_VERSION: "8"
REGISTRY: ghcr.io
jobs:
quality-check:
name: 质量门禁
runs-on: ubuntu-latest
if: >-
!(github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'rollback') && !(github.event_name == 'workflow_dispatch' && github.event.inputs.skip_tests == 'true')
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 安装 pnpm
uses: pnpm/action-setup@v2
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: npx tsc --noEmit
working-directory: backend
- name: 前端类型检查
run: npx vue-tsc --noEmit
working-directory: frontend
- name: 后端测试
run: npm run test:run -- --reporter=default --reporter=junit --outputFile=test-results.xml
working-directory: backend
env:
NODE_ENV: test
- if: always()
name: 上传测试结果
uses: actions/upload-artifact@v4
with:
name: test-results
path: backend/test-results.xml
retention-days: "7"
timeout-minutes: "15"
...
|
quality-check
|
null
|
["ubuntu-latest"]
|
7763
|
2
|
1774229182
|
1774229205
|
1774229062
|
1774229205
|
|
0
|
|
0
|
Edit
Delete
|
|
9207
|
7416
|
6
|
5
|
67078f9a95f53530156ad27027eafe15a3126e89
|
0
|
质量门禁
|
1
|
name: Deploy to Aliyun
"on":
push:
name: Deploy to Aliyun
"on":
push:
branches: [main]
tags: ['v*']
workflow_dispatch:
inputs:
environment:
description: '部署环境'
required: true
default: 'production'
type: choice
options:
- production
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:
quality-check:
name: 质量门禁
runs-on: ubuntu-latest
if: >-
github.event_name != 'workflow_dispatch' || github.event.inputs.skip_tests != 'true'
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: 后端类型检查
run: pnpm --filter juhi-api run type-check
- name: 前端类型检查
run: pnpm --filter juhi-frontend run type-check
- name: 后端单元测试
run: pnpm --filter juhi-api run test:run
env:
NODE_ENV: test
DATABASE_URL: postgresql://test:test@localhost:5432/juhi_test
REDIS_URL: redis://localhost:6379
- name: 多租户安全审计
run: pnpm --filter juhi-api run audit:tenant
- name: Kafka 事件审计
run: pnpm --filter juhi-api run audit:events
timeout-minutes: "20"
...
|
quality-check
|
null
|
["ubuntu-latest"]
|
7954
|
2
|
1774262704
|
1774262734
|
1774262376
|
1774262734
|
|
0
|
|
0
|
Edit
Delete
|
|
9211
|
7417
|
6
|
5
|
67078f9a95f53530156ad27027eafe15a3126e89
|
0
|
质量门禁
|
1
|
name: Deploy
"on":
push:
branc name: Deploy
"on":
push:
branches: [main]
tags: ['v*']
workflow_dispatch:
inputs:
environment:
description: '部署环境'
required: true
default: 'staging'
type: choice
options:
- staging
- production
- rollback
skip_tests:
description: '跳过测试(紧急修复时使用)'
required: false
default: false
type: boolean
env:
IMAGE_PREFIX: ${{ github.repository_owner }}/juhi
NODE_VERSION: "20"
PNPM_VERSION: "8"
REGISTRY: ghcr.io
jobs:
quality-check:
name: 质量门禁
runs-on: ubuntu-latest
if: >-
!(github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'rollback') && !(github.event_name == 'workflow_dispatch' && github.event.inputs.skip_tests == 'true')
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 安装 pnpm
uses: pnpm/action-setup@v2
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: npx tsc --noEmit
working-directory: backend
- name: 前端类型检查
run: npx vue-tsc --noEmit
working-directory: frontend
- name: 后端测试
run: npm run test:run -- --reporter=default --reporter=junit --outputFile=test-results.xml
working-directory: backend
env:
NODE_ENV: test
- if: always()
name: 上传测试结果
uses: actions/upload-artifact@v4
with:
name: test-results
path: backend/test-results.xml
retention-days: "7"
timeout-minutes: "15"
...
|
quality-check
|
null
|
["ubuntu-latest"]
|
7955
|
2
|
1774262735
|
1774262759
|
1774262376
|
1774262759
|
|
0
|
|
0
|
Edit
Delete
|
|
9371
|
7506
|
6
|
5
|
ff3149170c6b0deb6d8151cb962592199b95bdd8
|
0
|
质量门禁
|
1
|
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"]
|
8128
|
1
|
1774286325
|
1774286326
|
1774286007
|
1774286326
|
|
1
|
|
0
|
Edit
Delete
|
|
9534
|
7601
|
6
|
5
|
2ec5b7d8079ffd911c7b27a395d5aba3ceafe372
|
0
|
质量门禁
|
1
|
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"]
|
8264
|
1
|
1774312506
|
1774312508
|
1774312190
|
1774312508
|
|
1
|
|
0
|
Edit
Delete
|
|
9609
|
7615
|
6
|
5
|
07680473f95a02e139e159147a93ef74e61f3db2
|
0
|
质量门禁
|
1
|
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"]
|
8312
|
1
|
1774314834
|
1774314835
|
1774314118
|
1774314836
|
|
1
|
|
0
|
Edit
Delete
|
|
9680
|
7625
|
6
|
5
|
cfe1efeda7265f05374d3bd0036cf684a15f3cb9
|
0
|
质量门禁
|
1
|
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"]
|
8355
|
1
|
1774315825
|
1774315826
|
1774315057
|
1774315826
|
|
1
|
|
0
|
Edit
Delete
|
|
9763
|
7638
|
6
|
5
|
8c39619c9cdb0d888d10942bf50533c8238021df
|
0
|
质量门禁
|
1
|
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"]
|
8400
|
1
|
1774317031
|
1774317032
|
1774316715
|
1774317032
|
|
1
|
|
0
|
Edit
Delete
|
|
9836
|
7650
|
6
|
5
|
dbf34b08bbb60650d15b0c55262dbfe8d0a3a655
|
0
|
质量门禁
|
1
|
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"]
|
8449
|
2
|
1774319105
|
1774319280
|
1774318461
|
1774319280
|
|
1
|
|
0
|
Edit
Delete
|
|
9914
|
7667
|
6
|
5
|
db7f39e63151b9c065646855287b8be73e13649b
|
0
|
质量门禁
|
1
|
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"]
|
8499
|
2
|
1774321792
|
1774321882
|
1774321431
|
1774321882
|
|
1
|
|
0
|
Edit
Delete
|
|
9994
|
7686
|
6
|
5
|
81e883dfff9283af39b3dd2aa30e25ae2119e8f0
|
0
|
质量门禁
|
1
|
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"]
|
8553
|
1
|
1774325280
|
1774325281
|
1774325031
|
1774325281
|
|
1
|
|
0
|
Edit
Delete
|
|
10144
|
7766
|
6
|
5
|
9f09902dce3537d952595fd6d33175b6f0c24c7e
|
0
|
质量门禁
|
1
|
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"]
|
8667
|
1
|
1774346928
|
1774346929
|
1774346642
|
1774346929
|
|
1
|
|
0
|
Edit
Delete
|
|
10437
|
7982
|
6
|
5
|
adc3e0209b2ffa4d34c89b638f1f03b36ebfd24f
|
0
|
质量门禁
|
1
|
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"]
|
8922
|
1
|
1774408813
|
1774408813
|
1774408525
|
1774408814
|
|
1
|
|
0
|
Edit
Delete
|
|
10606
|
8090
|
6
|
5
|
1b2a0b35284edd65cdda0501ced15ca388220ddd
|
0
|
质量门禁
|
1
|
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"]
|
9066
|
2
|
1774439148
|
1774439238
|
1774438841
|
1774439238
|
|
1
|
|
0
|
Edit
Delete
|
|
10680
|
8103
|
6
|
5
|
6dde21cfcbb2a424db0efb8629b4351eb2a43315
|
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
|
1774443470
|
1774440695
|
1774443470
|
|
0
|
|
0
|
Edit
Delete
|
|
10729
|
8115
|
6
|
5
|
3b540d63f39a66a79b06f096f7b7e9041dd4bc26
|
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
|
1774444076
|
1774443472
|
1774444076
|
|
0
|
|
0
|
Edit
Delete
|
|
10771
|
8120
|
6
|
5
|
83de8108577a8633d0dc3193eb7e19e9e6a668c1
|
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
|
1774444188
|
1774444078
|
1774444188
|
|
0
|
|
0
|
Edit
Delete
|
|
10811
|
8123
|
6
|
5
|
18d5b913e02e9d72301206688cfee01e40b9d2cd
|
0
|
质量门禁
|
1
|
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"]
|
9190
|
2
|
1774451250
|
1774451340
|
1774444191
|
1774451340
|
|
1
|
|
0
|
Edit
Delete
|
|
11077
|
8303
|
6
|
5
|
0b85dd84d5debc36f158918b188cde8b6305035f
|
0
|
质量门禁
|
1
|
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"]
|
9385
|
1
|
1774494801
|
1774494802
|
1774493746
|
1774494802
|
|
1
|
|
0
|
Edit
Delete
|
|
11263
|
8428
|
6
|
5
|
6b8d13c5b29827249da650ed1bd9ace7fc68959c
|
0
|
质量门禁
|
1
|
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"]
|
9543
|
1
|
1774529277
|
1774529278
|
1774528964
|
1774529278
|
|
1
|
|
0
|
Edit
Delete
|
|
11572
|
8669
|
6
|
5
|
c09d9ff6160d851464fbce2a407409d4e8d2bc95
|
0
|
质量门禁
|
1
|
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"]
|
9835
|
1
|
1774602061
|
1774602061
|
1774598827
|
1774602062
|
|
1
|
|
0
|
Edit
Delete
|
|
11900
|
8929
|
6
|
5
|
59f46625b28f7defe4dba196efd9edd5f82ed701
|
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
|
1774674958
|
1774674588
|
1774674958
|
|
0
|
|
0
|
Edit
Delete
|