构建、部署与验收文档:Makefile、冒烟脚本、systemd/Caddy、决策与验收报告
- Makefile:web / sync-assets(go:embed 约束拷贝)/ build / linux / test / smoke / dev / vulncheck;版本信息 ldflags 注入 - scripts/smoke.sh:构建冒烟(§8.3-6)——起服务断言 SPA 资源 200 + 正确 MIME、meta 注入、安全头、私有不可见(19 项断言) - deploy:pure-note.service(沙箱加固)、每日 gc+backup 的 maint service/timer(03:00 Persistent)、Caddyfile 反代示例 - docs/decisions.md:35 条实施决策留档(D1–D35) - docs/acceptance.md:§9.3 检查单逐项证据、§13 测试组映射、 §12 里程碑门、浏览器端到端走查记录 - README.md:快速开始、测试、部署与升级/恢复 SOP
This commit is contained in:
Executable
+91
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env bash
|
||||
# 构建冒烟测试(设计 §8.3-6):
|
||||
# 启动二进制 → 创建公开/私有笔记 → 断言首页/深链 HTML 注入 meta、
|
||||
# script/link 以 200 + 正确 MIME 加载(防「深链白屏」类回归)→ 可见性抽查。
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
BIN="$ROOT/pure-note"
|
||||
TMP="$(mktemp -d)"
|
||||
PORT="${SMOKE_PORT:-18099}"
|
||||
BASE="http://127.0.0.1:$PORT"
|
||||
FAIL=0
|
||||
|
||||
cleanup() { kill "$SRV_PID" 2>/dev/null || true; rm -rf "$TMP"; }
|
||||
trap cleanup EXIT
|
||||
|
||||
ok() { echo " ✓ $1"; }
|
||||
bad() { echo " ✗ $1"; FAIL=1; }
|
||||
check() { if [ "$2" -eq 0 ]; then ok "$1"; else bad "$1"; fi }
|
||||
|
||||
echo "[smoke] 初始化数据目录"
|
||||
PN_ADMIN_PASSWORD=smoke-password-123 PN_SITE_TITLE="Smoke 站" "$BIN" init --data-dir "$TMP/data"
|
||||
|
||||
echo "[smoke] 启动服务 :$PORT"
|
||||
"$BIN" serve --data-dir "$TMP/data" --addr "127.0.0.1:$PORT" --dev >"$TMP/serve.log" 2>&1 &
|
||||
SRV_PID=$!
|
||||
for _ in $(seq 1 50); do
|
||||
curl -sf "$BASE/api/health" >/dev/null 2>&1 && break
|
||||
sleep 0.2
|
||||
done
|
||||
|
||||
echo "[smoke] 健康检查"
|
||||
curl -sf "$BASE/api/health" | grep -q '"ok"'; check "GET /api/health 200 含 ok" $?
|
||||
|
||||
echo "[smoke] 登录并创建笔记"
|
||||
CSRF=$(curl -sf -X POST "$BASE/api/auth/login" -H "Origin: $BASE" \
|
||||
-d '{"password":"smoke-password-123"}' -c "$TMP/cookies" \
|
||||
| sed 's/.*"csrf_token":"\([^"]*\)".*/\1/')
|
||||
[ -n "$CSRF" ]; check "登录获得 CSRF token" $?
|
||||
|
||||
CREATE=$(curl -sf -X POST "$BASE/api/admin/notes" -H "Origin: $BASE" -H "X-CSRF-Token: $CSRF" \
|
||||
-H 'Content-Type: application/json' -b "$TMP/cookies" \
|
||||
-d '{"title":"冒烟笔记 Smoke","content":"# Hello\n\n正文","status":"public"}')
|
||||
echo "$CREATE" | grep -q '"slug"'; check "创建公开笔记" $?
|
||||
SLUG=$(echo "$CREATE" | sed 's/.*"slug":"\([^"]*\)".*/\1/')
|
||||
|
||||
curl -sf -X POST "$BASE/api/admin/notes" -H "Origin: $BASE" -H "X-CSRF-Token: $CSRF" \
|
||||
-H 'Content-Type: application/json' -b "$TMP/cookies" \
|
||||
-d '{"title":"私有机密","content":"秘密","status":"private"}' >/dev/null
|
||||
check "创建私有笔记" $?
|
||||
|
||||
echo "[smoke] SPA 与 meta 注入"
|
||||
HOME_HTML=$(curl -sf "$BASE/")
|
||||
echo "$HOME_HTML" | grep -q '<title>Smoke 站</title>'; check "首页注入站点标题" $?
|
||||
|
||||
DEEP_HTML=$(curl -sf "$BASE/notes/$SLUG")
|
||||
echo "$DEEP_HTML" | grep -q '<title>冒烟笔记 Smoke - Smoke 站</title>'
|
||||
check "深链注入笔记标题(防白屏回归)" $?
|
||||
|
||||
JS=$(echo "$DEEP_HTML" | grep -o '/assets/index-[^"]*\.js' | head -1)
|
||||
[ -n "$JS" ]; check "HTML 含打包 script 引用" $?
|
||||
CODE=$(curl -s -o /dev/null -w '%{http_code}' "$BASE$JS" || true)
|
||||
[ "$CODE" = "200" ]; check "script 资源 200(实际 $CODE)" $?
|
||||
MIME=$(curl -s -o /dev/null -w '%{content_type}' "$BASE$JS" || true)
|
||||
echo "$MIME" | grep -q 'javascript'; check "script MIME 为 javascript(实际 $MIME)" $?
|
||||
|
||||
CSS=$(echo "$DEEP_HTML" | grep -o '/assets/index-[^"]*\.css' | head -1)
|
||||
CODE=$(curl -s -o /dev/null -w '%{http_code}' "$BASE$CSS" || true)
|
||||
[ "$CODE" = "200" ]; check "link 样式资源 200(实际 $CODE)" $?
|
||||
MIME=$(curl -s -o /dev/null -w '%{content_type}' "$BASE$CSS" || true)
|
||||
echo "$MIME" | grep -q 'text/css'; check "link MIME 为 css(实际 $MIME)" $?
|
||||
|
||||
echo "[smoke] 安全头抽查"
|
||||
H=$(curl -sI "$BASE/")
|
||||
echo "$H" | grep -qi "Strict-Transport-Security"; check "HSTS" $?
|
||||
echo "$H" | grep -qi "X-Frame-Options: DENY"; check "X-Frame-Options DENY" $?
|
||||
echo "$H" | grep -qi "X-Content-Type-Options: nosniff"; check "nosniff" $?
|
||||
echo "$H" | grep -qi "Content-Security-Policy.*script-src 'self'"; check "CSP script-src 'self'" $?
|
||||
|
||||
echo "[smoke] 可见性抽查(私有不可见)"
|
||||
if curl -sf "$BASE/api/notes" | grep -q '私有机密'; then bad "匿名列表不含私有笔记"; else ok "匿名列表不含私有笔记"; fi
|
||||
if curl -sf "$BASE/feed.xml" | grep -q '私有机密'; then bad "RSS 不含私有笔记"; else ok "RSS 不含私有笔记"; fi
|
||||
CODE=$(curl -s -o /dev/null -w '%{http_code}' "$BASE/api/notes/$SLUG")
|
||||
[ "$CODE" = "200" ]; check "公开笔记详情匿名 200(实际 $CODE)" $?
|
||||
|
||||
if [ "$FAIL" -eq 0 ]; then
|
||||
echo "[smoke] 全部通过 ✓"
|
||||
else
|
||||
echo "[smoke] 存在失败项 ✗"
|
||||
exit 1
|
||||
fi
|
||||
Reference in New Issue
Block a user