Repository navigation
123 lines (115 loc) · 6.21 KB
/
Copy pathdeploy.yml
File metadata and controls
123 lines (115 loc) · 6.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
name: Deploy to Cloudflare Pages
# 把 mox-site 纯静态官网部署到 Cloudflare Pages(首次运行自动创建同名项目)。
# 由 GitHub Actions runner(有公网)经 wrangler 调 Cloudflare,沙箱无需直连。
#
# 诊断通道:沙箱无法下载 Actions 日志 zip,故 Deploy 步骤把 wrangler 的完整输出
# tee 到 /tmp/w.log,失败时用 gh issue 暴露(与 moxsh 的 build.yml 同一手法),
# 以便在沙箱内也能读到真实报错(多为 token 权限 / accountId 问题)。
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
deployments: write
issues: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Deploy to Cloudflare Pages
id: deploy
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
npm i -g wrangler@3 2>&1 | tail -3 | tee /tmp/w.log
echo "### wrangler whoami" | tee -a /tmp/w.log
npx wrangler whoami 2>&1 | tee -a /tmp/w.log
echo "### ensure project exists" | tee -a /tmp/w.log
# wrangler pages deploy 不会自动创建项目;先建(已存在时报错可忽略)
npx wrangler pages project create mox-site --production-branch main 2>&1 | tee -a /tmp/w.log || true
echo "### wrangler pages deploy" | tee -a /tmp/w.log
set +e
npx wrangler pages deploy . --project-name=mox-site --branch=main 2>&1 | tee -a /tmp/w.log
rc=${PIPESTATUS[0]}
echo "DEPLOY_RC=$rc" | tee -a /tmp/w.log
exit $rc
- name: 部署后安全验证(安全头 + CSP + 资源完整性)
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
S=0
BASE="https://mox-site.pages.dev"
echo "验证目标: $BASE"
# 生产域名/边缘传播就绪重试(最多 ~2 分钟)
code=000
for i in 1 2 3 4 5 6 8 10 12; do
code=$(curl -sS -m 15 -o /dev/null -w '%{http_code}' "$BASE/" || echo 000)
[ "$code" = "200" ] && break
echo "等待生产域名就绪($code)… 第 $i 次"; sleep 12
done
[ "$code" = "200" ] && echo "OK: 首页 200" || { echo "FAIL: 首页不可达($code)"; S=1; }
# 注意:验证一律打 / 根路径;/index.html 可能命中边缘缓存的旧版本
H=$(curl -sS -m 20 -D - -o /dev/null "$BASE/")
for hdr in 'x-frame-options: DENY' 'x-content-type-options: nosniff' 'referrer-policy:' 'strict-transport-security:' 'permissions-policy:'; do
echo "$H" | grep -qi "$hdr" && echo "OK: $hdr" || { echo "FAIL: 缺 $hdr"; S=1; }
done
# 页面内容检查:CSP meta / app.js 外链 / 无内联脚本。
# 静态资源边缘传播有延迟,单次抓取可能命中尚未就绪的边缘节点,
# 故与登录反代检查一样采用重试,避免传播竞态造成的误判 FAIL。
CSP_OK=0; APP_OK=0; NOSCRIPT_OK=1
for i in 1 2 3 4 5 6 8 10 12; do
PG=$(curl -sS -m 20 "$BASE/" 2>/dev/null || true)
if [ -z "$PG" ]; then echo "首页抓取为空,第 $i 次重试…"; sleep 8; continue; fi
echo "$PG" | grep -q 'Content-Security-Policy' && CSP_OK=1
echo "$PG" | grep -q 'src="app.js"' && APP_OK=1
if echo "$PG" | grep -q '<script>'; then NOSCRIPT_OK=0; else NOSCRIPT_OK=1; fi
if [ "$CSP_OK" = 1 ] && [ "$APP_OK" = 1 ] && [ "$NOSCRIPT_OK" = 1 ]; then
echo "首页内容检查通过(第 $i 次)"; break
fi
echo "首页内容尚未就绪(CSP=$CSP_OK app=$APP_OK noscript=$NOSCRIPT_OK),第 $i 次重试…"; sleep 8
done
[ "$CSP_OK" = 1 ] && echo "OK: CSP meta" || { echo "FAIL: 首页缺 CSP"; S=1; }
[ "$APP_OK" = 1 ] && echo "OK: 外链 app.js" || { echo "FAIL: app.js 未外链"; S=1; }
[ "$NOSCRIPT_OK" = 1 ] && echo "OK: 无内联脚本" || { echo "FAIL: 仍有内联脚本"; S=1; }
# 静态资源可达性:同样带重试,避免边缘传播窗口内的瞬时 404/000
for f in app.js app.en.js index.css index.en.css; do
c=""
for i in 1 2 3 4 5 6; do
c=$(curl -sS -m 20 -o /dev/null -w '%{http_code}' "$BASE/$f" 2>/dev/null || echo 000)
[ "$c" = "200" ] && break
sleep 5
done
[ "$c" = "200" ] && echo "OK: $f 200" || { echo "FAIL: $f -> $c"; S=1; }
done
# 登录链路同域反代:start 应 302 到 GitHub 授权页(Functions 生效有传播延迟,重试)
ST=""; LOC=""
for i in 1 2 3 4 5 6 8 10 12; do
ST=$(curl -sS -m 20 -D - -o /dev/null "$BASE/auth/github/start" || true)
if echo "$ST" | grep -qiE "^HTTP/([12](\.[0-9])?)? 302" && echo "$ST" | grep -qi "^location: https://github.com/login/oauth/authorize"; then
LOC=1; break
fi
echo "等待反代生效… 第 $i 次"; sleep 10
done
echo "$ST" | grep -iE "^HTTP|^location" | sed 's/\(client_id=[A-Za-z0-9]\{6\}\)[A-Za-z0-9]*/\1****/' || true
[ -n "$LOC" ] && echo "OK: 官网同域 OAuth 反代 302 正常" || { echo "FAIL: /auth/github/start 反代未返回 302"; S=1; }
[ "$S" = 0 ] && echo "✅ 官网安全验证全绿" || {
gh issue create --title "官网安全验证失败 $(date -u +%FT%TZ)" \
--body "安全头/CSP/资源完整性检查未通过,见 Actions 日志。" 2>&1 | head -2 || true; exit 1; }
- name: 诊断失败(建 issue 暴露 wrangler 日志)
if: failure() && steps.deploy.outcome == 'failure'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
SUMMARY=$(tail -c 20000 /tmp/w.log 2>/dev/null || echo "(无日志)")
gh issue create \
--title "Pages 部署失败 $(date -u +%FT%TZ)" \
--body "$SUMMARY" 2>&1 | head -2 || true