背景
サイトを自動更新するとき、コミットは想定パスだけをステージするようにしています。
以前 git add -A で並行作業中の無関係なファイル10件を巻き込んで push した実害があったので、対象を明示する方式に変えました。想定外の変更があるとコミット自体は成功したうえで exit 3 を返し、当日のログに一覧が残ります。
今日、この exit 3 が複数のサイトで出ました。
最初は「また作業中のファイルが混ざっているだけだろう」と思ったのですが、中身を見たら違いました。
何が残っていたのか
1つ目のサイトで残っていたのは build.js の差分でした。
+ // 終了セールは検索対象から外す。
+ // listedArticles() から外れた時点で sitemap にも一覧にも載らなくなるが、
+ // ページ自体は 200 のまま残る設計(直URL・サイト内検索で参照できる)。
+ noindex: isEnded(a),
見覚えがあります。これはすでにクローズしたIssueの修正でした。「sitemapにもリンクにも無い不可視ページが200で公開され続けている」という指摘に対して、期限切れ記事へ noindex を付ける、という対応です。
同じ修正を6サイトへ入れてクローズしてありました。
そこで6サイトを調べたら、こうなっていました。
作業ツリー HEAD
manga-deals 1 0 ← ここだけ未コミット
ocha 1 1
senzai 1 1
menrui 1 1
coffee 1 1
beer 1 1
1サイトだけ、修正が git に入っていませんでした。
同じことが別の2サイトでも起きていました。片方は配信サービスへの検索リンクを追加する修正(375行)、もう片方は表示ステータスの綴りを正規化する修正です。どちらもクローズ済みのIssueの中身でした。
なぜ気づかなかったのか
本番の挙動が正しかったからです。
自動更新は毎回 node build.js を実行してから配信します。実行されるのは作業ツリーの build.js なので、未コミットであっても修正は効きます。毎日、直った状態の HTML が公開されていました。
そして生成物である public/ はコミット対象に入っています。
整理するとこうなります。
- 入力(
build.js)… コミット対象外 → 作業ツリーに残る - 出力(
public/)… コミット対象内 → git に入る
「新しい入力で作った出力」だけがコミットされ、入力は残る。
リポジトリを clone して node build.js を回すと、コミット済みの public/ とは違う HTML が出ます。ビルドの再現性が壊れている、という形の不整合でした。
しかもコマンドは全部成功します。deploy も通る、検査も通る、監査も緑。
検査が見ていた向きが逆だった
デプロイ前のゲートには「作業ツリーが origin より遅れていないか」を見る項目があります。
これは、古い作業ツリーからデプロイして公開直後の記事を本番から消してしまった事故(実際に10分ほど404にしました)の再発防止として入れたものです。
つまり見ているのは遅れの側です。
今回のは逆で、origin より進んでいる(未コミットの変更がある)側でした。こちらは検査対象になっていません。
言われてみれば当たり前なのですが、「同期しているか」という言葉で括ってしまうと、片方向しか見ていないことに気づきにくい。
origin より遅れている → 検査あり(過去の事故の再発防止)
origin より進んでいる → 検査なし ← ここ
対処と、残した判断
3サイトとも、その場で明示的にコミットしました。
node automation/run.js git:commit --scope manga-deals \
--allow build.js,fleet-sites.json \
--message "fix: 終了セール記事に noindex を付ける(他5サイトはコミット済み・ここだけ残っていた)" \
--push
--allow は「想定パス以外を意図的に含める」ときのフラグで、判断が記録に残るようにしてあります。無条件に巻き込む -A に戻すのではなく、1件ずつ理由を書いて入れる形です。
一方、検査を足すところまではやりませんでした。
「未コミットの変更があったら落とす」にすると、並行作業をしている最中は毎回落ちます。日次の自動実行なので、落ち続ける検査は必ず無視されるようになります。
なので、まずは警告として出してログに明示するところから、という提案をIssueに残して判断を仰ぐことにしました。ここは自動で決めてよい範囲を超えていると思ったからです。
この仕組みで更新しているサイトの1つがマンガお得情報です。
学び
一般化するとこうなります。
生成物をリポジトリに入れる構成では、「生成物の入力」もセットでコミット対象に含めないと、本番とリポジトリが静かにズレる。
そしてズレたことに気づく手掛かりが、この構成にはほとんどありません。
- 本番は正しく動いている
- コマンドは全部成功する
- 検査も監査も通る
唯一の手掛かりが「コミット対象外の変更があります」という exit 3 でした。今日それを読み流していたら、3サイトとも見つかっていません。
毎回出る警告は無視されるようになるので、この exit 3 が普段は出ない状態を保っておくこと自体に価値がある、とも言えます。今日は逆に、普段出ないものが出たので中身を見た。そういう設計になっていたのは運がよかったです。
本記事はAI補助で執筆した、個人開発の紹介記事です。