<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Static Site on U-REI.com</title><link>https://u-rei.com/tags/static-site/</link><description>Recent content in Static Site on U-REI.com</description><generator>Hugo</generator><language>ja-JP</language><copyright>2023-2026 Kosuke Uchida</copyright><lastBuildDate>Sat, 22 Aug 2026 23:00:00 +0900</lastBuildDate><atom:link href="https://u-rei.com/tags/static-site/index.xml" rel="self" type="application/rss+xml"/><item><title>HugoとGitHub PagesだけでWordPressライクな予約投稿を実現する</title><link>https://u-rei.com/posts/2026/08/2026-08-22-hugo-github-pages-scheduled-publish/</link><pubDate>Sat, 22 Aug 2026 23:00:00 +0900</pubDate><guid>https://u-rei.com/posts/2026/08/2026-08-22-hugo-github-pages-scheduled-publish/</guid><description>&lt;p&gt;このブログはHugo + GitHub Pagesという、いわゆる「静的サイト」構成で動いている。ホスティングは無料でCDNも効くし、サーバーの面倒を見る必要もない。ただしこの構成には、WordPressのような動的CMSなら当たり前にある機能がひとつ欠けている。&lt;strong&gt;予約投稿&lt;/strong&gt;だ。&lt;/p&gt;
&lt;p&gt;Hugoはビルド時点の現在時刻より未来の&lt;code&gt;date:&lt;/code&gt;を持つ記事をデフォルトでビルド対象から除外する(&lt;code&gt;buildFuture: false&lt;/code&gt;相当の挙動)。裏を返せば、「未来日付の記事をmasterにマージしておいて、その時刻が来たら勝手にサイトに出てくる」ということは、&lt;strong&gt;誰かが正しいタイミングでもう一度ビルドを起動しない限り起きない&lt;/strong&gt;。動的CMSなら公開時刻をDBに書いておいてリクエストのたびに判定すればいいが、静的サイトは「ビルドした瞬間の世界」を切り出して配信しているだけなので、時間の経過そのものをトリガーにできない。&lt;/p&gt;
&lt;p&gt;この記事では、GitHub Actionsだけを使ってこの制約を回避し、実際に予約投稿を動かしている仕組みを、詰まった点も含めて書いておく。&lt;/p&gt;
&lt;h1 id="全体構成"&gt;全体構成&lt;/h1&gt;
&lt;pre class="mermaid"&gt;flowchart TD
Author[記事を書く] --&amp;gt;|&amp;#34;date: に未来日時を指定してPR作成&amp;#34;| PR[Pull Request]
PR --&amp;gt;|opened / synchronize| DateCheck[correct-manual-post-dates.yaml]
DateCheck --&amp;gt;|&amp;#34;date &amp;lt;= 現在時刻なら現在時刻に補正&amp;lt;br/&amp;gt;未来ならそのまま&amp;#34;| PR
PR --&amp;gt;|レビュー後マージ| Master[masterにpush]
Master --&amp;gt;|pushイベント| HugoBuild[hugo.yaml: ビルド&amp;amp;デプロイ]
HugoBuild --&amp;gt;|&amp;#34;未来日付記事は除外&amp;#34;| Pages[GitHub Pages]
Cron[&amp;#34;Cron: 15分ごと&amp;#34;] --&amp;gt;|起動| Checker[publish-checker.yaml]
Checker --&amp;gt;|&amp;#34;content/posts/**/*.md をスキャン&amp;#34;| Due{公開時刻到来?}
Due --&amp;gt;|Yes| Dispatch[&amp;#34;gh workflow run hugo.yaml&amp;#34;]
Dispatch --&amp;gt; HugoBuild
DailyCron[&amp;#34;Cron: 毎日0時UTC&amp;#34;] --&amp;gt;|安全網| HugoBuild&lt;/pre&gt;
&lt;p&gt;登場するワークフローは3つ。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;correct-manual-post-dates.yaml&lt;/code&gt;&lt;/strong&gt;: PRを開いた/更新した時点で、新規追加記事の日時を補正する&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;hugo.yaml&lt;/code&gt;&lt;/strong&gt;: Hugoでビルドし、GitHub Pagesにデプロイする(未来日付の記事は自動的に除外される)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;publish-checker.yaml&lt;/code&gt;&lt;/strong&gt;: 15分ごとに全記事をスキャンし、公開時刻が到来した未来日付記事があれば&lt;code&gt;hugo.yaml&lt;/code&gt;を起動する&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="1-記事のdateと実際に公開された時刻を一致させる"&gt;1. 記事の&lt;code&gt;date:&lt;/code&gt;と「実際に公開された時刻」を一致させる&lt;/h1&gt;
&lt;p&gt;普段の運用(予約投稿ではなく、書いたらすぐ公開したい記事)では、&lt;code&gt;date:&lt;/code&gt;フィールドに正確な時刻を毎回手で入れるのは面倒だし、書き始めた時刻とマージされた時刻がずれることも多い。そこで、PRの&lt;code&gt;opened&lt;/code&gt;/&lt;code&gt;synchronize&lt;/code&gt;イベントをトリガーに、新規追加されたMarkdownファイルの&lt;code&gt;date:&lt;/code&gt;を機械的に補正するワークフローを用意した。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Correct publish dates of added posts&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: |&lt;span style="color:#e6db74"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; files_to_check=$(gh api &amp;#34;repos/${{ github.repository }}/pulls/$PR_NUMBER/files&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; --paginate --jq &amp;#39;.[] | select(.status==&amp;#34;added&amp;#34;) | .filename&amp;#39; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; | grep -E &amp;#39;^content/posts/.*\.md$&amp;#39; | grep -v &amp;#39;_index.md&amp;#39; || true)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; if [ -n &amp;#34;$files_to_check&amp;#34; ]; then
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; git fetch origin &amp;#34;$BRANCH&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; git checkout -B &amp;#34;$BRANCH&amp;#34; origin/&amp;#34;$BRANCH&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; python3 scripts/correct_publish_dates.py $files_to_check
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; # ... 差分があれば github-actions[bot] としてPRブランチにコミット&amp;amp;push
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;ポイントは3つ。&lt;/p&gt;</description></item></channel></rss>