docs: 部署计划吸收审查4项修正——tar排除.venv(97MB)/敏感扫描前置/mkdir先于参数解析/脏注入具体化/用户背景模板提醒
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -34,7 +34,14 @@ deploy/logs/
|
||||
/模拟访客测试页.html
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 核实 logo 改动组内聚后提交**(5 文件是一组:art-logo 改用 common/logo.png、LoginLeftView 引用调整、两个 logo 文件、logo-w 替换——`git diff` 快速确认无其他意外改动混入;**提交前跑 `cd admin && npx vue-tsc --noEmit` 确认引用可解析**):
|
||||
- [ ] **Step 2: 敏感文件扫描**(spec Part 1 要求,推送前必做——m1+main 全历史将进新远程仓):
|
||||
|
||||
```bash
|
||||
git ls-files | grep -iE '\.env$|\.pem$|id_rsa|secret|password' ; echo "exit=$?" # 预期仅 .env.example 类
|
||||
git ls-files assets/ | head -20 # 目检设计稿清单(6.6MB/11 文件已核,确认无意外内容)
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 核实 logo 改动组内聚后提交**(5 文件是一组:art-logo 改用 common/logo.png、LoginLeftView 引用调整、两个 logo 文件、logo-w 替换——`git diff` 快速确认无其他意外改动混入;**提交前跑 `cd admin && npx vue-tsc --noEmit` 确认引用可解析**):
|
||||
|
||||
```bash
|
||||
git add .gitignore assets/ admin/src/assets/images/ admin/src/components/core/base/art-logo/index.vue admin/src/components/core/views/login/LoginLeftView.vue
|
||||
@@ -43,7 +50,7 @@ git commit -m "chore: 入库设计稿与品牌logo资产,gitignore部署日志
|
||||
Co-Authored-By: Claude Code <noreply@anthropic.com>"
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 确认 `git status --porcelain` 为空**(工作区干净——后续推送与部署门禁的前提)
|
||||
- [ ] **Step 4: 确认 `git status --porcelain` 为空**(工作区干净——后续推送与部署门禁的前提)
|
||||
|
||||
### Task 2: 接入远程仓并推送
|
||||
|
||||
@@ -81,6 +88,7 @@ Expected: 推送成功。若报认证失败(`terminal prompts disabled` / 401
|
||||
# 用法: bash deploy/deploy.sh <server|admin|sdk|all> [--skip-tests] [--allow-dirty]
|
||||
# 红线: 只操作 /opt/cms-cj 与 tracker-web 容器,绝不触碰服务器其他项目
|
||||
set -euo pipefail
|
||||
SECONDS=0
|
||||
|
||||
# ---- 配置(环境变量可覆盖)----
|
||||
JUMP_HOST="${JUMP_HOST:-root@8.152.213.143}"
|
||||
@@ -102,6 +110,8 @@ log() { echo "[$(date +%H:%M:%S)] $*" | tee -a "$LOG_FILE"; }
|
||||
die() { log "✗ 失败: $*(部署中止,此前已完成的目标保持已部署状态)"; exit 1; }
|
||||
ok() { log "✓ $*"; }
|
||||
|
||||
mkdir -p "$LOG_DIR" # 必须先于参数解析——早期 die() 也要能写日志
|
||||
|
||||
# ---- 参数解析 ----
|
||||
TARGET_CURRENT="preflight"; STEP="-"
|
||||
[ $# -ge 1 ] || die "用法: bash deploy/deploy.sh <server|admin|sdk|all> [--skip-tests] [--allow-dirty]"
|
||||
@@ -118,8 +128,6 @@ case "$DEPLOY_TARGET" in
|
||||
server|admin|sdk|all) ;;
|
||||
*) die "目标必须是 server|admin|sdk|all,收到: $DEPLOY_TARGET" ;;
|
||||
esac
|
||||
|
||||
mkdir -p "$LOG_DIR"
|
||||
GIT_HASH="$(git -C "$REPO_DIR" rev-parse --short HEAD)"
|
||||
log "===== 部署开始 target=$DEPLOY_TARGET git=$GIT_HASH ====="
|
||||
|
||||
@@ -152,13 +160,13 @@ build_admin() {
|
||||
STEP="admin 构建"
|
||||
(cd "$REPO_DIR/admin" && NODE_OPTIONS=--max-old-space-size=6144 pnpm build >>"$LOG_FILE" 2>&1) \
|
||||
|| die "admin 构建失败(本机 OOM 则确认 NODE_OPTIONS 已带)"
|
||||
ok "admin 构建完成: $(grep -oE 'index-[A-Za-z0-9_-]+\.js' admin/dist/index.html | head -1)"
|
||||
ok "admin 构建完成: $(grep -oE 'index-[A-Za-z0-9_-]+\.js' "$REPO_DIR/admin/dist/index.html" | head -1)"
|
||||
}
|
||||
build_sdk() {
|
||||
STEP="sdk 构建"
|
||||
(cd "$REPO_DIR/sdk" && node build.mjs >>"$LOG_FILE" 2>&1) || die "sdk 构建失败"
|
||||
local gz; gz=$(gzip -c "$REPO_DIR/sdk/dist/v1.js" | wc -c)
|
||||
ok "sdk 构建完成: build_id=$(grep -oE '[0-9a-f]{7,9}-2026[0-9]{8}' sdk/dist/v1.js | head -1) gzip=${gz}B"
|
||||
ok "sdk 构建完成: build_id=$(grep -oE '[0-9a-f]{7,9}-2026[0-9]{8}' "$REPO_DIR/sdk/dist/v1.js" | head -1) gzip=${gz}B"
|
||||
}
|
||||
transfer() { # $1=包内路径列表(空格分隔,相对 REPO_DIR) $2=远端包名
|
||||
STEP="传输与落位"
|
||||
@@ -176,7 +184,8 @@ rebuild_web() {
|
||||
"${SSH[@]}" "cd $REMOTE_DIR/deploy && docker compose up -d --build web" >>"$LOG_FILE" 2>&1 \
|
||||
|| die "docker compose 重建失败"
|
||||
sleep 5
|
||||
local status; status="$("${SSH[@]}" "docker ps --filter name=tracker-web --format '{{.Status}}'")"
|
||||
local status; status="$("${SSH[@]}" "docker ps --filter name=tracker-web --format '{{.Status}}'")" \
|
||||
|| die "容器状态查询失败"
|
||||
echo "$status" | grep -q "^Up" || die "tracker-web 容器状态异常: $status"
|
||||
ok "tracker-web: $status"
|
||||
}
|
||||
@@ -189,7 +198,8 @@ verify_admin_hash() {
|
||||
STEP="admin 哈希验证"
|
||||
local local_h online_h
|
||||
local_h=$(sha256sum "$REPO_DIR/admin/dist/index.html" | cut -d' ' -f1)
|
||||
online_h=$(curl -sf --max-time 20 "$SITE_URL/" | sha256sum | cut -d' ' -f1)
|
||||
online_h=$(curl -sf --max-time 20 "$SITE_URL/" | sha256sum | cut -d' ' -f1) \
|
||||
|| die "线上 index.html 拉取失败"
|
||||
[ "$local_h" = "$online_h" ] || die "admin index.html 线上哈希与本机产物不一致(部署未生效或缓存)"
|
||||
ok "admin 哈希一致"
|
||||
}
|
||||
@@ -206,8 +216,10 @@ deploy_server() {
|
||||
TARGET_CURRENT=server
|
||||
server_tests
|
||||
STEP="server 打包"
|
||||
# 排除清单镜像 server/.dockerignore 本意:Windows venv(97MB)/缓存/本地xdb 绝不上服务器
|
||||
(cd "$REPO_DIR" && tar -czf /tmp/keji-server.tgz \
|
||||
--exclude='__pycache__' --exclude='.pytest_cache' --exclude='db.sqlite3' server/) \
|
||||
--exclude='__pycache__' --exclude='.pytest_cache' --exclude='db.sqlite3' \
|
||||
--exclude='.venv' --exclude='.cache' --exclude='tracker/data/*.xdb' server/) \
|
||||
|| die "server 打包失败"
|
||||
"${SCP[@]}" /tmp/keji-server.tgz "$TARGET_HOST:/tmp/" >/dev/null 2>&1 || die "scp 失败"
|
||||
rm -f /tmp/keji-server.tgz
|
||||
@@ -242,7 +254,7 @@ esac
|
||||
log "===== 部署完成 target=$DEPLOY_TARGET git=$GIT_HASH 用时 $SECONDS 秒 ====="
|
||||
```
|
||||
|
||||
**实现者注意**:`$SECONDS` 需在脚本开头(`set -euo pipefail` 之后)加 `SECONDS=0` 重置;落盘即上方最终形态,勿再引入管道判断测试退避的写法(pytest/vitest 退出码即真相,输出重定向进日志)。
|
||||
**实现者注意**:落盘即上方最终形态(含 SECONDS=0/mkdir 前置/tar 全排除清单/die 包装),勿再引入管道判断测试退避的写法(pytest/vitest 退出码即真相,输出重定向进日志)。
|
||||
|
||||
- [ ] **Step 2: 语法检查**:`bash -n deploy/deploy.sh`(无输出即通过)
|
||||
- [ ] **Step 3: 本地无副作用的参数面测试**:`bash deploy/deploy.sh` (无参 → 应打用法并退出 1);`bash deploy/deploy.sh foo` (非法目标 → 退出 1);`bash deploy/deploy.sh admin --bad-flag`(未知参数 → 退出 1)——三条都不应触碰网络
|
||||
@@ -312,7 +324,13 @@ JUMP_HOST=nobody@127.0.0.1 bash deploy/deploy.sh admin; echo "exit=$?"
|
||||
|
||||
Expected: 前置检查 ✗ SSH 不可达 → 退出码非 0,日志留档,**未发生任何传输**。
|
||||
|
||||
再注入一次工作区脏检查(`touch /tmp/x && git add --intent-to-add 任意文件` 太绕——直接改一个已跟踪文件加空行)验证"工作区不干净 → 拒绝部署",验完还原。
|
||||
再注入工作区脏检查(具体方法):
|
||||
|
||||
```bash
|
||||
echo "" >> CLAUDE.md # 制造未提交改动
|
||||
bash deploy/deploy.sh admin; echo "exit=$?" # 预期: 工作区不干净 → 拒绝,退出 1,未触碰网络(脏检查先于 SSH 检查)
|
||||
git checkout -- CLAUDE.md # 还原
|
||||
```
|
||||
|
||||
- [ ] **Step 3: all 完整验证**(含 server 容器秒级重启,用户已确认接受):
|
||||
|
||||
@@ -329,7 +347,7 @@ Expected: pytest→打包→容器 Up→探活→admin 构建/哈希→sdk 测
|
||||
### Task 6: 收尾推送
|
||||
|
||||
- [ ] **Step 1: 推送全部新提交:`git push origin m1`**(deploy.sh/CLAUDE.md/gitignore 等)
|
||||
- [ ] **Step 2: 汇报**:远程仓两分支状态、脚本用法示例、验证结果摘要(admin 首验/失败注入/all)、部署日志位置
|
||||
- [ ] **Step 2: 汇报**:远程仓两分支状态、脚本用法示例、验证结果摘要(admin 首验/失败注入/all)、部署日志位置;**提醒用户**:其会话粘贴的"项目背景"模板中"部署策略"段更新为指向 `bash deploy/deploy.sh`(spec Part 3 第 2 条,用户侧自行更新)
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user