Files
novalon-website/.woodpecker.yml
T
张翔 d37b5b0fec fix(ci): 修复YAML解析错误
问题:
- YAML解析失败: cannot unmarshal type map into string
- 中文字符和多行字符串导致解析错误

修复:
- 移除中文字符,使用英文
- 将多行SSH命令改为单行命令
- 简化echo输出
2026-03-30 07:23:26 +08:00

301 lines
8.6 KiB
YAML

# ============================================
# 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: 测试 (unit-tests, e2e-tests - 仅dev分支)
# 阶段3: 构建并部署到生产环境 (release分支,一体化)
# 阶段4: 归档到main分支 (release分支)
# 阶段5: 企业微信通知
# ============================================
variables:
- &node_image node:20-alpine
steps:
# ============================================
# 阶段0: 依赖安装(统一缓存)
# ============================================
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
# ============================================
# 阶段1: 并行代码质量检查
# ============================================
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
# ============================================
# 阶段2: 测试 (仅dev分支,允许失败)
# ============================================
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
# ============================================
# 阶段3: 构建并部署到生产环境 (仅release分支)
# ============================================
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 "Step 1: Building production artifacts..."
- npm run build
- echo "Build completed"
- ls -la dist/
- echo "Step 2: Deploying to production..."
- 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 "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"
- echo "Production deployment completed!"
volumes:
- /tmp/npm-cache:/root/.npm
- /tmp/node-modules-cache:/woodpecker/src/node_modules
when:
event:
- push
branch:
- release
- release/**
# ============================================
# 阶段4: 归档到main分支 (仅release分支)
# ============================================
archive-to-main:
image: alpine/git:latest
environment:
SSH_PRIVATE_KEY:
from_secret: ssh_private_key
depends_on:
- build-and-deploy
commands:
- echo "Archiving to main branch..."
- mkdir -p ~/.ssh
- echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- ssh-keyscan -H git.f.novalon.cn >> ~/.ssh/known_hosts
- |
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"
exit 0
fi
echo "Retry $i/3..."
sleep 5
done
echo "⚠️ Archive failed, but deployment succeeded"
exit 0
when:
event:
- push
branch:
- release
- release/**
# ============================================
# 阶段5: 企业微信通知
# ============================================
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