おもこん

おもこんは「思いつくままにコンピュターの話し」の省略形です

「Jekyll + GitHub Pages」のチュートリアルを公開しました

このブログの「Jekyllを勉強中」をもとに、内容を整理し、より分かりやすく書き直したチュートリアルが完成しました。

トップ画面のイメージです。

Jekyll tutorial for beginners

ブログとチュートリアルの違い

ブログとの違いは4つです。

  • 内容の順を変えている
  • テーマにCaymanを使っている
  • 変数の名前やファイル名をより適切なものに変更している
  • 各ページ間の移動がリンクを使ってよりスムーズにできる

このチュートリアル自身がJekyllとGitHub Pagesを使った実例になっています。

Caymanについて

ブログではLeap dayをテーマに使いましたが、チュートリアルではCamanに変更しました。 その理由は

  • 背景のデザインが落ち着いているので本文にフォーカスしやすい
  • フォントが読みやすい
  • スマホのレイアウトが見やすい

ということです。 Leap dayの特徴であるナビゲーションがCaymanにはありませんが、さほど不便だとは感じませんでした。

スタイルシートの違いを除くと、レイアウトの構成は似ています。 そのことから、テーマの変更をするのは簡単でした。 _config.ymlのテーマを書き換える以外の修正はほとんどいりません。 おそらくGitHub Pagesでサポートされている他のテーマでも変更は簡単だと思います。

チュートリアルについて

初心者向けの易しいチュートリアルなので、多くの方に読んでいただければと思います。

今後「Liquidを使ってデータを処理する」「GitHub Actionsを使う」などを追加したいと思いますが、今は少し休憩したい気持ちです。

Jekyllを勉強中(9)

長くJekyllのことを書いてきましたが、今回で最後です。

今回は、Jekyllでサイトを作る上で中心となるLiquidのポイントを説明します。 Liquidには簡潔なドキュメントがありますので、参考にしてください。

Liquidの基本

Liquidはhtmlの中に埋め込みます。 ちょうどphp、あるいはRailsのeRubyに似ています。 埋めこむ対象が3つあります(オブジェクト、タグ、フィルター)。 また、Liquidが埋め込まれたHTMLをテンプレートと呼んでいます。

オブジェクト

オブジェクトは、何らかの情報を持っているものです。 オブジェクトは変数で参照されます。 例えば、pageという変数はページの情報を持っているオブジェクトを指しています。 その情報はドット記法で参照します。

  • page.title ページのタイトル
  • page.description ページのディスクリプション

オブジェクト「page」につけたドットの後の部分をプロパティといいます。 titleやdescriptionはpageオブジェクトのプロパティです。 ページのフロントマターに記述されたものはプロパティになり、ドット記法で参照することができます。

オブジェクトをHTMLの中に表示するには二重波カッコを使います。

{{ page.title }}
タグ

タグは制御構造(分岐、ループ)、テンプレート(コメント、raw)、変数への代入などです。 他の言語のステートメントにあたるものです。 タグを使うときは、それを出力することは原則としてありません。 タグは波カッコとパーセントで囲みます。

{% if page.title == "サンプル" %}
フィルター

フィルターは縦棒|で左の出力を右に流すものです。 ちょうどシェル・スクリプトのパイプのようなものです。 出力結果はHTMLに表示することが多いので二重波カッコを使うことが多いです。

{{ page.title | escape }}

パイプの右側にフィルターの動作を記述します。 escapeはHTMLで特殊な意味を持つ記号をエスケープします。 例えば<を&lt;と変更します。

フィルターは多くの動作をサポートしています。 他の言語、例えばRubyなどの演算子やメソッドの役割をフィルターが担っています。

Jekyllで使う変数

Jekyllで使える変数はJekyllのドキュメントに書かれています。 変数はLiquidで使います。

グローバル変数は次のとおりです。

  • site => サイトの情報。_config.ymlに書かれた情報をもつオブジェクトを参照する(以下「オブジェクトを参照する」を略す)
  • page => ページの情報。ページのフロントマターに書かれた情報
  • layout => レイアウトの情報。そのフロントマターの情報
  • content => ページのコンテンツ
  • paginator => パジネーター。ブログ記事がたくさんある場合など、それをページに分割するときに使う

siteの情報を見るのに「site.url」(サイトのURL)のようにドット記法が使えます。 詳細はJekyllのドキュメントを参照してください。

Liquidのオペレーター

Liquidのオペレータはコントロールフロー(ifなど)やループ(for)の条件に使えます。

  • ==
  • !=
  • >
  • <
  • >=
  • <=
  • or
  • and
  • contains => 左側の文字列が右側の文字列を含んでいればtrue

オペレータが複数ある場合は右から順に評価されます。 また、かっこ(())で評価順を変更することはできません

Liquidには算術演算子加減乗除)がありません。 その代わりにフィルターを使います。

タグ(Liquidの制御構造)

ここでは項目が多いので極めて簡単な説明だけになっています。 詳しい説明はLiquidのドキュメントを見てください。

コントロール・フロー

コントロール・フローは条件分岐です。 他の言語と同様の条件分岐ができます。 コントロール・フローは次の2種類です。

  • if/unless (条件)〜 elsif/else 〜 endif
  • case(条件)when 〜 [when 〜] else 〜 endcase
イテレーション(ループ)

ループをLiquidではイテレーションと呼んでいます。

  • for (条件)〜 else 〜 endfor。 for文の中のelseは条件が成り立たなかった時に出力される
  • break => ループを中止して外にでる
  • continue => ループの現在の回をスキップして次の回にすすむ
  • forループにはlimit/offset/range/reversedのオプションをつけることができる
  • forの中でcycle (項目のリスト)を使うことができる。 項目は1回目はリストの最初の項目、2回めは2番めの項目・・・と出力される
  • tablerow => ループの中でHTMLの表の行を出力する。 cols/limit/offset/rangeのオプションをつけることができる

tablerowはHTMLの表を作れるので便利です。 HTMLテンプレートらしい機能です。

テンプレート
  • comment 〜 endcomment => コメントとして扱われHTML出力されない
  • raw 〜 endraw => Liquidの解釈をせずにそのまま出力される。 例えば二重波カッコをそのまま出力したい場合に使う。
  • liquid => 複数行にわたりLiquidの構文を書くことができる。 いちいち{% %}で囲まずにすむのがありがたい
  • echo => タグの中で用いられ、echoの次の要素を出力する。 フィルターを使える
  • render => 他のテンプレートファイルを読み込む
変数
  • assign => 変数への代入をする
  • capture => 変数への代入をする
  • increment/ decrement => 引数に変数をとり、0/-1からはじめて変数を1ずつ増加/減少させ、その値をHTMLに出力する。

JekyllのLiquidで良く用いられるフィルター

以下では、JekyllのLiquidで良く用いられる書き方を順不同で説明します。

default

パイプの左側からの入力がnil、false、空文字列のいずれかであるときに引数の値を出力し、それ以外は入力をそのまま出力します。

{{ page.title | default: 無題 }}
  • ページタイトルが設定されていれば、その文字列を出力
  • ページタイトルが設定されていなければ「無題」を出力

おそらく最も使われているフィルターです。

relative_url

ベースURLを加えたURLにします。

GitHubを例にとって説明します。 GitHub Pagesのサイトはhttps://ユーザ名.github.io/以下に展開されます。 そのスペースを分割していくつかのサイトを構築する場合、ベースURLを個々のサイトにつけます。 例えば、そのサイトのベース名がjekyll-tutorial-for-beginnersであれば、そのサイトはhttps;//ユーザ名.github.io/jekyll-tutorial-for-beginners/をベースとして展開されることになります。

URLの指定には3通りがあります。 例えばサイトのルートにあるabc.htmlは

  • https;//ユーザ名.github.io/jekyll-tutorial-for-beginners/abc.html これを絶対URLといいます
  • /jekyll-tutorial-for-beginners/abc.html これはいろいろの呼び方をされていますが、「The URL Standard」によると絶対URLの一種で、「path-absolute-URL string」(絶対パスURL文字列)というようです。 ですが、https:のついた絶対URLとは振る舞いが違います。
  • 今かりにhttps;//ユーザ名.github.io/jekyll-tutorial-for-beginners/abc.htmlの画面を開いているとします。 そのとき、現在の画面のURLとabc.htmlのURLは最後の部分の違いだけです。 URLを単に「abc.html」と表現することを相対URLといいます。 現在画面のURLと相対URLを組み合わせると絶対URLを求めることができるので、これもURLの表現として可能です。

このうち2番めが問題になります。 /(ルート)がどこに設定されるのかはサーバによります。 GitHubでは/https;//ユーザ名.github.io/と解釈されます。 ですから、サイトのルートhttps;//ユーザ名.github.io/jekyll-tutorial-for-beginners/GitHubのルートとずれがあるわけです。 この差jekyll-tutorial-for-beginnersをJekyllではBase URLといいます。

relative_urlフィルターは流れてきた文字列の先頭にBase URLを付け加えます。 なお、Base URLは_config.ymlの中で定義します。

_config.yml での定義
baseurl: /jekyll-tutorial-for-beginners

{{ "/assets/image/abc.png" | relative_url }}
これにより、出力は次のようになる
=> /jekyll-tutorial-for-beginners/assets/images/abc.png

このことによって、サイトのルートがGitHubのルートに一致するようになります。 なお、baseurlのデフォルト値は空文字列です。

Base URLを定義するかどうかはサーバーサイトのルートの解釈で決まってきます。 また、URL表現の1,3番すなわちhttpsから始まる絶対URLまたは相対URLを使っている限りはこの問題は起きません。 ですから、複雑さを避けるには相対URLのみを使うのが良いと思います。

ただし、Liquidでpage.urlを参照すると/から始まる絶対パスURLが帰ってくるので、relative_urlが必要になります。

absolute_url

文字列の先頭にsite.urlとsite.base_urlを加えます。 前提として_config.ymlにurlを登録することが必要です。

url: https://(ユーザ名).github.io

文字列の最後にスラッシュはつきません。 デフォルト値はhttps://lofalhost:4000です。

escape

escapeはHTMLで特別な意味を持つ文字をエスケープします。

  • < => &lt;
  • > => &gt;
  • & => &amp;

これ以外にもエスケープ文字はあるので、詳細はインターネットで調べてみてください。 とくに<と>が現れるとHTMLタグと解釈されてしまうので、文字そのものを出力するにはエスケープが必要です。 このときescapeフィルターをかけてやります。

例えばpage.titleにはエスケープしなければならない文字が入っているかもしれません。 page.titleはレイアウトの中で使うことが多いと思います。 その場合レイアウトの使用者は自分自身とは限りません。 あらかじめエスケープすべき文字が入っているかどうかわからないので、escapeは必須です。

strip_html

HTMLタグを取り去りたいときに使います。 markdownifyを使う後に使うことが多いと思います。 marakdownifyの説明は後でします。

{{ f.image_caption | markdownify | strip_html }}

「マークダウンをHTMLに変換してタグを取る」ということは、ベタなテキストを手に入れることになります。

replace

文字列置換です。 1番めの引数を2番めの引数に置換します。

{{ "暑いなあ" | replace: "暑い", "寒い" }}
=> 寒いなあ
split

文字列を引数をデリミタとして分割した配列にします。

{% assign ary = "abc, def, ghi, jkl" | split: ", " %}
{% for s in ary %}
  {{ s }}
{% endfor %}
=>
abc
def
ghi
jkl

結果は4行になっていますが、HTMLなのでブラウザ上の表示はつながってしまいます。 このように、コンマ区切りのデータを配列にするときに良くつかいます。

markdownify

これは、Jekyllが拡張したフィルターです。 マークダウンで記述された文字列をHTMLに変換します。 マークダウンで書かれたデータを処理するケースで使います。

date

タイムスタンプを別の形式の時刻表示に切り換えます。 形式の指定は引数の文字列で与えます。 形式にはstrftimeが用いられます。

{{ "2022-8-21" | date: "%Y 年 %m 月 %e 日" }}
=> 2022 年 08 月 21 日
remove

引数の文字列を削除します。

{{ "<p>abcd</p> | remove: <p> | remove </p> }}
=> abcd

シリーズの最後に

はじめてみたら9回も続くことになった「Jekyllを勉強中」は今回で終わります。 自分自身が勉強をしながらの説明だったので、不十分なところが多々ありましたことをお詫びします。

GitHubチュートリアルをあげました。 このチュートリアルはCaymanをテーマに、Jekyllで作られています。 内容はこのブログとほとんど同じですが、構成を変え、説明をより分かりやすくなるように手直ししました。

Jekyllを勉強中(8)

今回はブログの作り方です。

ブログの仕組みの作り方

Leap daysはデフォルトでブログ機能をサポートしていないので、自分で仕組みを作らなければなりません。 といっても、それは大変な作業ではありません。 デフォルトのテーマminimaがブログをサポートしているので、それを参考に作るのが早いです。

あとはブログ記事を書いて_postsディレクトリに保存するだけです。

Minimal Mistakes

Jekyllのテーマはたくさんあり、JekyllのウェブサイトのResourcesにリンクがあります。 ここではブログ機能をサポートしているMinimal Mistakesをテーマにしてブログのサンプルを作ってみます。 なお、ここではローカルで動かします。 GitHubと連携するには別の方法が必要です。 Minimal Mistakesのドキュメントを参考にしてください。

インストール

ブログ用のディレクトリを作ります。 ここでは「sample」という名前にします。

$ mkdir sample
$ cd sample

Gemfileを作ります。

source "https://rubygems.org"

gem "minimal-mistakes-jekyll"
gem "webrick"

_cofig.ymlを作ります

   theme: minimal-mistakes-jekyll

gemをインストールします。

$ bundle install

以上でインストールは完了です。 簡単!!

index.mdを作る

トップページの名前は「index.md」と決まっています。 簡単なメッセージを書いておくことにします。

---
layout: home
title: サンプルページ
description: Sample site with Minimal Mistakes
header:
  image: "/assets/images/home.png"
---

みなさん、こんにちは!
  • レイアウトはhomeにする
  • titleとdescriptionを書く
  • ページを少しお洒落にするためにタイトルのヘッダに写真を入れる。 headerキーの下にimageキーで画像の場所を指定する

タイトル画面の画像を設定するyamlはMinimal Mistakesのレイアウトを調べると分かります。 ドキュメントには短く書かれているだけで分かりにくいです。 ソースコードを見るのが一番確かなのですが、ドキュメントにも書いてほしいです。

それでは、画面をチェックしましょう。

$ bundle exec jekyll serve

ブラウザでlocalhost:4000を開きます。

サンプルのホーム画面

写真が入るとイメージが良くなりますね。

記事の投稿

ブログの投稿記事は日時で順番付けられます。 日時はファイル名とフロントマターの2ヶ所に書きます。

---
layout: posts
title:  "Welcome to Sample page"
description: "The first post to Sample page"
date:   2022-08-20 14:05:00 +0900
---

はじめて記事を投稿します。

Sample page をよろしく!
  • layoutはpostsにする。これはMinimal Mistakesの仕様です。minimaではpostと単数形
  • タイトルとディスクリプションを書く
  • dateに日時を書く。フォーマットはYYYY-MM-DD HH:MM:SS +900。最後の+900はTokyoの標準時からの時差
  • ファイル名は2022-08-20-welcome-to-Sample-pagei.mdとした。「日付-タイトル.md」のフォーマット

もう一つ記事を作ってみます。

---
layout: posts
title:  ふたつめの記事
description: この記事が投稿2回目
date:   2022-084-20 14:15:00 +0900
---

2回目の投稿です。

Sample page をもっともっとよろしく!

ファイル名は2022-08-20-The-second-post.mdとしました。 全角文字を使ってもたぶん良いと思います。

2つの記事はホーム画面のコンテンツの下にリンクとして現れます。

まとめ

このように、ブログの仕組みがテーマに用意されていれば、記事の投稿は簡単です。 また、Leap dayも良かったのですが、Minimal Mistakeは洗練されたデザインでこれも使ってみたくなりました。 しかもざっと見ただけですが、機能が豊富です。

インターネットを見ると、「はてな」からJekyll+GitHubに移行した人もいました。 ある程度スキルが必要ですが、テーマを使えば敷居は低いように思います。

次回は、前回紹介したLiquidについて取り上げたいと思います。

Jekyllを勉強中(7)

追記 2022/8/23: コレクションの名前に「documents」を使っていましたが、この名前はJekyll自身が別の意味で使う変数になっていたので、「chapters」に変更しました。 また、_config.ymlの中で使っていたsort_byキーはJekyll4.0以降で可能ですが、GitHubのJekyllのバージョンは3.9.2でまだサポートされていません。 そこでsort_buキーを使わないように変更しました。

追記 2022/8/19: GitHubにあげる場合、baseurlの設定が必要な場合があります。 修正点は2つで(1)_config.ymlにbaseurlを設定する(2)レイアウトにrelative_urlフィルタを追加する、です。 baseurlの解説はJekyllを勉強中(9)にあります。 この記事中のプログラムは修正済みです。

今回は、レイアウトについて説明します。 あわせて、Jekyllが採用しているテンプレート言語のLiquidについても簡単な説明をします。

ファイルの構成と配置

このシリーズで例にとりあげてきた「Jekyll tutorial for beginners」の構成を考えてみます。

このうちドキュメントは「_chapters」フォルダに入れることにします。 ディレクトリ名の最初にアンダースコア(_)がつくのは、Jekyllのコレクションという機能を使うからです。

+--- _config.yml
+--- index.md
+--- about.md
+--- toc.md
+---_chapters
|       +--- doc1.md
|       +--- doc2.md
|       + ... ... ...
+---assets
|       +--- css
|       +--- images
|       +--- js
+---_site
... ... ...

トップディレクトリ以下のファイル構成が出力ディレクトリ_site以下に反映されます。 ただし、Markdown=>HTML、Scss=>Cssと変換して生成されます。 生成されたページ相互に行き来できるようリンクを付けなければなりません。

コンテンツ内上部にリンクを横並びに設置する方法を取ることにします。 このリンクのことをコンテンツ・ナビゲーション(短くして「コナビ」)と呼ぶことにします。 これは、このブログの中での呼び方で、一般に使われるものではありません。 複数のファイルで同じコナビが使われるので、コナビはレイアウトに記述するのが良いです。

レイアウトhome.html

「index.md」「about.md」「toc.md」の3つのファイルのコナビを記述するレイアウトを「home.html」とします。 新たに_layoutsフォルダを作り、その下にhome.htmlを置きます。

---
layout: default
---
<ul class="nav">
  <li>
    <a href={{ "/index.html" | relative_url }}>トップページ</a>
  </li>
  <li class="nav">
    <a href={{ "/about.html" | relative_url }}>Aboutページ</a>
  </li>
  <li class="nav">
    <a href={{ "/toc.html" | relative_url }}>目次</a>
  </li>
</ul>
{{ content }}

フロントマターにlayout: defaultがあるので、デフォルトのレイアウトのコンテンツにhome.htmlのレイアウトが設定されます。

それぞれのページへのリンクが順序なしリストになっています。 リンクにかかっているフィルターの説明は「Jekyllを勉強中(9)」にあります。

クラスnavスタイルシートは「style.scss」に記述し、assets/cssフォルダに置きます。

---
---

@import "{{ site.theme }}";

ul.nav {
  display: flex;
}
li.nav{
  list-style: none;
  margin-left: 20px;
}

(注意)このファイルの内容はcssですが、インポートするファイルがscssなので、拡張子はscssにします。

site.theme_config.ymlで設定したthemeキーの値で、「jekyll-theme-leap-day」です。 同名のScssファイルがテーマに含まれています。

「index.md」「about.md」「toc.md」のフロントマターのレイアウト設定をdefaultからhomeに変更します。 ここまでで、index.mdを表示するとコンテンツの最初に3つのリンクが横並びに表示されます。

コンテンツ・ナビゲーション

コレクション

Jekyllにはコレクションという機能があります。 コレクションはページ(またはそのページを生成するファイル)を整理し、管理できるようにします。

例えば「doc1.md」「doc2.md」・・・は「チュートリアルのドキュメント」という括りでまとめられます。 これらを「_chapters」ディレクトリの下に入れることにより、他のファイルと区別でき、ルートディレクトリもすっきりします。 そしてコレクションは_config.ymlの中で定義します。 コレクション名はディレクトリ名から先頭のアンダースコアを取ったものになります。

collections:
  chapters:
    output: true
  • コレクションは「collections」をキーとするハッシュで表す
  • この例では「chapters」がコレクション名で、更にその属性がハッシュで表される
  • デフォルトではコレクションは出力されないので「output: true」で出力されるようにする

この定義により、Liquidのタグなどでsite.documentsが「documentsコレクション」を表すようになります。

Base URL

_config.ymlにBase URLの設定をします。 Base URLの説明は「Jekyllを勉強中(9)」にあります。

baseurl: /jekyll-tutorial-for-beginners

ドキュメントのレイアウト

ドキュメントではコナビに「前の章」「次の章」を加えます。 また、コナビはコンテンツの最初と最後につけることにします。 2箇所に同じコナビをつけるので、コナビを独立したファイルにし、レイアウトの中でインクルードします。

インクルード

インクルード対象のファイルは「_include」ディレクトリに置きます。

コナビを表すファイルは下記のリストのようになり、_include/doc_nav.htmlに保存されます。

<ul class="nav">
  <li>
    <a href={{ "/index.html" | relative_url }}>トップページ</a>
  </li>
  <li class="nav">
    <a href={{ "/about.html" | relative_url }}>Aboutページ</a>
  </li>
  <li class="nav">
    <a href={{ "/toc.html" | relative_url }}>目次</a>
  </li>
  {% if page.chap > 1  %}
    <li class="nav">
      <a href="doc{{page.chap|minus: 1}}.html">前の章</a>
    </li>
  {% endif %}
  {% if page.chap < site.chapters.size %}
    <li class="nav">
      <a href="doc{{page.chap|plus: 1}}.html">次の章</a>
    </li>
  {% endif %}
</ul>

上から10行まではhome.htmlのレイアウトに書いた内容と同じです。 {%raw%}{%%}{%endraw%}で囲まれたものはLiquidのタグと呼ばれます。 ここにはプログラムのコントロール・フロー(if, else, unlessなどの条件分岐)などを書くことができます。

{% if page.chap > 1  %}
  <li class="nav">
    <a href="doc{{page.chap|minus: 1}}.html">{{chap}}前の章</a>
  </li>
{% endif %}

ifの条件は「そのページの章(chap)が1より大きい」すなわち「第2章以降」で、このときは「前の章」が存在します。 条件が成立する時にifとendifで囲まれた部分が出力されますが、その出力内容は「前の章へのリンク」です。

例えば第3章の前は第2章で、ファイル名は「doc2.html」(変換後なので拡張子は.mdではない)になります。 数字の2にあたる部分はLiquidのオブジェクト({%raw%}{{}}{%endraw%}で囲まれた部分)で表されています。

{{page.chap|minus: 1}}

縦棒|は前にも出てきた「フィルター」というもので、左の出力を右の入力につなげます。 このときフィルターの動作はパイプの右側に記述します。 「minus」は引き算をします。 例えば第3章では「page.chap」は3なので、「minus: 1」はそこから1を引き、2になります。

Liquidには算術演算子が無く、計算はフィルターを使って行います。

{% if page.chap < site.chapters.size %}
  <li class="nav">
    <a href="doc{{page.chap|plus: 1}}.html">次の章</a>
  </li>
{% endif %}

ほとんど同じですが、site.chapters.sizeのところが新たな内容です。 変数siteにコレクション名をつけ、更にsizeをつけています。

  • site.chaptersはchaptersコレクションに属するページの配列を返す
  • sizeは文字列の文字数や配列の要素数を返す。 本来パイプ(|)とともに使われるフィルターだが、 タグ(<%raw%}{%%}{%endraw%}で囲まれた部分)では、ドット(.)記法が使える。

章の数が配列のサイズより小さい、すなわち「最後の章ではない」ときに「次の章」をコナビに入れます。

ドキュメントのレイアウト

ドキュメントのレイアウトはdocument.htmlです。

---
layout: default
---
{% include doc_nav.html %}
<h1>第{{page.chap}}章 {{page.title | escape}}</h1>
{{ content }}
{% include doc_nav.html %}

前に作ったdoc_nav.htmlを2ヶ所でインクルードしています。 インクルードのおかげで短くさっぱりした記述になっています。 コナビとコンテンツの間に「第何章」というタイトルをh1タグで入れました。 escapeフィルタは「Jekyllを勉強中(9)」で説明されています。

ドキュメント

ドキュメント(doc1.mdなど)のフロントマターにはchapなどを入れます。 例えば、第1章のドキュメントのフロントマターは次のようになります。

---
layout: document
title: Jekyll、GitHub Pagesとは
description: JekyllとHitHub Pagesの基礎知識
chap: 1
---
  • レイアウトはdocument.htmlを使う
  • タイトルとディスクリプションを指定=>default.html(document.htmlの親レイアウト)で使われる
  • chapは章の番号を表す=>document.htmlで使われる

目次

目次の生成にはforループを使います。 以下はtoc.mdソースコードです。

---
layout: home
title: 目次
description: チュートリアルの目次
---
## 目次

{% assign chaps = site.chapters | sort: "chap" %}
{% for doc in chaps %}
- [{{doc.chap}}章 {{ doc.title | escape }}]({{ doc.url | relative_url }})
{% endfor %}

下3行が目次を生成する部分です。

  • site.chaptersはchaptersコレクションに含まれるページの配列。 この配列を章の番号順にソートするために、フィルターが使われている。 | sort: "chap"はフロントマターのchapキーの値で配列をソートしたものを値とする。 (このソートされた配列は元の配列とは別オブジェクト)。 変数chapsにソート済み配列が代入される
  • for文は変数docに次々と配列要素を入れてループする
  • {{doc.chap}}はそのページの章番号になる
  • {{doc.title}}はそのページのタイトル
  • {{doc.url}}はそのページのURLアドレスになる

このようにして、各章へのリンクがリストとして生成されます。

Leap dayの日本語対応

Leap dayのJavascriptjQuery)は、見出しが日本語のときに上手く動作しません。 修正は、次のようにします。

  • assets/jsディレクトリを作る
  • Leap dayのgemの場所を調べる($ bundle info jekyll-theme-leap-dayでパスが表示される)
  • Leap dayのassets/js/main.jsを最初に作ったassets/jsの下にコピーする
  • 下記のようにmain.jsを修正する
... ... ...
... ... ...
$(window).resize(sectionHeight);

var tag_number = 1;

$(function() {
  $("section h1, section h2, section h3").each(function(){
    $("nav ul").append("<li class='tag-" + this.nodeName.toLowerCase() + "'><a href='#tag_" + String(tag_number) + "'>" + $(this).text() + "</a></li>");
    $(this).attr("id","tag_"+String(tag_number));
    ++tag_number;
    $("nav ul li:first-child a").parent().addClass("active");
  });
});
... ... ...
... ... ...

変更箇所は4ヶ所です。

  • var tag_number = 1;
  • $(function()から2-3行目のString(tag_number)に置き換える部分(2ヶ所)
  • ++tag_number

手作業では間違いやすいのでコピペするのが良いと思います。

ここまでで、Jekyllの使い方がかなり分かってきたのではないでしょうか。 次回はJekyllでブログを書く方法を扱おうと思います。

Jekyllを勉強中(6)

今回は、Leap-dayテーマのレイアウトを説明し、index.htmlを作ってみます。

レイアウトファイルの置き場

Jekyllではレイアウトファイルの置き場は/_layoutsです。 ただし、最初のスラッシュはJekyllのソースファイルのトップディレクトリです。 Linuxのルートディレクトリではないので注意してください。

Leap-dayテーマはgemで提供されているので、そのレイアウトはgemがおかれている場所の_layoutsディレクトリになります。

$ bundle info jekyll-theme-leap-day
  * jekyll-theme-leap-day (0.2.0)
    Summary: Leap Day is a Jekyll theme for GitHub Pages
    Homepage: https://github.com/pages-themes/leap-day
    Path: (ここにそのPCにおけるjekyll-theme-leap-day-0.2.0のパスが表示される)
    Reverse Dependencies: 
        github-pages (227) depends on jekyll-theme-leap-day (= 0.2.0)

そのディレクトリのdefault.htmlというファイルがleap-dayのレイアウトを記述したファイルになります。

レイアウトの構成

Leap-dayは次のようなレイアウト構成になっています。

Leap-dayのレイアウト

画像をクリック(またはタップ)すると大きくなります。

  • 上の青い部分が「ヘッダ」で、その中に「タイトル」「ディスクリプション」がある
  • 黄色い部分が「バナー」でアイコンが並んでいる
  • 左側の部分が「ナビゲーション」で、本文の見出しへのリンクが設置されている。 なお、この部分はJavascriptで動的に生成されています。
  • 中央の白い部分が「コンテンツ」

このうち、index.mdのような個々のファイルで変更できるのは「タイトル」「ディスクリプション」「コンテンツ」です。 また、「ナビゲーション」の部分はコンテンツの見出しが自動的に反映されます。

タイトルとディスクリプション

タイトルとディスクリプションはdefault.htmlの20から23行目で記述されています。

<header>
  <h1>{{ page.title | default: site.title | default: site.github.repository_name }}</h1>
  <p>{{ page.description | default: site.description | default: site.github.project_tagline }}</p>
</header>

HTMLに埋め込まれている{{}}で囲まれている部分はLiquidという一種のプログラミング言語です。 LiquidのGItHubページでは、これを「テンプレート・エンジン」と呼んでいます。 h1タグのところがタイトルです。

page.title | default: site.title | default: site.github.repository_name
  • page.titleはページのタイトル。 例えば、index.mdのフロントマターにtitle: インデックスページと書かれていたとすると、その文字列「インデックスページ」がpage.titleに代入される。 それがなくて、コンテンツの最初が見出しの場合は、その見出しがpage.titleに代入される。 ページのタイトルがなければ、nilが代入される。
  • site.titleはサイトのタイトル。 _config.ymltitle: サイトのタイトルと書かれていたとすると、「サイトのタイトル」がsite.titleに代入される。
  • site.github.repository_nameGItHubのレポジトリーの名前。 ローカルでJekyllを実行する場合は、jekyll-github-metadata gemがGitHubに問い合わせてレポジトリー名を取得している。
  • 縦棒|はフィルターと呼ばれているLiquidの構文で、左の式の値が右への入力になる
  • default: はフィルターの動作を規定するコマンドで、入力が空文字列、nil、falseのいずれかであればデフォルト値(default:の次に書かれている式)をとる。 それ以外はパイプから入力された式を値とする。

これよりこのLiquidのフィルタは

  • ページタイトルがあればページタイトルを出力
  • ページタイトルがなくてサイトタイトルがあれば、サイトタイトルを出力
  • ページタイトルもサイトタイトルもなければ、GitHubレポジトリ名を出力

となります。 同様にディスクリプションのところも

  • ページディスクリプションがあればページディスクリプションを出力
  • ページディスクリプションが無くてサイトディスクリプションがあれば、サイトディスクリプションを出力
  • ページディスクリプションもサイトディスクリプションもなければ、GitHubレポジトリのディスクリプション(レポジトリのAboutに書かれている文章)を出力

となります。

index.mdのフロントマターには、原則としてタイトルとディスクリプションを記述するのが望ましいです。

コンテンツ

コンテンツはindex.mdのフロントマターを除いた本体部分です。 拡張子が「.md」なので、Markdownで記述します。 この部分でもLiquidが使えます。 今回はLiquidを使いませんが、後の方で説明する機会があると思います。

サンプルに用いている「Jelyll tutorial for beginners」のindex.mdを次のように作成してみました。 参考にしてください。

---
layout: default
title: はじめてのJekyll + GitHub Pages
description: JekyllとGitHub Pagesを使って静的ウェブサイトを作る
---

# はじめてのJekyll + GitHub Pages

## Written in Japanese

This website is written in **Japanese**.

## このウェブサイトは未完成です

現在、このウェブサイトは作成途中にあります。
書かれている内容も十分吟味されておりませんし、変更も多々あります。
完成までしばらくお待ちください。

## はじめてのJekyll + GitHub Pages

Jekyllは静的ウェブサイトを構築するためのフレームワークです。
また、GitHub PagesはJekyllをサポートし、ウェブサイトのスペースを提供するサービスです。

このウェブサイトは、GitHub Pagesでウェブサイトを作るためのチュートリアルです。

## 目次とリンク

(ここに各章へのリンクが箇条書きで提供される)

サイトタイトルとサイトディスクリプション

サイトタイトルとサイトディスクリプションも書いておきましょう。 _config.ymlに書き加えます。

theme: jekyll-theme-leap-day

title: はじめてのJekyll + GitHub Pages
description: JekyllとGitHub Pagesを使って静的ウェブサイトを作る

exclude:
  - README.md

Jekyllでテスト

Jekyllを動かしてテストしてみましょう。

$ bundle exec jekyll serve

index.md ページ

Jekyllを勉強中(5)

今回は、github-pagesというgemについて書きます。

Gemfileの利点

前回(「Jekyllを勉強中(4)」)Gemfileを使ってJekyllを動作させる上で必要なgemを指定しました。 実際に追加されるgemはGemfileに書いたものより多く、10以上のgemが追加されることが多いです。 これをいちいちインストールする手間を考えれば、GemfileとBundlerの便利さが分かります。

しかし、それだけではありません。 最も重要なことはバージョン管理をサポートしていることです。 Gemfileでは、gemの後に、そのバージョンを指定することができます。

gem 'abcd'
gem 'efgh', '3.0.0'
gem 'ijkl',  '>=1.0'
gem 'mnop',  '~>2.1'
  • abcdは、最新のものがインストールされる(バージョン指定無しの場合)
  • efghはバージョン3.0.0のものがインストールされる
  • ijklのバージョン1.0以上のものがインストールされる
  • mnopはバージョンが2.1以上で3.0未満がインストールされる。 ~>では、最後のドットの前は変わらない。 例えば~>1.2.3.41.2.3.4以上1.2.4`未満。

公開されたRubyプログラムがGemfileを持っていれば、それをダウンロードした誰もが同じバージョンを使うことになります。 それによって、バージョンの違いによる誤動作を防ぐことができます。

github-pages

さて、GitHubでは、レポジトリからページを作成する時に、内部でJekyllが使われます。 このとき、Jekyllやその他のgemのバージョンが、GitHubとローカルで違っていると、出力結果が異なる可能性があります。 GitHubで使うgemのバージョンは公開されています

これだけのgemについて人手でチェックするのは大変です。 それを解決するgemがgithub-pagesです。 このgemをGemfileに書いておけば、必要なgemを正しいバージョンで取り込んでくれるのです。

では、Gemfileを書き直してみましょう

source "https://rubygems.org"

gem "github-pages", "~> 227", group: :jekyll_plugins

gem "webrick"

バージョンの227は先程のページから取ってきます。 このバージョンは比較的頻繁に変わるので、書き換えが必要になることに注意しましょう。 このgemを書いておけば、jekyll-theme-leap-dayなどをGemfileに書く必要はありません。 github-pagesが関連gemを指定しているので、自動的にインストールされます。

Gemfileを更新したら、Gemfile.lockを削除して、bundle installします。

jekyll-github-metadataの警告メッセージ

Jekyllを動かしてみましょう。

$ bundle exec jekyll serve
Configuration file: /(クローンしたディレクトリ)/_config.yml
To use retry middleware with Faraday v2.0+, install `faraday-retry` gem
            Source: /(クローンしたディレクトリ)
       Destination: /(クローンしたディレクトリ)/_site
 Incremental build: disabled. Enable with --incremental
      Generating... 
   GitHub Metadata: No GitHub API authentication could be found. Some fields may be missing or have incorrect data.
                    done in 0.948 seconds.
 Auto-regeneration: enabled for '/(クローンしたディレクトリ)'
    Server address: http://127.0.0.1:4000
  Server running... press ctrl-c to stop.

下から5行目に警告メッセージが出ています。

GitHub Metadata: No GitHub API authentication could be found. Some fields may be missing or have incorrect data.

はじめに「GitHub Metadata:」とあるので、jekyll-github-metadata gemの発出したメッセージであることがわかります。 このgemはgethub-pagesでインストールが指定されていたgemの1つです。 内容は、「GitHub APIの認証が見つかりませんでした。 一部のフィールドが欠落しているか、データが正しくない可能性があります。」ということですが、これだけでは何のことかわかりません。 jekyll-github-metadataのウェブサイトのAuthenticationページを見ると、次のようなことが書いてあります。

  • cname などの一部のフィールドでは、自分自身を認証する必要がある(このことの意味は私はよく分かっていませんが)。
  • それを解決するにはいくつかの方法がある
  • JEKYLL_GITHUB_TOKEN環境変数にPAT(以前https接続のために生成したパーソナル・アクセス・トークンのこと)をセットしてJekyllを起動する
  • netrc gemを用い、~/.netrcにユーザ名とPATを書く
  • OCTOKIT_ACCESS_TOKEN環境変数にPATをセットしてJekyllを起動する(1番目とほぼ同じ)

この3つのうちのどれかをすれば警告メッセージは無くなります。 (ただ、エラーが出ているわけではないので、無視してもそれほど大きな問題にはなりません)。 1番目と3番目の環境変数を使うのが簡単ですが、毎回これをするのは煩わしいです。 その点、2番めは一度セットすれば後は何もしなくて良いのが利点です。

今回はnetrcを使ってみます。 まず、~/.netrcがあるかどうかチェックします。 (~はユーザの(あなたの)ホームディレクトリです。ドットから始まるファイルは隠しファイルなので、CTRL+Hで表示できるようにして確認します)。 もしある場合は下記の内容を付け足します。 なければ新規にファイルを作り下記の内容で保存します。

machine api.github.com
    login <GitHubのユーザ名>
    password <PAT>

次に、Gemfileにnetrc gemを加えます。

source "https://rubygems.org"

gem "github-pages", "~> 227", group: :jekyll_plugins

gem "webrick"
gem "netrc"

Gemfile.lockを削除し、bundle installします。 これでbundle exec jekyll serveで警告は出なくなったはずです。

GitHubと同じ表示になっていることを確認

表示された画面を見てみましょう。

以前と違うのは

  • メニューバーの「View on GitHub」がGitHubのレポジトリへのリンクになっている
  • 左下の方に「Project maintained by (ユーザ名)」の表示が現れている

ことです。 これらは、gethub-pages gemによってGItHubと同じ環境になったことの現れです。

以上で、ローカルでGitHubと同じ環境を構築できました。 前よりもより正確にテストをすることができます。

次回はウェブサイトにコンテンツを加える方法を見ていきます。

GitHubにSSHで接続する

前回のブログで、ローカルのgitとGitHubとでHTTPSプロトコルを使って通信する方法を書きました。 SSHプロトコルを使う方法もあります。 今回はそれを話題に取り上げたいと思います。

この記事を読む前に注意していただきたいこと

会社のコンピュータなどの場合、インターネットと社内ネットワークの間にプロキシサーバーが置かれている場合が多いです。 プロキシサーバーの中にはSSHの通信を禁止している場合があります。 具体的には、SSHサーバーはポート番号22を使うので、プロキシがポート22への通信を破棄すればSSH通信を禁止できます。 この場合はポート22でGitHubへのSSH接続はできませんが、ポート443でもGitHubSSH接続できます。

Using SSH over the HTTPS port

このことを使ってGitHubSSH接続する情報を見つけました。

httpプロキシサーバがわかればGitHubは使える

ただし、私の方では動作確認は取れていません。

とにかく、プロキシがある場合は面倒なことになります。 以下では、プロキシがない場合(個人でプロバイダーを通してインターネットに接続する場合など)を説明します。

SSHについて

SSHは、2台のコンピュータがネットワークを通してデータを暗号化して通信するためのプロトコルです。 主にサーバにリモート・ログインして使うのに用いられますが、GitHubとGitの通信でも使えます。

情報の暗号化には「公開鍵」と「秘密鍵」を使います。 公開鍵と秘密鍵についてはウィキペディアに説明があります。

簡単に説明すると、

  • 秘密鍵は自分の手元に置いておく
  • 公開鍵は誰でも取得することができる
  • 公開鍵でデータを暗号化したものは秘密鍵で元のデータに復元できる
  • 秘密鍵でデータを暗号化したものは公開鍵で元のデータに復元できる
  • 公開鍵で暗号化したデータを公開鍵で元に戻すことはできない。秘密鍵も同様。

AがBに暗号化したデータを送るとき、万一B以外がその暗号データを手に入れても、復号できないために

Aが(Bの作成した)公開鍵で暗号化し、Bはそれを(Bの作成した)秘密鍵で元のデータに戻す

という方法が取られます。 秘密鍵と公開鍵の使い方を逆にすると誰でも解読できてしまうので注意が必要です。 (そういう使い方は別の意味で有効なことがありますが)

このことから、GitHubを使っているユーザをAさんとすると

  • AさんがGitHubからデータをダウンロードするときには、GitHubがAさんの公開鍵で暗号化しAさんは(Aさんの)秘密鍵で復元する
  • AさんがGitHubにデータをアップロードするときには、AさんがGitHubの公開鍵で暗号化してGitHubは(GitHubの)秘密鍵で復元する

ということになります。 したがって、データのやり取り以前に公開鍵を相手に伝えておくことが必要になります。

GitHubのドキュメントに、SSHを使ったGitとGitHubの通信の設定方法が書かれています。 これを読みながら設定することをお勧めします。

鍵の生成

鍵の生成にはssh-keygenというコマンドを使います。

$ $ ssh-keygen -t ed25519 -C "ユーザのemailアドレス"

ed25519は暗号化の方式です。 ウィキペディアエドワーズ曲線デジタル署名アルゴリズムに説明があります。

Enter a file in which to save the key

というプロンプトに対しては単にエンターキーを押せば良いです。

Enter passphrase (empty for no passphrase)

というプロンプトに対してはパスフレーズを入力します。 パスフレーズはパスワードのようなものです。 後で使うので控えておかなければなりません。

これで、~/.ssh/に公開鍵「id_ed25519.pub」と秘密鍵「id_ed25519」が生成されます。

ssh-agentをバックグラウンドで走らせ、秘密鍵を登録します

$ eval "$(ssh-agent -s)"
$ ssh-add ~/.ssh/id_ed25519

ここで行った手続きの詳細についてはOpenSSHのウェブサイトを参照してください。

GituHubへの公開鍵登録

作成した公開鍵をGitHubに登録します。 GItuHubのドキュメントも参考にしてください。

  • GItHubをブラウザで開きサインインする
  • 右上のアイコン(プロフィールアイコン)をクリックしポップアップメニューから「Settings」をクリックする
  • 左側の列のメニューから「SSH and GPG Keys」をクリックする
  • 右側の一番上SSH Keysの「New SSH Key」ボタン(緑のボタン)をクリック
  • タイトルフィールドには、キーのタイトル、例えば「〇〇のデスクトップマシン」のようなメモを入力する
  • ローカルのコンピュータの公開鍵「id_ed25519.pub」をエディタで開き、キーをクリップボードにコピーする。 (重要な注意)秘密鍵と間違わないでください
  • ブラウザ画面のKeyのところにペーストする
  • 「Add SSH Key」ボタンをクリックする

Gitのリモート・アドレスの変更

GitHubからクローンしたときにHTTPSを選んでクローンしていた場合は、アドレスをSSH用に変更しなければなりません。 今後クローン時には「HTTPS」でなく「SSH」を選んでください。 以下に例を示します。

$ git remote -v
origin  https://github.com/ToshioCP/jekyll-tutorial-for-beginners.git (fetch)
origin  https://github.com/ToshioCP/jekyll-tutorial-for-beginners.git (push)
$ git remote set-url origin git@github.com:ToshioCP/jekyll-tutorial-for-beginners.git
$ git remote -v
origin  git@github.com:ToshioCP/jekyll-tutorial-for-beginners.git (fetch)
origin  git@github.com:ToshioCP/jekyll-tutorial-for-beginners.git (push)

「get remote -v」はリモートの一覧を表示します。 上の例では「origin」つまりGitHubが唯一のリモートです。 「git remote set-url (リモート名)(アドレス)」はリモートアドレスを変更します。 「git@」で始まるアドレスはSSH通信でのGitHubのレポジトリアドレスです。

ここまでで、SSH通信の準備ができました。

GitからSSHGitHubにアクセスする

最初にGItHubからデータを受け取るときに、GitHubの公開鍵を持っていないとデータの復号ができません。 では、公開鍵はいつどうやって手に入れるのでしょうか。 公開鍵は最初にGitHubからデータを入手するときに送信されてきます。 その公開鍵が正しいものかどうかはこの時点ではわかりません。 そこでメッセージが現れ、その公開鍵のフィンガープリント(fingerprintは指紋という意味)が表示されます。 フィンガープリントはGitHubで公開されています。

このフィンガープリントとパソコンに表示されたフィンガープリントが一致するかどうか確認します。 一致していれば正しいGItHubの公開鍵が送られてくることになります。 「yes」を入力すると公開鍵がローカルに取り込まれます。

これから先は以前と同様、「git clone」「git pull」「git push」などでアクセスできます。

公開鍵は通信の開始時に交換され相手を認証します。 公開鍵はすでに互いが持っているので、それと送られてきた公開鍵を比較することによって相手を確認するわけです。 ですから、仮にGitHubが公開鍵を変更すると、送られてきた公開鍵はローカルに持っている公開鍵と違いますから、先程のフィンガープリントがまた表示され、確認を求められることになります。

以上、HTTPSからSSHに変更する流れを見てきました。 SSHを使えばいちいちユーザ名やパスワードを求められることがないのでプッシュが楽になります。

次回はまたGitHub pagesとJekyllの話に戻ります。