chore(ops): track deploy-frontend.sh and its launchd plist - #443
Conversation
This has been sitting untracked in the working tree while being the actual production deploy path for the frontend, so the knowledge it encodes lived on one machine only. What it knows that nothing else in the repo does: the frontend runs under the launchd agent `io.aastar.yaa-frontend` (KeepAlive + RunAtLoad, `next start -p 5173`) behind the cloudflared tunnel, so `npm run build` alone leaves the *running* process serving a stale chunk manifest — /_next/static/chunks/* return 500, hydration fails, white screen. The script builds, kickstarts the agent, waits for :5173, then verifies every chunk the homepage references returns 200 (`--public` runs the same check through Cloudflare end-to-end). It also refuses to restart if the build fails, so a broken build can't take the running site down. Sits at the repo root next to the existing dev helpers (backend.sh / dev.sh / frontend.sh) — frontend.sh starts dev, this deploys prod. Mode 100755, no secrets (public hostname, port and agent label only). Gap noted, not addressed here: the referenced `io.aastar.yaa-frontend.plist` lives in ~/Library/LaunchAgents and is still untracked, so the script's precondition can't be reproduced from the repo. `scripts/ops/` already tracks io.aastar.yaa-monitor.plist and would be its natural home — left out because that file is machine-specific and its contents should be reviewed before committing. Claude-Session: https://claude.ai/code/session_01BxmyQj2A82DfFXu97kKACk
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 21e6af1a6b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| # 4) verify every referenced JS/CSS chunk is 200 (this is the white-screen guard) | ||
| verify_chunks() { | ||
| local base="$1" label="$2" total=0 bad=0 code | ||
| local home; home="$(curl -s "$base/")" || die "$label: could not fetch homepage" |
There was a problem hiding this comment.
Fail public verification on HTTP errors
When --public is used and Cloudflare/the public origin returns an HTTP error page such as 404 or 502, curl -s still exits successfully unless --fail is used (curl --help all describes --fail as failing on HTTP errors). That error HTML can contain no /_next/static/... links, leaving total=0 and bad=0, so the script reports deploy OK even though the end-to-end public site is down; check the homepage status or use curl -fsS before accepting the chunk check.
Useful? React with 👍 / 👎.
Without this, the script's own precondition can't be reproduced from the repo — it just dies with "launchd agent io.aastar.yaa-frontend not loaded" and the reader has no way to find out what that agent should contain. Copied verbatim from the running ~/Library/LaunchAgents/ definition; verified with `plutil -lint` and by diffing both files through `plutil -convert xml1`, which match apart from the added comment header. No secrets (paths, a label and a node path only). Follows the convention io.aastar.yaa-monitor.plist already set in this directory: committed as-is with absolute /Users/jason paths rather than templated, plus an install-command header. The header additionally flags the pinned nvm node path (v22.22.2), because a node upgrade makes the agent fail to start silently — the only trace is ~/Library/Logs/yaa-frontend.log. Claude-Session: https://claude.ai/code/session_01BxmyQj2A82DfFXu97kKACk
clestons
left a comment
There was a problem hiding this comment.
Review — #443 (chore(ops): track deploy-frontend.sh + its 依赖的 launchd plist)
结论:APPROVE
把之前一直躺在工作区里没纳入版本控制、但实际是生产部署路径的 deploy-frontend.sh 和它依赖的 io.aastar.yaa-frontend.plist 收进仓库。2 个 commit,逻辑自洽:脚本本身 + 它引用的 launchd agent 定义,PR 描述里说得很清楚这两个东西为什么绑在一起(脚本的 precondition 靠 plist 才能在仓库里复现)。
独立动手验证(不是只信 PR 自述)
bash -n deploy-frontend.sh→ 语法通过- 密钥扫描(
grep -iE "sk-|api[_-]?key|secret|token|password|-----BEGIN") → 干净,没有真凭据 plutil -lint新增的 plist → OK- 把 PR 里的 plist 跟你机器上实际装着的
~/Library/LaunchAgents/io.aastar.yaa-frontend.plist做了字节级 diff(先各自转成 xml1 规范化格式)——完全一致,坐实了 PR 描述里"逐字拷贝、已验证匹配"这个说法 - 文件权限确认
deploy-frontend.sh是755(可执行),符合预期
PK Summary
- R1 DeepSeek(flash):跑了,2 条 finding,都站不住。① 说
curl -s "$base/"没有|| die兜底失败——直接读 diff,这行就在那,|| die "$label: could not fetch homepage"字面意思都对不上它说的"没有"。② 说 chunk 正则可能漏掉带 query string 的 URL——技术上这条正则确实会把?v=123这类后缀从匹配结果里去掉,但去掉 query string 不影响静态文件本身能不能拿到 200(chunk 文件本身不靠 query string 才能命中),所以"会漏掉验证"这个结论不成立,顶多是"验证的时候没带上 query string",检查本身不受影响。 - Codex PK:跳过。纯 ops 脚本/配置追踪,我自己动手验证(语法+密钥扫描+plist 校验+跟活体文件字节比对)已经比读代码更硬,没有可对抗的业务逻辑。
自评 — #443
- 轮数:R1 DeepSeek(真跑)+ 我独立动手验证(bash -n / 密钥扫描 / plutil -lint / 跟你机器上活体 plist 字节比对)。纯 ops 追踪 PR,未跑 Codex PK。
- 机械证据:全部是真实命令的输出(不是读代码猜),尤其是跟活体
~/Library/LaunchAgents/里的文件做字节级比对,这个验证力度比单纯审 diff 更强。 - DeepSeek flash 评级:1/5 —— 2 条 finding 一条是明显误读自己输入内容的假阳性(跟之前几轮同类错误一样),一条技术上有影可循但结论不成立。样本 10/20,进度过半。
- 与 skill 设计一致,未跑 Codex PK(无可挑战逻辑,我自己的动手验证已经足够)。
R1 DeepSeek 真跑 + 独立动手验证(语法/密钥扫描/plist lint/跟活体文件字节比对),纯 ops PR 未跑 Codex PK。
把前端生产部署路径完整收进仓库:部署脚本 + 它依赖的 launchd 定义。
1.
deploy-frontend.sh(根目录)这个脚本一直在工作区里未跟踪,但它是前端实际的生产部署路径 —— 它编码的运维知识此前只存在于一台机器上。
它知道仓库里别处没有的事:前端跑在 launchd agent
io.aastar.yaa-frontend下(KeepAlive+RunAtLoad,next start -p 5173),前面挂 cloudflared 隧道。所以只跑npm run build是不够的 —— 正在运行的进程仍服务旧的 chunk manifest,/_next/static/chunks/*返回 500,hydration 失败,白屏。脚本:build → kickstart agent → 等 :5173 → 逐个校验首页引用的每个 JS/CSS chunk 是否 200(
--public再走一遍 Cloudflare 端到端)。且 build 失败时拒绝重启,坏构建不会把正在跑的站点带下去。放根目录,和现有
backend.sh/dev.sh/frontend.sh并列 ——frontend.sh起开发,这个部署生产。模式100755。2.
scripts/ops/io.aastar.yaa-frontend.plist没有它,脚本的前置条件在仓库里重建不出来 —— 只会报
launchd agent not loaded,而读者无从得知这个 agent 该长什么样。从本机运行中的
~/Library/LaunchAgents/定义原样复制。验证方式:plutil -lint通过,并把两个文件都经plutil -convert xml1归一化后 diff —— 除新增的注释头外完全一致,即提交的就是本机在跑的那份。遵循同目录
io.aastar.yaa-monitor.plist已定的约定:带绝对/Users/jason路径原样提交(不做模板化),配一个安装命令注释头。注释头额外标注了写死的 nvm node 路径(v22.22.2)—— node 升级后 agent 会静默启动失败,唯一线索是~/Library/Logs/yaa-frontend.log。Verification
bash -n deploy-frontend.shplutil -lintplistgit ls-files -s100755可执行位保留Claude-Session: https://claude.ai/code/session_01BxmyQj2A82DfFXu97kKACk