ブログをHexoからHugoへ移行することにしました

2026-10-04 10:30≈ 28字≈ 1分

長年、Hexoを使ってきました。最初はシンプルで十分に使え、テーマもカスタマイズしやすいものでした。でも、自分の習慣に合わせようと、機能を追加し、スタイルを調整し、プラグインやスクリプトを足し続けてきました。手を加えすぎてしまいました。

振り返ると、サイトは動いていたものの、だんだん不安定になっていました。前回バージョン6.3にアップグレードしたときも、カスタマイズのせいで長い時間を取られました。今回移行するのは、Hexoが悪いからではありません。自分が手を加えすぎて、そのカスタマイズを保守する負担が、便利さを上回り始めたからです。

移行を決めた理由

管理するものは記事ページだけではありませんでした。中国語、英語、日本語の各サイトにそれぞれ設定があり、公開時には一つずつビルドしてデプロイする必要がありました。トップページ、随筆の年別アーカイブ、小説のカテゴリ、タグ、言語切り替え、文字数表示、プロフィールページにも、それぞれテーマ側の処理がありました。小さな変更を一つ加えるにも、ほかの言語サイトやページに影響しないか、まず確認しなければなりません。

デプロイの流れにも、長年の積み重ねで多くの負担がありました。以前のGitHub Actionsでは、3つのサイトそれぞれにシークレットを用意し、Hexoを実行したうえで、スクリプトを使って設定や生成結果を切り替えていました。動いてはいましたが、依存パッケージのバージョン、Node.jsの実行環境、自作スクリプトをずっと面倒見なければなりません。ブログは本来、書くことに集中させてくれるもののはずなのに、更新のたびにビルドや公開の流れを心配していました。

年を取ってきて、書くこと自体に気持ちが向かないときもあります。それなのに、半年も書かずにいたら設定項目がまだ正常かどうか分からなくなるのでは、と心配しなければなりません。いつか変更をpushしたあと、GitHubでエラーを探し、また調整することになるのも不安でした。

そこで、コンテンツと表示の仕組みを整理し直し、Hugoが備えているコンテンツ管理、Markdownの解析、シンタックスハイライトをできるだけ活用することにしました。残したい個人的なデザインは、少数のテンプレートにまとめます。

移行の進め方

移行に踏み切れたのはChatGPTのおかげです。大規模言語モデルがあったからこそ、試してみる勇気が出ました。稼働中のHexoサイトに影響しないよう、CodexにHugoプロジェクトを別のディレクトリで移行・検証してもらい、Hexoプロジェクトのファイルは上書きしませんでした。まず旧サイトの設定、テーマの変更、プラグイン、記事のフロントマター、GitHubのデプロイ手順を確認し、旧サイトの生成結果をリンクやページ構成の比較基準にしました。

記事は中国語、英語、日本語それぞれのHugoコンテンツディレクトリに配置し、元のタイトル、カテゴリ、タグ、日付、記事URLをできる限り維持しました。コンテンツの構成も整理し直し、トップページには経験談だけを載せ、随筆は年別、小説はカテゴリ別にまとめました。どれも当時自分で加えたカスタマイズで、今となっては後に残った落とし穴です。

文字数や読了時間などの記事情報は、Hugoが本文から生成します。画面とMarkdownも一つずつ確認しました。言語切り替え、目次、前後の記事へのリンクはHugoのテンプレートに移し、コードブロックはHugo標準のGoldmarkとChromaに任せています。

Hugoが生成したサイトマップを解析できなかった

移行前後の3つのサイトマップを比較しました。中国語はどちらも243件、英語は90件から92件、日本語は217件から219件でした。URLをデコードし、Unicodeを正規化して比べると、既存記事のURLはすべて対応していました。中国語176記事、英語42記事、日本語162記事です。日本語のサイトマップには、旧サイトのサイトマップになかった記事が1件追加されていました。パーセントエンコードなどの違いで文字列が異なって見えるURLもありましたが、正規化後のパスは一致しました。

移行後、中国語サイトのサイトマップは開けるのに、Google Search Consoleでは解析できないことにも気づきました。もともと3言語のサイトを一度の多言語ビルドで生成していたためです。Hugoはサイトマップインデックスと各言語のサブディレクトリ内のサイトマップを出力しますが、実際のデプロイ先は互いに独立した3つのサイトでした。そのため中国語のサイトマップは/cn/sitemap.xmlに置かれる一方、掲載されているページは中国語サイトのルートパスを使っており、配置場所とデプロイ構成が合っていませんでした。ブラウザーで内容が続けて表示されるため、XMLファイルではないように見えることもありますが、問題の核心はサイトマップの生成方法とデプロイ先の不一致でした。

**対処として、中国語、英語、日本語をそれぞれ専用のHugo設定で個別にビルドするようにしました。各ビルドでは1言語だけを有効にし、多言語サイトマップインデックスを出力せず、公開ディレクトリのルートに標準のsitemap.xmlを直接生成します。**3つのサイトは引き続き従来のドメインとデプロイ先を使い、サイトマップを通じて互いに関連付ける必要はありません。変更後、ブラウザーではサイトマップがXMLとして表示され、各サイトのルートにあるサイトマップをSearch Consoleに再送信すると、正常にクロールされるようになりました。

GitHub Actionsでの公開はそのまま

Hugoに切り替えた後も、デプロイにはGitHub Actionsを使い続けています。従来のdeployワークフローは、hugo-migrationブランチへのpushを受けて実行されます。中国語、英語、日本語それぞれの設定で個別にビルドし、3つのサイトを従来と同じデプロイ先に公開します。各サイトの公開ルートには独自のsitemap.xmlが置かれます。たとえばメインサイトは/sitemap.xml、英語サイトは/en/sitemap.xml、日本語サイトは/jp/sitemap.xmlです。

デプロイでは、ブログリポジトリにすでに設定してある3つのActions secretsを引き続き使います。今後は記事を書き終えたら、いつもどおりコミットしてHugoブランチにpushすれば、同じワークフローが動きます。3言語分を手作業で生成する必要はありません。リポジトリもパスワードも同じで、問題も何も変わりません。変わったのは、Hexoプロジェクトを開いてコミットしていたのが、Hugoプロジェクトを開くようになったことだけです。

移行を終えて

以前なら、こうした作業には1日、あるいはそれ以上かかったかもしれません。今回はCodexを使って半日で済みました。そのうち2時間はコードブロックの表示形式とサイトマップの問題に費やしたので、それがなければ全部で2時間ほどで終わったかもしれません。

HugoがHexoより速いのは確かですが、それが一番大事なことではありません。自分が加えたカスタマイズを面倒見るのが億劫だし、もう覚えてもいられません。いつかのアップデートで全部だめになるのでは、と心配しています。ははは。