免费

让 Claude 做生产部署

部署出错没人告诉你。用独立 credentials、EDITOR 脚本、readback 校验、env 自动切换四道护栏,让 Claude 能参与但不失控。


部署和写代码最大的不同是:一次性、高风险、回滚麻烦。写错一行代码,测试跑一次就发现。写错一行 credentials,你要到第一次真实支付失败才发现——这时候用户已经付了钱、钱没到你账户、日志里全是 400。

最近用 Claude 把 how2claude 从本地开发推上生产,要配 Stripe live 账户、x402 mainnet 钱包、Google OAuth、Kamal secrets。Claude 不知道哪些值是测试环境的、哪些是真的,不知道 sk_live_sk_test_ 差一个字母就是天壤之别。护栏得自己搭。

护栏 #1:dev 和 prod 用两个独立的 credentials 文件

Rails 默认的 config/credentials.yml.enc,解密密钥 config/master.key。用这个默认你就输了。

如果你把 sk_live_xxx 塞进这个文件,然后本地跑测试——测试代码就在用生产 Stripe。跑一次就扣一次钱。

换成两个:
- config/credentials.yml.enc + config/master.key:dev/test 用,塞 sk_test_xxx
- config/credentials/production.yml.enc + config/credentials/production.key:生产用,塞 sk_live_xxx

.gitignore 里两个 .key 都忽略,两个 .enc 都 commit。production.key 单独放在机器上。

然后 Kamal secrets 要指对文件:

 # .kamal/secrets
-RAILS_MASTER_KEY=$(cat config/master.key)
+RAILS_MASTER_KEY=$(cat config/credentials/production.key)

容器里的 RAILS_MASTER_KEY 指向生产密钥,解出来就是生产 credentials。Claude 写这行的时候我特意盯着——默认生成的 Kamal 模板是 config/master.key(dev 的),完全静默地把 dev Stripe key 部署到生产去。

护栏 #2:用 EDITOR 脚本塞值,不手动复制粘贴

bin/rails credentials:edit --environment production 要开一个交互式编辑器。Claude 驱动不了交互式编辑器。你也不想手动复制十几个密钥(一个打错全家福崩)。

用这个 pattern:

EDITOR="ruby script/set_prod_webhook_secret.rb" \
  bin/rails credentials:edit --environment production
rm script/set_prod_webhook_secret.rb

script/set_prod_webhook_secret.rb 长这样:

# ARGV[0] 是解密后的临时 YAML 文件路径
file = ARGV[0]
require "yaml"
data = YAML.load_file(file) || {}
data["stripe"] ||= {}
data["stripe"]["webhook_secret"] = "whsec_GHWObNAKFh2HPOlJpbGmlYfIiKz1C8EY"
File.write(file, data.to_yaml)

Rails 把解密后的 YAML 写到临时文件,把文件路径当参数调 "EDITOR",退出后重新加密。我们的"EDITOR"实际上是个 Ruby 脚本,精确修改一个键,存回。

好处:
- 精确:只动 stripe.webhook_secret 这一个字段,不动其他。
- 幂等:脚本跑两次结果一样。
- 可审计:脚本本身就是改动的 diff。Claude 写完脚本我看一眼就知道它要改什么。
- 删除即忘rm 之后磁盘上没有明文 credential 残留,shell history 里也没有 whsec_... 的复制粘贴痕迹。

每个 credential 都写一个脚本:set_stripe_live_key.rbset_webhook_secret.rbset_price_ids.rbset_wallet_address.rb。写完立刻 rm。

护栏 #3:写完用 Rails runner 回读校验

写进去之后,不要相信它写对了。读回来

bin/rails runner -e production "
c = Rails.application.credentials
puts 'sk_live set: ' + c.dig(:stripe, :secret_key).to_s.start_with?('sk_live_').to_s
puts 'webhook_secret set: ' + c.dig(:stripe, :webhook_secret).to_s.start_with?('whsec_').to_s
puts 'wallet prefix: ' + c.dig(:x402, :wallet_address).to_s[0..5]
puts 'wallet len: ' + c.dig(:x402, :wallet_address).to_s.length.to_s
"

关键是前缀长度校验,不是光打印出来看。

  • Stripe live secret key 永远是 sk_live_ 开头。如果读出来是 sk_test_ 开头,Claude 把 test key 塞进了 prod 文件——这个 bug 在第一次真实支付前你不会发现。
  • Webhook secret 永远是 whsec_ 开头。格式对不对一眼看出来。
  • EVM 钱包地址永远是 42 字符 0x...。长度不对就说明混进了别的字符。

前缀校验加上去,手误、漏字段、混环境这类错误基本都能挡住。

护栏 #4:让运行时按 env 自动切换,不要 deploy 时手动切

容易想到"在 deploy 前把 network 改成 mainnet"——这种切换靠人记忆,迟早出事。

x402 initializer 里直接写死规则:

# config/initializers/x402.rb
X402.configure do |c|
  c.wallet_address = Rails.application.credentials.dig(:x402, :wallet_address)
  c.chain = Rails.env.production? ? "base" : "base-sepolia"
end

同一份代码,dev 跑 testnet(sepolia),prod 跑 mainnet(base)。部署时不用改任何东西。Claude 也不会"忘了切换",因为切换不需要它。

同样的逻辑用在 basescan_tx_url、Plan 可见性(dev-only plans 生产不显示)、Stripe price ID 选择……凡是 dev 和 prod 不一样的配置,都用 Rails.env 翻,不要依赖部署时记得改。

最后一道:人工走一次真实流程

四道护栏搭完,第一次真金白银的支付仍然必须你自己点一次。

上一篇《让 Claude 调试玄学 bug》讲过,我在生产第一次点 x402 支付,console 直接报 invalid_string at payTo——钱包地址第 43 位是一个从中文输入法混进来的全角问号。前缀校验挡不住(0x 开头没变),测试抓不到(因为测试不发真实交易),只有真实点一次才暴露。

部署的护栏不是给 Claude 看的,是给你看的——把可验证的东西(前缀、长度、env 切换)自动化,省下的人工注意力用来盯那些自动化挡不住的真实交互。


让 Claude 做部署,完整流程:

  1. 分开 dev 和 prod 的 credentials 文件 + .key 文件。
  2. 每个要塞的值写一个一次性 Ruby 脚本,用 EDITOR=script 塞进去,立刻 rm。
  3. 塞完立刻用 Rails runner 回读,带前缀/长度校验。
  4. 运行时差异全部用 Rails.env 翻转,不要部署时手动切。
  5. 第一次点真钱的按钮,自己点。

部署不是"更危险的写代码",部署是"写错了没人马上告诉你"的写代码。护栏的作用是把"没人告诉你"变成"15 秒内就能告诉你"。