GitLabでHugoブログを簡単に公開する方法

More than 3 years have passed since last update.

最近、GitLabを使い始めていますが、一言で言うと「よい」です。GitLabはGitHubを真似して作られましたが、無料機能が微妙に違います。特に大きいのは以下ですね。


  • プライベートリポジトリを使える


  • GitLab CIが使える


今までCIはリポジトリ管理とは別のサービスで使っていたひとが多いと思います。GitHub+WerckerやGitHub+Travisなど。

しかし、GitLabは例えば、GitLab Pagesをリポジトリ(master)で作り、GitLab CIを使って自動デプロイすると、即座にページを公開できます。今までCIサービスに紐付けして、リポジトリのビルドをしていたので、その辺は随分簡単になっていると思います。

https://gitlab.com/groups/pages

例えば、hugoでページを作りたいなら、以下の手順で公開できます。

$ git clone git@gitlab.com:pages/hugo.git

$ cd hugo
$ rm -rf .git

# 自分のプロジェクト(リポジトリ)にhugoを作成して以下のコマンドを実行
$ git init
$ git remote add origin git@gitlab.com:$USER/hugo.git

# baseurlを自身のものに変更する
$ vim config.toml
baseurl = "https://$USER.gitlab.io/hugo/"

$ git add .
$ git commit -m "first"
$ git push -u origin master

# ページが出来上がるまで多少待たなくてはいけないかもしれません
$ curl -sL $USER.gitlab.io/hugo

https://syui.gitlab.io/hugo

すごく簡単ですね。.gitlab-ciを置いておくと、ソースをプッシュする度にページがビルドされ更新されます。

ここで、$user.gitlab.ioなどのプロジェクト(リポジトリ)名にすると、アドレスがトップになり、それ以外なら$user.gitlab.io/repoという形になります。

このような関係からこちらにあるサイトジェネレーターを使ってページを作成する場合、使用するアドレスによってはソースの変更が必要になります。

例えば、Octopressで$user.gitlab.ioという形でページを作るには、以下のリポジトリを参考に/octopressを削除します。ちなみに、このリポジトリをGitLabの$user.gitlab.ioリポジトリに使うと、何もしなくてもそのままページを表示できるようになっています。ただ、_config.ymlのURLはユーザーのものに変更しないといけませんが。

公式は$user.gitlab.io/octopressという形を予定しているために、コードを変更しないと適切に表示されません。具体的には、/octopressという指定をコードから削除する必要があります。トップではなく、リポジトリ名を使う場合は、/octopressをリポジトリ名に置き換える必要があります。

もちろん、グループも使えますので、$userのところは、既にGitLabで取得されていないアカウントIDでないかぎり変更可能です。

特にCIとの連携が機能すると思います。このことは、単純なWebサイト作成手順を見ても明らかです。


Hugoのアーカイブページを作成する

Hugoに全記事一覧を分かりやすく表示するページを作成します。以下の記事でだいたいわかると思います。

https://parsiya.net/blog/2016-02-14-archive-page-in-hugo/

自分の場合は、以下の様なレイアウト。


layouts/archive/single.html

{{ partial "head" . }}

{{ partial "header" . }}
<div id="container">
<div class="outer">
<div id="blog-archives" class="category">
{{ .Content }}
{{ range (where .Site.Pages "Type" "post") }}
<h1>
<time datetime="{{ .Date }}" itemprop="datePublished">{{ .Date.Format .Site.Params.date_format }}</time>
<a href="{{ .Permalink }}" title="{{ .Title }}">{{ .Title }}</a>
</h1>
{{ end }}
</div>
</div>
</div>
{{ partial "footer" . }}
</body>
</html>

あまりカスタマイズしていません。


Hugoの検索システム

Hugoの検索システムを構築する方法です。

大体は下記のコードのとおりです。具体的にGrunt.jsLunr.jsでHugo用に構築します。

https://gist.github.com/sebz/efddfc8fdcb6b480f567

自分の場合の構築方法です。書いてあるとおりだとconfigparmalinkを設定しない場合の出力が得られますが、設定している場合は使えません。したがって、急遽無理やりShellScriptで書き直します。これはGruntfile.jsを直せばいいのですが、正直、面倒くさかった...。

#!/bin/zsh

d=${0:a:h:h}
# backup
h=${0:a:h}
if cat $j|jq . ;then
cp $j $h
fi
h=$h/PagesIndex.json
j=$d/static/bower_components/lunr.js/PagesIndex.json
t=`cat $j |jq '.[]'|sed -e 's#/post##g'`
a=`cat $j | sed -e 's#/post##g'`
echo $a >! $j
t=`echo $t|jq -r '.href'|cut -d - -f 1-4`

for (( i=1;i<=`echo "$t"|wc -l`;i++ ))
do
f=`echo "$t"|awk "NR==$i"`
c=`echo $f|tr '-' '/'`
sed -i "" "s#${f}#${c}#g" $j
done

cat $j

diff $j $h

とりあえず記事にあるGrntfile.jsを適時修正し作成し、ビルド後にスクリプトを実行するという手順。

出力ファイルは/bower_components/lunr.js/PagesIndex.jsonに置くこととします。


layouts/partials/widgets/search.html

<div class="widget-wrap">

<input id="search" type="text" placeholder="search">
<ul id="results">
</ul>
<script type="text/javascript" src="/js/search.js"></script>
<script type="text/javascript" src="/bower_components/lunr.js/lunr.min.js"></script>
</div>


js/search.html

var lunrIndex,

$results,
pagesIndex;

// Initialize lunrjs using our generated index file
function initLunr() {
// First retrieve the index file
$.getJSON("/bower_components/lunr.js/PagesIndex.json")
.done(function(index) {
pagesIndex = index;
console.log("index:", pagesIndex);

// Set up lunrjs by declaring the fields we use
// Also provide their boost level for the ranking
lunrIndex = lunr(function() {
this.field("title", {
boost: 10
});
this.field("tags", {
boost: 5
});
this.field("content");

// ref is the result item identifier (I chose the page URL)
this.ref("href");
});

// Feed lunr with each file and let lunr actually index them
pagesIndex.forEach(function(page) {
lunrIndex.add(page);
});
})
.fail(function(jqxhr, textStatus, error) {
var err = textStatus + ", " + error;
console.error("Error getting Hugo index flie:", err);
});
}

// Nothing crazy here, just hook up a listener on the input field
function initUI() {
$results = $("#results");
$("#search").keyup(function() {
$results.empty();

// Only trigger a search when 2 chars. at least have been provided
var query = $(this).val();
if (query.length < 2) {
return;
}

var results = search(query);

renderResults(results);
});
}

/**
* Trigger a search in lunr and transform the result
*
* @param {String} query
* @return {Array} results
*/
function search(query) {
// Find the item in our index corresponding to the lunr one to have more info
// Lunr result:
// {ref: "/section/page1", score: 0.2725657778206127}
// Our result:
// {title:"Page1", href:"/section/page1", ...}
return lunrIndex.search(query).map(function(result) {
return pagesIndex.filter(function(page) {
return page.href === result.ref;
})[0];
});
}

/**
* Display the 10 first results
*
* @param {Array} results to display
*/
function renderResults(results) {
if (!results.length) {
return;
}

// Only show the ten first results
results.slice(0, 10).forEach(function(result) {
var $result = $("<li>");
$result.append($("<a>", {
href: result.href,
text: "» " + result.title
}));
$results.append($result);
});
}

// Let's get started
initLunr();

$(document).ready(function() {
initUI();
});


これで検索システムの完成です。Hugo Buildで同時にPagesIndexを作成するようにしておけばOKです。