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


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

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

- GitLab CIが使える

今までCIはリポジトリ管理とは別のサービスで使っていたひとが多いと思います。GitHub+[Wercker](https://wercker.com)やGitHub+[Travis](https://travis-ci.org/)など。

しかし、GitLabは例えば、[GitLab Pages](http://doc.gitlab.com/ee/pages/README.html)をリポジトリ(master)で作り、[GitLab CI](http://doc.gitlab.com/ce/ci/quick_start/README.html)を使って自動デプロイすると、即座にページを公開できます。今までCIサービスに紐付けして、リポジトリのビルドをしていたので、その辺は随分簡単になっていると思います。

[https://gitlab.com/groups/pages](https://gitlab.com/groups/pages)

例えば、[hugo](https://gitlab.com/pages/hugo)でページを作りたいなら、以下の手順で公開できます。

```bash:
$ 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`という形になります。

このような関係から[こちら](https://gitlab.com/groups/pages)にあるサイトジェネレーターを使ってページを作成する場合、使用するアドレスによってはソースの変更が必要になります。

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

![](https://raw.githubusercontent.com/mba-hack/images/master/octopress_gitlab_pages_user.png)

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

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

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


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

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

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

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

```html: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.js](http://gruntjs.com/)と[Lunr.js](http://lunrjs.com/)でHugo用に構築します。

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

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

```bash:
#!/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`に置くこととします。

```html: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>
```

```html: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です。