静的サイトへ移行したら、記事を自動で公開する仕組みが外れていた話

  • #Astro
  • #静的サイト
  • #RSS
  • #ドリップ公開
  • #自動化
  • #launchd
静的サイトへ移行したら、記事を自動で公開する仕組みが外れていた話

この記事について

WordPress から Astro(静的サイト)へ移行したとき、記事の入れ物とビルドパイプラインは正常に動いたが、「記事を定期的に公開し続ける仕組み」だけが移設リストから漏れた。移行後しばらくは RSS の器が正常に配信され続けるため監視上は無事に見え、気づくまでに時間がかかった。対策として書いた「ドリップ公開」の設計と、公開スロットを時刻順ではなく GA4 実測の PV 価値順で並べる理由のメモ。

実装ステータス: 実装済み

何が外れたか

WordPress には「スケジュール投稿」がある。管理画面から公開日時をセットすれば、指定した時刻に自動で publish される。毎日更新し続けるメディアにとっては空気のような機能で、WordPress を使っている限り最初から付いてくる。

Astro(静的サイト)に移行すると話が変わる。デプロイ時点でビルドを走らせると、そのときに draft: false になっている記事はすべて公開状態になる。「後から公開する」概念が存在しない。「将来の日付の記事を隠す」仕組みは自分で作るか、GitHub Actions で定刻ビルドをかけるか、draft: true で管理するしかない。

ある旅行系メディアをこの構成へ移行したとき、「公開タイミングの制御」を設計しないまま動かし始めた。記事は git push で即公開、書き溜めの概念もない状態。RSS の配信自体は正常に動いていたため、フィード購読者の監視上は「生きている」に見えた。実際には新記事が出なくなっており、アンテナ経由の流入が減り始めてから異変に気づいた。

対策: ドリップ公開の仕組み

「書き溜め」と「時間差リリース」を分離することで解決した。

記事は draft: true として git に commit する。Astro の getCollection()draft: true の記事を除外するため、push してビルドが通っても一覧にも RSS にも出ない。これが書き溜めの器になる。

// Astro のコレクション取得側
const posts = await getCollection('articles', ({ data }) => !data.draft);

リリース時には draft: truedraft: false に書き換え、pubDate をリリース時刻で上書きして commit・push する。この「反転」を行うリリーススクリプト(release_next.py)は git を操作しない純 Python で、最も長く待っている1本(pubDate が最も古いもの)を選んで書き換えるだけの決定的な実装になっている。単体でテスト可能。

pubDate を書き溜め時の日付ではなくリリース時刻に打ち直す理由は、アンテナサイトの挙動にある。アンテナは pubDate と新着順で並べるため、古い日付のまま公開すると「1年前の記事」として下位表示される。リリース時刻でスタンプし直すことで、公開した瞬間がアンテナ最上位になる。

公開スロットを「価値の高い順」で並べる

launchd で毎時起動するシェルスクリプト(drip)がリリーススクリプトを呼び、JST の特定時刻だけ実際に反転処理を行う設計にした。

スロットの時刻は当初「わかりやすい等間隔」で設定していたが、GA4 の時間帯別 PV データを引いたところ、時間帯ごとに最大2倍近い差があることがわかった。実測値(90日間のデータ)を並べると以下のようになる。

時刻(JST)PV 比率(時間帯別)
11時7.0%
17時6.6%
16時6.6%
15時6.4%
12時6.2%
18時5.3%
8時3.6%

旧設定では 8時 が筆頭スロットだった(最弱の枠に毎日1本を出していた)。現在は「在庫 N 本のとき、価値の高い順に N スロットだけ使う」設計にしている。在庫が1本しかない日は必ず 11時(最良枠)に出る。

「上位3枠(11/17/21時)は互いに4時間以上離す」ルールも設けた。アンテナ/RSS 経由の流入は露出の窓の数で決まる。同じ時刻帯に複数本出しても窓は1つにしかならないため、分散させて窓を増やす。

環境変数で「見守り」と「無人公開」を切り替える

立ち上げ当初は「ドリップスクリプトが PR を作るだけ・マージは人間が確認する」見守りモードで運用した。数本の公開を見てリリースの cadence に納得したら DRIP_AUTOMERGE=on を設定して無人公開に切り替える方針。

if [ "$DRIP_AUTOMERGE" = "on" ]; then
  gh pr merge "$BRANCH" --merge --delete-branch
  # Discord に公開通知
else
  # PR だけ作成して Discord に「マージ待ち」通知
fi

「見守りモードで確認 → 無人化」というステップを踏むことで、リリースのタイミングや内容に違和感を感じたときに気づきやすくなる。一度に全部自動化するより立ち上げコストが低い。

まとめ: 移行 checklist に加えること

WordPress から静的サイトへ移行するときに見落としやすい点をまとめる。

  • スケジュール投稿の再実装: WordPress の自動公開に相当する仕組みを自前で作る必要がある。GitHub Actions の定刻ビルドか、drip スクリプトか
  • pubDate の扱い: 書き溜めの日付をそのまま使うとアンテナで最下位になる。リリース時に打ち直す
  • RSS の正常稼働 ≠ 更新の継続: RSS の器が動いていても、中身の更新が止まったことは監視で検出されない

動作環境: Astro 5.x / Cloudflare Pages / macOS launchd
更新履歴: 2026-07-23 — 初稿
訂正履歴: なし