variables: - &node_image node:20-alpine steps: install-deps: image: *node_image environment: NODE_ENV: development commands: - npm ci --cache /tmp/npm-cache --prefer-offline volumes: - /tmp/npm-cache:/root/.npm - /tmp/node-modules-cache:/woodpecker/src/node_modules when: event: - push - pull_request lint: image: *node_image environment: NODE_ENV: development depends_on: - install-deps commands: - npm run lint volumes: - /tmp/npm-cache:/root/.npm - /tmp/node-modules-cache:/woodpecker/src/node_modules when: event: - push - pull_request type-check: image: *node_image environment: NODE_ENV: development depends_on: - install-deps commands: - npm run type-check volumes: - /tmp/npm-cache:/root/.npm - /tmp/node-modules-cache:/woodpecker/src/node_modules when: event: - push - pull_request security-scan: image: *node_image environment: NODE_ENV: production HUSKY: 0 depends_on: - install-deps commands: - npm audit --audit-level=high --omit=dev volumes: - /tmp/npm-cache:/root/.npm - /tmp/node-modules-cache:/woodpecker/src/node_modules when: event: - push - pull_request unit-tests: image: *node_image environment: NODE_ENV: test CI: true depends_on: - lint - type-check commands: - npm run test:unit -- --coverage --coverageReporters=text-summary --forceExit 2>&1 | tee test-results.txt || true - echo "Unit tests completed." failure: ignore volumes: - /tmp/npm-cache:/root/.npm - /tmp/node-modules-cache:/woodpecker/src/node_modules when: event: - push - pull_request branch: - dev e2e-tests: image: mcr.microsoft.com/playwright:v1.48.0-jammy environment: NODE_ENV: test CI: true BASE_URL: http://localhost:3000 TEST_TIER: standard depends_on: - unit-tests commands: - npm run build - npx playwright install chromium --with-deps - npm run test:e2e failure: ignore volumes: - /tmp/npm-cache:/root/.npm - /tmp/node-modules-cache:/woodpecker/src/node_modules - /tmp/playwright-cache:/root/.cache/ms-playwright when: event: - push branch: - dev build-and-deploy: image: *node_image environment: NODE_ENV: production NEXT_TELEMETRY_DISABLED: 1 SSH_PRIVATE_KEY: from_secret: ssh_private_key depends_on: - install-deps - lint - type-check commands: - echo "Checking SSH client availability" - which ssh && echo "SSH client found" || echo "Installing SSH client" - apk add --no-cache openssh-client rsync - echo "Building production artifacts" - npm run build - echo "Build completed" - ls -la dist/ - echo "Deploying to production" - mkdir -p ~/.ssh - 'echo "SSH key length: $(echo $SSH_PRIVATE_KEY | wc -c)"' - 'echo "First 50 chars of SSH key: $(echo $SSH_PRIVATE_KEY | cut -c1-50)"' - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - echo "SSH key file created, checking permissions:" - ls -la ~/.ssh/ - 'echo "Testing SSH connection..."' - 'ssh -o StrictHostKeyChecking=no root@139.155.109.62 "echo Server connection OK"' - 'ssh -o StrictHostKeyChecking=no root@139.155.109.62 "df -h | grep -E \"^/\" | grep -E \"/$|/home\""' - 'echo "Syncing build artifacts to production server"' - 'rsync -avz --delete -e "ssh -o StrictHostKeyChecking=no" dist/ root@139.155.109.62:/home/novalon/docker-app/novalon-website/dist/' - 'rsync -avz -e "ssh -o StrictHostKeyChecking=no" public/ root@139.155.109.62:/home/novalon/docker-app/novalon-website/public/' - 'rsync -avz -e "ssh -o StrictHostKeyChecking=no" package.json package-lock.json root@139.155.109.62:/home/novalon/docker-app/novalon-website/' - 'rsync -avz -e "ssh -o StrictHostKeyChecking=no" Dockerfile.prod docker-compose.server.yml root@139.155.109.62:/home/novalon/docker-app/novalon-website/' - 'rsync -avz -e "ssh -o StrictHostKeyChecking=no" scripts/deploy-production.sh root@139.155.109.62:/home/novalon/docker-app/novalon-website/scripts/' - 'rsync -avz -e "ssh -o StrictHostKeyChecking=no" .env.production root@139.155.109.62:/home/novalon/docker-app/novalon-website/ 2>/dev/null || echo "No .env.production file"' - 'ssh -o StrictHostKeyChecking=no root@139.155.109.62 "cd /home/novalon/docker-app/novalon-website && [ -f docker-compose.server.yml ] && mv docker-compose.server.yml docker-compose.yml; chmod +x scripts/deploy-production.sh && ./scripts/deploy-production.sh"' - 'echo "Production deployment completed"' volumes: - /tmp/npm-cache:/root/.npm - /tmp/node-modules-cache:/woodpecker/src/node_modules when: event: - push branch: - release - release/** archive-to-main: image: node:20-alpine environment: SSH_PRIVATE_KEY: from_secret: ssh_private_key depends_on: - build-and-deploy commands: - echo "=== Archiving to main branch (增强可靠性版本) ===" - echo "当前容器信息:" - 'echo "主机名: $(hostname)"' - 'echo "IP地址: $(hostname -i)"' - echo "" - echo "" - echo "1. 安装必要的工具" - apk add --no-cache git openssh-client curl bind-tools netcat-openbsd - echo "" - echo "2. 配置SSH环境" - mkdir -p ~/.ssh - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - echo "✅ SSH私钥文件已创建" - 'ls -la ~/.ssh/id_rsa' - 'wc -c < ~/.ssh/id_rsa' - echo "" - echo "3. 配置Git服务器主机密钥" - ssh-keyscan -H -p 22 git.f.novalon.cn >> ~/.ssh/known_hosts - echo "✅ Git服务器主机密钥已添加" - echo "" - echo "4. 增强网络连接测试" - echo "测试DNS解析:" - 'dig +short git.f.novalon.cn || nslookup git.f.novalon.cn || echo "DNS解析测试完成"' - echo "测试端口连通性:" - 'timeout 10 nc -zv git.f.novalon.cn 22 && echo "✅ SSH端口可达" || echo "❌ SSH端口不可达"' - echo "" - echo "5. 增强SSH连接测试" - echo "测试SSH连接到Git服务器..." - 'timeout 15 ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 -o BatchMode=yes -T git@git.f.novalon.cn "echo \"✅ SSH连接成功\"" 2>&1 || echo "❌ SSH连接失败,但继续执行"' - echo "" - echo "6. 配置Git用户信息" - git config --global user.email "ci@novalon.cn" - git config --global user.name "Woodpecker CI" - echo "✅ Git用户信息已配置" - echo "" - echo "7. 配置Git远程仓库" - git remote set-url origin git@git.f.novalon.cn:novalon/novalon-website.git - echo "✅ Git远程仓库已配置" - echo "" - echo "8. 增强Git远程访问测试" - echo "测试Git远程仓库访问权限..." - 'timeout 10 git ls-remote origin --heads 2>&1 | head -5 && echo "✅ Git远程访问成功" || echo "❌ Git远程访问失败,但继续执行"' - echo "" - echo "9. 执行归档操作(增强错误处理)" - 'export CURRENT_BRANCH="${CI_COMMIT_BRANCH}"' - 'echo "当前分支: $CURRENT_BRANCH"' - 'echo "提交SHA: ${CI_COMMIT_SHA:0:7}"' - echo "" - echo "9.1 切换到main分支" - 'git checkout main || { echo "❌ 切换到main分支失败"; exit 1; }' - echo "" - echo "9.2 拉取最新main分支" - 'git pull origin main --no-rebase || { echo "❌ 拉取main分支失败"; exit 1; }' - echo "" - echo "9.3 合并当前分支到main" - 'git merge "$CURRENT_BRANCH" --no-ff -m "archive: $CURRENT_BRANCH → main [CI]" || { echo "❌ 合并分支失败"; exit 1; }' - echo "" - echo "9.4 创建版本标签" - 'export VERSION_TAG="v$(date +%Y.%m.%d)-${CI_COMMIT_SHA:0:7}"' - 'git tag -a "$VERSION_TAG" -m "Release: $CURRENT_BRANCH → $VERSION_TAG [CI]" || { echo "❌ 创建标签失败"; exit 1; }' - echo "" - echo "9.5 推送到远程仓库" - 'git push origin main || { echo "❌ 推送main分支失败"; exit 1; }' - 'git push origin --tags || { echo "❌ 推送标签失败"; exit 1; }' - echo "" - 'echo "✅ 归档成功完成!版本: $VERSION_TAG"' when: event: - push branch: - release - release/** notify-wechat-success: image: curlimages/curl:latest environment: WECHAT_WEBHOOK: from_secret: wechat_webhook depends_on: - archive-to-main commands: - sh scripts/notify-wechat.sh success when: event: - push branch: - release - release/** notify-wechat-failure: image: curlimages/curl:latest environment: WECHAT_WEBHOOK: from_secret: wechat_webhook depends_on: - build-and-deploy commands: - sh scripts/notify-wechat.sh failure when: event: - push branch: - release - release/** workspace: base: /woodpecker path: src clone: git: image: woodpeckerci/plugin-git settings: depth: 1 partial: false lfs: false