name: Next.js CI/CD 流水线 on: push: branches: [ "main" ] pull_request: branches: [ "main" ] jobs: # 构建作业 build: runs-on: ubuntu-latest steps: # 检出代码 - name: 检出代码 uses: actions/checkout@v4 # 配置 Node.js - name: 配置 Node.js uses: actions/setup-node@v4 with: node-version: '22' # 安装 pnpm - name: 安装 pnpm uses: pnpm/action-setup@v4 with: version: 'latest' # 获取 pnpm store 目录 - name: 获取 pnpm store 目录 shell: bash run: | echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV # 缓存 pnpm 依赖 - name: 缓存 pnpm 依赖 uses: actions/cache@v4 with: path: ${{ env.STORE_PATH }} key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} restore-keys: | ${{ runner.os }}-pnpm-store- # 安装依赖 - name: 安装依赖 run: pnpm install --frozen-lockfile # 代码检查 - name: 代码检查 run: pnpm run lint --fix || true # 构建应用 - name: 构建应用 run: pnpm run build # 创建部署包 - name: 创建部署包 run: | # 创建部署目录 mkdir -p deploy-package # 复制必要文件 cp -r .next deploy-package/ cp -r public deploy-package/ cp -r src deploy-package/ cp package.json deploy-package/ cp pnpm-lock.yaml deploy-package/ cp next.config.ts deploy-package/ cp Dockerfile deploy-package/ cp .dockerignore deploy-package/ # 创建压缩包 tar -czf deploy-package.tar.gz -C deploy-package . # 上传部署包 - name: 上传部署包 uses: actions/upload-artifact@v4 with: name: deploy-package path: deploy-package.tar.gz retention-days: 7 # 部署作业(仅在 main 分支) deploy: needs: build runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' && github.event_name == 'push' steps: # 下载部署包 - name: 下载部署包 uses: actions/download-artifact@v4 with: name: deploy-package # 拷贝部署包到服务器 - name: 拷贝部署包到服务器 uses: appleboy/scp-action@master with: host: ${{ secrets.SERVER_HOST }} username: ${{ secrets.SERVER_USERNAME }} key: ${{ secrets.SERVER_SSH_KEY }} source: "deploy-package.tar.gz" target: "/vol1/1000/Docker/" overwrite: true # 在服务器上部署 - name: 在服务器上部署应用 uses: appleboy/ssh-action@master with: host: ${{ secrets.SERVER_HOST }} username: ${{ secrets.SERVER_USERNAME }} key: ${{ secrets.SERVER_SSH_KEY }} script: | set -e # 遇到错误立即退出 echo "开始部署 saas2 应用..." # 进入 Docker 目录 cd /vol1/1000/Docker/ # 创建项目目录 mkdir -p saas2 cd saas2 # 备份当前版本(如果存在) if [ -d "backup" ]; then rm -rf backup fi if [ -f "package.json" ]; then mkdir -p backup cp -r ./* backup/ 2>/dev/null || true echo "已备份当前版本" fi # 解压新版本 tar -xzf ../deploy-package.tar.gz echo "已解压新版本" # 停止并删除旧容器(如果存在) if [ "$(docker ps -q -f name=saas2-app)" ]; then echo "停止运行中的容器..." docker stop saas2-app fi if [ "$(docker ps -aq -f name=saas2-app)" ]; then echo "删除旧容器..." docker rm saas2-app fi # 删除旧镜像(如果存在) if [ "$(docker images -q saas2-app:latest)" ]; then echo "删除旧镜像..." docker rmi saas2-app:latest fi # 构建新的Docker镜像 echo "构建新的 Docker 镜像..." docker build -t saas2-app:latest . # 运行新容器 echo "启动新容器..." docker run -d \ --name saas2-app \ --restart unless-stopped \ -p 3000:3000 \ --health-cmd="curl -f http://localhost:3000/api/health || exit 1" \ --health-interval=30s \ --health-timeout=10s \ --health-retries=3 \ saas2-app:latest # 等待容器启动 echo "等待容器启动..." sleep 10 # 检查容器状态 if [ "$(docker ps -q -f name=saas2-app)" ]; then echo "✅ 部署成功!容器状态:" docker ps | grep saas2-app echo "" echo "应用访问地址: http://$(hostname -I | awk '{print $1}'):3000" else echo "❌ 部署失败!容器未能正常启动" echo "容器日志:" docker logs saas2-app || true exit 1 fi # 清理无用的Docker资源 echo "清理无用的 Docker 资源..." docker system prune -f # 删除部署包 rm -f ../deploy-package.tar.gz echo "🎉 部署完成!"