Article:

個人でブログサイトを作り公開する方法は複数ありますが、最近はサーバーレスアーキテクチャが流行っているので できればそれを使ってみたいですよね。そこで注目しているのがHugo+AWSの組み合わせです。この記事では、Hugoの使い方・カスタマイズの仕方と AWSを用いてホストする方法までを解説します。

AWSに近年新しくAWS Amplifyが登場しました。これはHugoをサポートしており、GitHub上のレポジトリと連携設定をするだけで、 PUSHされたときに自動的にコンパイルし、公開してくれます。とっても便利です。

実際にこのサイトもHugoで生成し、AWSでホストしています。 スタティックであるため、料金も格安です。AWSの機能で複雑な設定もなくSSLが利用できます。

hugoとは

Hugoは静的サイトジェネレータです。 Hugoはサイトのコンテンツ(markdown等で記述されたページ内容)を、テンプレートを用いて実際のwebページに変換するという手法をとっています。

なので、そのテンプレートを自分仕様にカスタマイズすることで、自分の好きなデザインのページをかんたんに作ることができるわけです。 もちろんテンプレートは沢山の種類が公開されていますので、そこから好きなものを選んで使ったり、それをベースにカスタマイズすることもできます。

hugoのインストール・使い方

本記事ではMac OSを用います。他OSでもサポートされているようなので、他OSの人は他のサイトを参考にお願いします。

インストールにはHomeBrewを使います。

1
$ brew install hugo

これで完了です。

新しいプロジェクトを生成するときは、そのプロジェクトを保存したい場所で

1
$ hugo new site サイト名

で新しいプロジェクトを作成することができます。このとき生成されるフォルダー階層とその役割は次のとおりです。

  • config.toml 設定ファイル
  • archetypes/ 記事を書くときのテンプレートを入れるフォルダ
  • content/ 記事本体を入れるフォルダ
  • layouts/ すでにあるテーマをカスタマイズしたいとき使うフォルダ(今回はテーマを自作するので弄らない)
  • themes/ テーマを入れるフォルダ
  • data/ hugoが内部的に使うフォルダ(弄らない)
  • static/ 記事で使う画像など、hugoを通さずにそのまま配備したいファイルを入れるフォルダ

conetntフォルダがサイトのページ内容を入れるフォルダになっていて、実際にサイトを運営するフェーズに入ったときに 最も使うフォルダになります。

新しいページを作成するときは、

1
$ hugo new セクション/ページ名.md

を発行すると/content/セクション名/にページ名.mdが作成されます。 エディタでそのまま作成しても問題ないですが、これを使うとarchetypes/で定義しておいたテンプレート(記事タイトルや作成日時などを含む) がそのまま挿入されるので便利です。

このファイルは、hugoでコンパイルすることで、http://yourdomain.com/セクション名/ページ名.htmlに射影されます。 content内のフォルダはセクションに、ファイルはページとして作成されるわけです。

hugoにはローカルでhttpdを立ち上げて、変更をすぐ確認できる機能がついているのですが、 テーマを設定しないとエラーになるので先にその設定をします。

プロジェクト内部で

1
$ hugo new theme テーマ名

すると、空のテーマプロジェクトが/themes/内に作成されます。

次に、設定ファイルを編集してテーマを割り当てます。

1
2
3
4
5
baseURL = "https://path-to-your.site/"
languageCode = "ja"
title = "my site title"
theme = "テーマ名"
hasCJKLanguage = true

ハイライトしている4行目がその設定ですが、ついでに他の設定も書いておいたので今のうちに設定しておいて良いと思います。

テーマ名を設定したら、プロジェクト内で

1
$ hugo server

することで今の内容でサイトをビルドし、ローカルでホストされている状態になります。 デフォルトではhttp://localhost:1313/になっているのでブラウザを開けばまだ真っ白なページですが見えるはずです。

次の章ではテンプレートをカスタマイズしていきます。

テンプレート概要

この章を読み進めるに当たって、先にすでに公開されているテーマを一度テスト用のプロジェクトに当ててみて、 コンテンツを作成したときにどのように反映されるのかを一通り試してみると内容が伝わりやすいかと思います。

テンプレートフォルダのフォルダ階層は次のとおりです。

  • LICENSE
  • theme.toml テーマ用の設定ファイル
  • archetypes/
  • layouts/ サイトのhtmlはすべてここに入る
  • static/ ビルド時にサイトのドキュメントルートに直接展開される。cssやjs、固定ページ用の画像を入れる。

ほとんどサイトプロジェクトの構造と同じなのがお分かりでしょうか。

というのも、Hugoがあるコンテンツをページに変換するとき、「まずプロジェクトフォルダのLayouts/を見て、 そこになければテーマフォルダのLayoutsを参照する」という順序でテンプレートを参照しており、それによって ユーザー側としてはもとのテーマファイルを直接編集せずともカスタマイズができる仕組みになっているからです。

しかし今回は自分専用のテーマを作るだけなので関係ない話でしたね。 主に使うことになるのはlayouts/フォルダで、ここにテンプレートを入れます。

layouts/フォルダの階層は次のとおりです。

  • index.html
  • 404.html
  • _default/ セクション専用のテンプレートがないときに適用されるデフォルトテンプレートを入れるフォルダ
    • baseof.html ページの素体となるテンプレート
    • list.html リスト構造を表示するときに適用するテンプレート
    • single.html 単体要素(リストでないという意味で)を表示するときに使う
  • partials/ ページの素材置場(モジュールとして使い回すための)
    • head.html
    • header.html
    • footer.html

Template Lookup Order

テンプレートの詳しい作り方を説明する前に、Hugoがどのようなページタイプに対して、 どのテンプレートを用いるかの説明が必要です。

ここでは、ホームページ(ドキュメントルート)と、単一の記事のページ、セクションページの3種類に分けて説明します。

ホームページ

ホームページはhttp://your-domain.com/に当たるページのことで、 言ってしまえばセクションページだと考えることもできるのですが、基本的にはそのサイトの説明など 別の内容を表示したい場合が多いと考えられているので、特別に分けられています。

Hugoはホームページを作成するときに、次の一覧を上から下の順番で参照します。

1
2
3
4
layouts/index.html
layouts/list.html
layouts/_default/index.html
layouts/_default/list.html

「layoutsフォルダの中に特別にindex.htmlというファイルが用意されてない場合は、 セクションとしてリスト表示する」という意向の順番になっていますね。

記事のページ

記事のページはcontentフォルダの中にあるファイルに該当します。

1
2
layouts/セクション名/single.html
layouts/_default/single.html

「layoutsフォルダの中に対象の記事が所属するセクション名と同じ名前のフォルダがあればそちらを優先し、 ない場合は_default内のテンプレートを参照する」という順番になっています。

セクションページ

セクションページは記事を束ねているセクションを表示するためのページで、 基本的にはそのセクションに該当する記事の一覧を表示することが多いです。

1
2
3
4
5
6
7
8
9
layouts/セクション名/セクション名.html
layouts/セクション名/section.html
layouts/セクション名/list.html
layouts/section/セクション名.html
layouts/section/section.html
layouts/section/list.html
layouts/_default/セクション名.html
layouts/_default/section.html
layouts/_default/list.html

セクションだけやたら項目が多いですね。 セクション名のフォルダが最優先され、次に"section"という名前のフォルダ、そして最後に_defaultが参照されるようです。

hugo構文

さて、前章によって、想定しているウェブデザインを生成するためにはlayouts内のどこにフォルダを作成・編集すればよいのかわかったと思います。

次に、テンプレートをテンプレートたらしめるためのhugo構文について軽く説明します。

layoutsフォルダに配備されるテンプレートはすべてhtmlで記述され、参照されればそのhtmlがそのまま出力されるわけですが、 contentフォルダの内容に応じて、記事の名前・内容・投稿日等のデータをバインドする機能を持たせなければテンプレートとして使えませんよね。

hugoではcontentの内容をどう処理してページに表示するかを完結にプログラムするための方法が用意されています。 例えば、“Hello,world!“というタイトルのコンテンツに対し、次のテンプレートを適用します。

1
2
3
<div id="title">
	{{.Title}}
<div>

すると、

1
2
3
<div id="title>
	Hello, world!
</div>

が実際のページとして出力されるといった具合です。

詳しくはHugoの公式ドキュメントによくまとめられているので、このページを一通り見ればだいたい理解できると思います。

記法の次に気になるのがページやセクションがどのような構造体として定義されているかどうかだと思うのですが、それも公式ドキュメントのVariablesのページにまとめられています。

実際に作成する手順

もう散々日本語を書いて疲れてきたので、この記事では実際にページをつくりながら手順を追って説明するといったことは しないことにします。

その代わり、どこから手をつければよいかのヒントだけを書いておくことにします。

1. _default/baseof.html

今まで全く説明しませんでしたが、baseof.htmlはすべてのページの素体とできるファイルです。 非常に重要ですが、これ以上の意味や活用法がないのでここで初めて説明しました。

_defaultフォルダ内に入ってることからわかると思いますが、これもまたルックアップオーダーが存在します。 でも僕はデフォルトだけで十分だったので説明しません。必要になったらこのページに詳しく書いてあります。

いま現状次の内容がすでに入っていると思います。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<!DOCTYPE html>
<html>
    {{- partial "head.html" . -}}
    <body>
        {{- partial "header.html" . -}}
        <div id="content">
        {{- block "main" . }}{{- end }}
        </div>
        {{- partial "footer.html" . -}}
    </body>
</html>

hugoは{{関数名 引数1 引数2…}}という記法で関数適用できます。 ここで使われている関数はpartial関数と、block関数ですね。

partial関数は、引数で指定した名前と同じファイルをlayouts/partials/フォルダから見つけてきて、内容をコピペする、言わば#includeと同じです。

block関数はpartial関数をよりインテリジェンスにしたもので、今参照しているテンプレートをこれと組み合わせることができます。

このbaseof.htmlを使うためには、他のテンプレートをmainblockとして定義する必要があります。例えば、つぎのようにです。

1
2
3
4
5
6
7
8
9
{{ define "main" }}
  <h1>Posts</h1>
  {{ range .Pages }}
    <article>
      <h2>{{ .Title }}</h2>
      {{ .Content }}
    </article>
  {{ end }}
{{ end }}

_default/single.htmlにこのように書いておけば、上記のbaseof.htmlと組み合わさって完全なhtmlページが完成するといった具合です。

baseof.htmlではpartialとしてhead.html、header.html、footer.htmlが要求されているので、次はこれらを作ることにしましょう。

2. partials

正直今回は1つのbaseof.htmlをベースに使うので、head、header、footerに関してはpartialsとして外に出さずに 直接baseof.htmlに記述しても問題ないとは思いますが、デフォルトで分割されているのでそれに従うことにします。

このあたりから実際にhugo serverを起動して、実際の出力をチェックしながらコーディングしたくなると思います。 今の状態だとトップページにアクセスしても、mainblockが未定義になってしまうので、先にindex.htmlに

1
2
{{ define "main" }}
{{ end }}

とだけ書いておけば、コンパイルが通り、http://localhost:1313/で内容が確認できるはずです。

また、このへんでcssを書きたくなると思いますが、cssは/static/フォルダに入れます。

例えば/static/css/main.cssを作成したときは、partials/head.htmlに

1
<link rel="stylesheet" href="/css/main.css">

と書けばいいですね。

3. index.html

ページの素体ができたのでトップページの中身を完成させましょう。

4. _default/single.html

実際のページの出力をチェックするために、content/test/sample.mdなどのテスト用のコンテンツを予め作成しておくとよいでしょう。 チェックするときはhttp://localhost:1313/test/sample/で見れます。 hugo newのデフォルトだと、ファイル内の設定がdraft: trueになっていて生成されないので、これを消すかfalseにするのをお忘れなく。

最低限は次のようになると思います。

1
2
3
4
5
6
7
8
9
{{ define "main" }}
	<h1>Single</h1>
	<div id=article>
		<h2>{{ .Title }}</h2>
		<div id=articleBody>
			{{ .Content }}
		</div>
	</div>
{{ end }}

5. _default/list.html

http://localhost:1313/testでチェックできます。

最低限はつぎのようになると思います。

1
2
3
4
5
6
7
8
{{ define "main" }}
	<h1>List</h1>
	{{ range .Pages }}
		<div id=element>
			<h2>{{ .Title }}</h2>
		</div>
	{{ end }}
{{ end }}

6. 404.html

罠なんですけど、hugo serverでは存在しないページにアクセスしても適当な文言が表示されるだけで、自前の404.htmlが表示されません。 チェックするにはhttp://localhost:1313/404.htmlを見ます。

AWS Amplifyへ接続

AWSのアカウントとドメインさえあればほんと一瞬で終わります。

AWS上でAWS Amplifyのページに移動し、Deployの方のGET Startedを選びます。

最初にレポジトリ連携の画面が出るので、利用中のGitサービスを選び、hugoプロジェクトをあげている レポジトリを選択します。

すると自動的にhugoを利用することを検出してくれるので、あとはSave&Deployを選べば処理が始まります。

最初はAWS側からランダムなドメインが提供されていますが、App SettingsのDomain managementから自分の所有するドメインを選ぶことができます。 SSLもこのとき自動で設定されます。

めでたしめでたし。

その他

初期設定だと、存在しないページにアクセスしても404が表示されず、index.htmlにリダイレクトされます。 多分SPAなど、ユーザーサイドのルーティングをもちいるアプリケーション向けの設定でしょうか。 Hugoでは使わないので、Amplifyコンソールの「書き換えて・リダイレクト」という謎命名の設定項目で 送信元アドレスを/<*>に、ターゲットアドレスを/404.htmlに、入力を404(Rewrite)に、国は空欄に設定しました。 これで存在しないページにアクセスししたとき、404.htmlに書いた中身が表示されるようになります。

デフォルトのHugoのバージョンがちょっと低くて、設定無しでコードスニペットを利用することができませんでした。 AWS側で使うHugoのバージョンを変更するには、ビルドの設定→Build image settings→Live package updatesで編集できます。 僕はバージョン0.64.1を設定しました。