name: CI on: push: branches: [main, master] pull_request: permissions: contents: read # 站点目录(宿主路径)。Nginx 容器挂载的就是这个目录,rsync 原地同步后立即生效。 env: DEPLOY_PATH: /opt/kanban/dist jobs: verify: runs-on: ubuntu-latest timeout-minutes: 15 steps: - name: 检出代码 uses: actions/checkout@v4 # 这是自托管 runner(host 模式),宿主机已装 Node 22, # 不再用 setup-node 下载一份到 tool cache,省时间和磁盘。 - name: 环境确认 run: node -v && npm -v - name: 安装依赖 run: npm ci - name: 代码检查 run: npm run lint - name: 类型检查 run: npm run typecheck - name: 单元测试 run: npm run test - name: 生产构建 run: npm run build # 只在 master / main 的 push、且 verify 全绿之后部署。 # runner 与站点同机,直接写本地目录,不需要 SSH 密钥和任何 Secrets。 deploy: name: 部署到服务器 needs: verify if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') runs-on: ubuntu-latest timeout-minutes: 10 steps: - name: 检出代码 uses: actions/checkout@v4 - name: 安装依赖 run: npm ci - name: 生产构建 run: npm run build # 站点目录是容器的 bind mount,不能整目录 mv(挂载跟 inode,容器会看不到新内容), # 必须原地同步:rsync 覆盖变化文件并删掉上一版残留的 hash 资源。 - name: 同步到站点目录 run: | set -euo pipefail rsync -a --delete dist/ "$DEPLOY_PATH"/ echo "已同步到 $DEPLOY_PATH" - name: 线上自检 run: | set -euo pipefail for i in 1 2 3 4 5; do curl -fsS -o /dev/null http://127.0.0.1:8081/ && break echo "第 $i 次探测失败,1 秒后重试" sleep 1 done curl -fsS -o /dev/null http://127.0.0.1:8081/ code=$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8081/today) echo "根路径 200,/today $code" [ "$code" = "200" ] || { echo "SPA 回落异常:/today 返回 $code"; exit 1; } curl -fsS http://127.0.0.1:8081/sw.js >/dev/null echo "自检通过"