让 AI 帮我发布一个作品,卡在三个跟业务无关的地方
昨天想干一件事:把一个小工具发布到华为云的作品展览馆。
工具本身早就写完了。真正耗掉一晚上的,是三个跟业务毫无关系的环境问题——每一个都长得像"这件事做不成"。
一、下载一个 14MB 的 zip,curl 死活下不来
官方 CLI(KooCLI,命令是 hcloud)只有一个下载地址。我用 curl:
curl -sL -o hcloud.zip https://.../huaweicloud-cli-windows-amd64.zip
# HTTP=000 size=0 退出码 35
退出码 35 是 SSL 连接错误。没有报错信息,没有 HTTP 状态码,就是零字节。
换 Python 试了一次:
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
urllib.request.urlopen(url, timeout=300, context=ctx)
# HTTP 200 | 下载字节: 14319386
一次就下来了。
同一台机器、同一个 URL,curl 拿不到、Python 拿到了。差别只有 SSL 校验策略。
我没有去深究 curl 为什么失败——在这种环境里,能拿到文件比搞清楚原因重要。
二、git push 返回 418
仓库建好了,代码提交了,然后:
git push origin master
fatal: unable to access 'https://gitcode.com/...': The requested URL returned error: 418
418 是 "I'm a teapot",一个玩笑状态码。它在 git 的场景里通常意味着服务端的 WAF 把 git 的智能 HTTP 端点拦了。
换 token、换协议、换 UA 都没用。
绕法是把 git 这条路整个换掉——直接用平台的文件 API 逐个建文件:
POST /api/v5/repos/{owner}/{repo}/contents/{path}
Header: private-token: <PAT>
Body: {"content": "<base64>", "message": "add xxx"}
五个文件,五次请求,全部 200。
代价是失去了 git 的历史和增量——但对于一个只发布一次的作品仓库,这完全不重要。
三、最像"环境坏了"的那个
真正让我差点下结论"这环境做不了"的,是第三个。
我写的发布脚本是 node 的,内部会调用 hcloud。运行后:
spawnSync C:/.../hcloud.exe EBUSY
EBUSY 是"设备忙"。我以为是那个 exe 的问题,换了个试:
spawnSync C:\WINDOWS\system32\cmd.exe EBUSY
spawnSync C:/.../bash.exe EBUSY
spawnSync C:/.../python.exe EBUSY
spawnSync C:/.../git.exe EBUSY
spawnSync C:/.../node.exe EBUSY ← 连它自己都起不来
每一个 exe 都 EBUSY。 连 node 派生 node 都失败。
这个结果非常有说服力地指向"环境禁掉了子进程"。
但有一个反例解释不通:我另一个项目的 bridge 进程,明明就是用 node 的 spawn 起来的,一直在正常工作。
于是我不再换 exe,改成换参数:
| 调用方式 | 结果 |
|---|---|
| spawnSync(exe, args, {stdio: 'inherit'}) | ✅ 正常 |
| spawnSync(exe, args, {stdio: 'ignore'}) | ✅ 正常 |
| spawnSync(exe, args)(默认 pipe) | ❌ EBUSY |
| 异步 spawn(..., {stdio:['pipe','pipe','pipe']}) | ✅ 正常 |
不是禁子进程,是禁"为子进程创建管道"——而且只影响同步版。
异步 spawn 用管道没事,所以 bridge 活得好好的;同步的 execSync / execFileSync 一律挂。
绕法:把管道换成文件
写了一个 preload 垫片,把同步三件套换成「临时文件接输出 + 真 spawnSync(文件 fd) + 读回」,用 NODE_OPTIONS=--import 挂上去,一行原脚本都不用改:
NODE_OPTIONS="--import file:///.../cp-shim.mjs" node some-script.mjs
垫片自己也踩了两个坑
第一版垫片没生效。 原因很有意思:
垫片自己 import cp from 'node:child_process',这个动作让 Node 提前生成了 ESM 包装器,把 execSync 这些具名导出固化住了。之后我再改 CJS 的导出对象,对 import { execSync } 完全无效——命名空间属性显示"已替换",但具名导入拿到的还是原始函数。
改成 createRequire 取 CJS 对象、不在模块顶层 import,才生效。
第二个坑小一些:垫片里的 shell 得用 Git Bash,cmd.exe 在这个环境下即便给了文件 stdio 也不可靠。
三个坑的共同点
它们都不在业务逻辑里。
发布流程本身(认证、上传、字段校验)反而是最顺的部分。真正花时间的是:一个 SSL 校验策略、一个 WAF 规则、一个管道创建限制。
还有一件事值得记:第三个坑我差点就停在错误结论上。"所有 exe 都起不来"和"某一种调用方式起不来",是完全不同的诊断——前者要怀疑环境,后者要怀疑参数。救我的不是运气,是那组 inherit / ignore / pipe 的对照实验。
如果你也在做类似的自动化,遇到"全线失败"时,先别急着下结论:找一个已知能工作的反例,然后只改一个变量。
- 点赞
- 收藏
- 关注作者
评论(0)