diff --git a/.woodpecker.yml b/.woodpecker.yml index 82d8c44..87a0654 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -2,36 +2,35 @@ # Novalon Website - 全自动CI/CD工作流 # ============================================ # 发布策略:feature -> dev -> release -> main -# +# # 分支角色与流程: # 1. feature/** 分支:开发新功能 # - 触发:PR和push # - 执行:Lint + TypeCheck + Smoke Test # - 合并到:dev分支 -# +# # 2. dev 分支:开发集成 # - 触发:push # - 执行:完整测试套件(不部署) # - 创建/合并到:release/**分支 -# +# # 3. release/** 分支:生产环境代码 # - 触发:push -# - 执行:代码检查 + 构建产物 + 同步部署 +# - 执行:代码检查 + 构建部署一体化 # - 部署到:生产环境(139.155.109.62) # - 归档到:main分支 -# +# # 4. main 分支:稳定代码归档 # - 只读分支 # - 仅接收来自release的自动归档 -# +# # 流水线阶段: # 阶段0: 依赖安装(统一缓存) # 阶段1: 并行代码质量检查 (lint, type-check, security-scan) -# 阶段2: 单元测试 -> E2E测试 (允许失败) -# 阶段3: 构建生产产物 (仅release分支) -# 阶段4: 同步产物到生产服务器并部署 (仅release分支) -# 阶段5: 归档到main分支 (仅release分支) -# 阶段6: 企业微信通知 +# 阶段2: 测试 (unit-tests, e2e-tests - 仅dev分支) +# 阶段3: 构建并部署到生产环境 (release分支,一体化) +# 阶段4: 归档到main分支 (release分支) +# 阶段5: 企业微信通知 # ============================================ variables: @@ -54,11 +53,6 @@ steps: event: - push - pull_request - branch: - - feature/** - - dev - - release - - release/** # ============================================ # 阶段1: 并行代码质量检查 @@ -78,11 +72,6 @@ steps: event: - push - pull_request - branch: - - feature/** - - dev - - release - - release/** type-check: image: *node_image @@ -99,11 +88,6 @@ steps: event: - push - pull_request - branch: - - feature/** - - dev - - release - - release/** security-scan: image: *node_image @@ -121,11 +105,6 @@ steps: event: - push - pull_request - branch: - - feature/** - - dev - - release - - release/** # ============================================ # 阶段2: 测试 (仅dev分支,允许失败) @@ -177,21 +156,55 @@ steps: - dev # ============================================ - # 阶段3: 构建生产产物 (release分支) + # 阶段3: 构建并部署到生产环境 (仅release分支) # ============================================ - build-artifacts: + build-and-deploy: image: *node_image environment: NODE_ENV: production NEXT_TELEMETRY_DISABLED: 1 + SSH_PRIVATE_KEY: + from_secret: ssh_private_key depends_on: - lint - type-check commands: - - echo "Building production artifacts..." + - echo "=== Step 1: 构建生产产物 ===" - npm run build - - echo "✅ Build completed" + - echo "✅ 构建完成" - ls -la dist/ + + - echo "=== Step 2: 部署到生产环境 ===" + - mkdir -p ~/.ssh + - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa + - chmod 600 ~/.ssh/id_rsa + - ssh-keyscan -H 139.155.109.62 >> ~/.ssh/known_hosts + + - echo "Pre-deployment checks..." + - ssh root@139.155.109.62 "echo 'Server connection OK'" + - ssh root@139.155.109.62 "df -h | grep -E '/$|/home'" + + - echo "Syncing build artifacts to production server..." + - rsync -avz --delete dist/ root@139.155.109.62:/home/novalon/docker-app/novalon-website/dist/ + - rsync -avz public/ root@139.155.109.62:/home/novalon/docker-app/novalon-website/public/ + - rsync -avz package.json package-lock.json root@139.155.109.62:/home/novalon/docker-app/novalon-website/ + - rsync -avz Dockerfile.prod docker-compose.server.yml root@139.155.109.62:/home/novalon/docker-app/novalon-website/ + - rsync -avz scripts/deploy-production.sh root@139.155.109.62:/home/novalon/docker-app/novalon-website/scripts/ + - rsync -avz .env.production root@139.155.109.62:/home/novalon/docker-app/novalon-website/ 2>/dev/null || echo "No .env.production file" + + - | + ssh root@139.155.109.62 << 'EOF' + set -e + cd /home/novalon/docker-app/novalon-website + + if [ -f docker-compose.server.yml ]; then + mv docker-compose.server.yml docker-compose.yml + fi + + chmod +x scripts/deploy-production.sh + ./scripts/deploy-production.sh + EOF + - echo "✅ 生产环境部署完成!" volumes: - /tmp/npm-cache:/root/.npm - /tmp/node-modules-cache:/woodpecker/src/node_modules @@ -203,57 +216,7 @@ steps: - release/** # ============================================ - # 阶段4: 部署到生产环境 (release分支) - # ============================================ - deploy-production: - image: alpine/git:latest - environment: - DEPLOY_ENV: production - SSH_PRIVATE_KEY: - from_secret: ssh_private_key - depends_on: - - build-artifacts - commands: - - echo "Deploying to production environment..." - - mkdir -p ~/.ssh - - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa - - chmod 600 ~/.ssh/id_rsa - - ssh-keyscan -H 139.155.109.62 >> ~/.ssh/known_hosts - - - echo "Pre-deployment checks..." - - ssh root@139.155.109.62 "echo 'Server connection OK'" - - ssh root@139.155.109.62 "df -h | grep -E '/$|/home'" - - - echo "Syncing build artifacts to production server..." - - rsync -avz --delete dist/ root@139.155.109.62:/home/novalon/docker-app/novalon-website/dist/ - - rsync -avz public/ root@139.155.109.62:/home/novalon/docker-app/novalon-website/public/ - - rsync -avz package.json package-lock.json root@139.155.109.62:/home/novalon/docker-app/novalon-website/ - - rsync -avz Dockerfile.prod docker-compose.server.yml root@139.155.109.62:/home/novalon/docker-app/novalon-website/ - - rsync -avz scripts/deploy-production.sh root@139.155.109.62:/home/novalon/docker-app/novalon-website/scripts/ - - rsync -avz .env.production root@139.155.109.62:/home/novalon/docker-app/novalon-website/ 2>/dev/null || echo "No .env.production file" - - - | - ssh root@139.155.109.62 << 'EOF' - set -e - cd /home/novalon/docker-app/novalon-website - - if [ -f docker-compose.server.yml ]; then - mv docker-compose.server.yml docker-compose.yml - fi - - chmod +x scripts/deploy-production.sh - ./scripts/deploy-production.sh - EOF - - echo "✅ Production deployment completed!" - when: - event: - - push - branch: - - release - - release/** - - # ============================================ - # 阶段5: 归档到main分支 (release分支) + # 阶段4: 归档到main分支 (仅release分支) # ============================================ archive-to-main: image: alpine/git:latest @@ -261,7 +224,7 @@ steps: SSH_PRIVATE_KEY: from_secret: ssh_private_key depends_on: - - deploy-production + - build-and-deploy commands: - echo "Archiving to main branch..." - mkdir -p ~/.ssh @@ -272,20 +235,20 @@ steps: set -e git config --global user.email "ci@novalon.cn" git config --global user.name "Woodpecker CI" - + git remote set-url origin git@git.f.novalon.cn:novalon/novalon-website.git git fetch origin - + CURRENT_BRANCH="${CI_COMMIT_BRANCH}" echo "Current branch: $CURRENT_BRANCH" - + git checkout main git pull origin main git merge "$CURRENT_BRANCH" --no-ff -m "chore: 归档${CURRENT_BRANCH} ${CI_COMMIT_SHA:0:7}" - + VERSION_TAG="v$(date +%Y.%m.%d)-${CI_COMMIT_SHA:0:7}" git tag -a "$VERSION_TAG" -m "Release $(date +%Y-%m-%d) from ${CURRENT_BRANCH}" - + for i in {1..3}; do if git push origin main && git push origin --tags; then echo "✅ Archive succeeded! Version: $VERSION_TAG" @@ -294,7 +257,7 @@ steps: echo "Retry $i/3..." sleep 5 done - + echo "⚠️ Archive failed, but deployment succeeded" exit 0 when: @@ -303,11 +266,9 @@ steps: branch: - release - release/** - status: - - success # ============================================ - # 阶段6: 企业微信通知 + # 阶段5: 企业微信通知 # ============================================ notify-wechat-success: image: curlimages/curl:latest @@ -324,8 +285,6 @@ steps: branch: - release - release/** - status: - - success notify-wechat-failure: image: curlimages/curl:latest @@ -333,7 +292,7 @@ steps: WECHAT_WEBHOOK: from_secret: wechat_webhook depends_on: - - deploy-production + - build-and-deploy commands: - sh scripts/notify-wechat.sh failure when: @@ -342,8 +301,6 @@ steps: branch: - release - release/** - status: - - failure workspace: base: /woodpecker