GitHub Pages & Jekyllで社内用ドキュメントページを構築した話

この記事はコインチェック株式会社（以下、コインチェック）のアドベントカレンダー5日目（シリーズ1）の記事です。  

https://qiita.com/advent-calendar/2024/coincheck

---

コインチェックでは、ビジネス用のドキュメントに関しては基本的にConfluenceを用いて管理しています。
表現の幅も広く、各種のツールとの連携機能や権限管理機能も豊富なため、とても便利に利用させていただいています。

一方で、エンジニアが日頃の業務の中で最も頻度高く触れているのはドキュメンテーションプラットフォーム内のページではなく、リポジトリ内に置いた各種のmd（マークダウン）ファイルではないでしょうか。
弊社では、主にエンジニアが運用する開発関連の資料については、メインで運用しているRailsのリポジトリ内に配置したdocsディレクトリで管理しています。

一定の知識があるエンジニアであればmdファイルの作成や更新については問題ないのですが、個人のローカル環境を整備していないとリンク先の参照がしづらかったりシンタックスハイライトが効かなかったりする等の閲覧の問題がありました。

こうした課題に対して、GitHub PagesとJekyllを用いた社内用ドキュメントページを構築して解決を図りました。

本記事では構築の概要やTIPSについてご説明したいと思います。

# 要件

社内用ドキュメントページを構築するにあたり、下記のような要件を満たす構築を目指しました。

- 社内向けに限定公開する
- WEBページとしてブラウザからアクセスする
- GitHub ActionsもしくはCircleCIで自動的にデプロイする（トリガーはスケジュール実行＆手動）

裏テーマは下記です。

- なるべく楽に構築する
- なるべく費用をかけずに構築する
- できるだけおしゃれな見栄え

# ツール選定

## 1. ホスティング

まずは何に対してどのようにホスティングするかを検討しました。
社内向けに公開する以上、なんらかの認証を挟むかIP等でアクセスを制御する必要があります。
今回はなるべく楽に構築したかったので、正直このあたりはできるだけサボりたかったのですが、調査してみるとGitHub Pagesであれば設定を行うだけでプライベート公開ができるとのことでした。

https://docs.github.com/ja/enterprise-cloud@latest/pages/getting-started-with-github-pages/changing-the-visibility-of-your-github-pages-site

プライベート公開の範囲はそのリポジトリへの参照権限を持つGitHubユーザのみとのことで、エンジニア向けのドキュメントのドキュメントページ化であれば十分であると判断し、ホスティングには**GitHub Pages＆GitHub Actions**を用いることにしました。

なお、GitHub Pagesには帯域幅制限、コンテンツ量制限、デプロイ頻度＆時間制限があるため、新たに導入を検討されている方はまずはご確認いただくと良いと思います。

https://docs.github.com/ja/pages/getting-started-with-github-pages/about-github-pages

## 2. ビルド

次はmdファイルのビルドツールを選定する必要があるのですが、GitHub PagesではデフォルトでJekyllをサポートしており公式でも推しているため、素直に**Jekyll**を用いることにしました。
Jekyllはインストールに際してもgemを使うため、Railsのプロジェクトとしては管理しやすい点もメリットでした。

https://docs.github.com/ja/pages/setting-up-a-github-pages-site-with-jekyll/about-github-pages-and-jekyll

https://jekyllrb-ja.github.io/

## 3. テンプレート

プレーンなJekyllも良いのですが、Jekyll界隈ではテンプレートの開発も活発で、いくつかのテンプレートギャラリーサイトが存在します。

https://jekyllthemes.io/jekyll-documentation-themes


https://jamstackthemes.dev/ssg/jekyll/

せっかくなので、今回は**Just the Docs**というテンプレートを利用することにしました。

https://jekyllthemes.io/theme/just-the-docs

下記の点が好みです。
- メニュー領域が広く、カテゴリが増えても安心
- 活発に開発されている
- おしゃれな見た目
- Mermaid図も表示可能
- 無限に子ページを作成可能（NEW）

# ビルド

## 基本的な設定

まず、リポジトリ内の構造はこの様になっています。
docsディレクトリの配下にmdファイルが格納されており、それらを生成したドキュメントページで表示することを目標とします。

```sh:tree
.
├── app
├── bin
├── ~~ いろいろ ~~
├── docs
│   ├── sample1.md
│   ├── sample2.md
```

ドキュメントページの生成はCIで行うのですが、ローカル環境でもJekyllでビルドできるように環境を構築しておくと、実際にどのような見栄えになるのかをリモートリポジトリへプッシュする前に確認ができるため、おすすめです。

リポジトリ内にGemfileを置いてgemの管理にBundlerを利用している場合は、`Gemfile`にまずはJekyllとJust the Docsを追加して`bundle install`をすると良いでしょう。
参考：https://jekyllrb-ja.github.io/tutorials/using-jekyll-with-bundler/

```rb:Gemfile
group :development do
  # ~~ いろいろ ~~
  gem "jekyll"
  gem "just-the-docs", "0.10.0
end
```

次に、docsディレクトリ内にJekyll用設定ファイルの`_config.yml`ファイルを作成します。

```yml:docs/_config.yml
title: Hoge docs
description: hogeリポジトリdocs配下のホスティング
theme: just-the-docs
baseurl: docs

# 全てのmdファイルにdefaultのレイアウト設定を追加
defaults:
  - scope:
      path: "**/*.md"
    values:
      layout: "default"
```

試しに下記のようなmdファイルを用意してみます。

```md:docs/sample1.md
---
title: Sample
nav_order: 1
---

# Hello document

ほげほげ
```

jekyllのserveコマンド打つとビルドが走り、`http://127.0.0.1:4000/docs/`以下にアクセスすると生成したページを見ることができます。

```sh
bundle exec jekyll serve -s docs --baseurl /docs
```

![スクリーンショット 2024-11-19 23.35.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401312/e43829e1-37de-b087-db48-7f336a959d3c.png)

buildコマンドでhtmlファイルとしてのビルドのみを行うこともできます。
下記例では、後々のビルドで平仄を合わせるために`output/docs`というディレクトリに格納するオプションを追加しています。

```sh
bundle exec jekyll build -s docs -d output/docs
```

基本的な構造のビルドができるようになったので、これをベースにやりたいことをどんどん詰め込んでいきます。

## 相対リンク

mdファイルにはリンクを記載することができます。
例えば下記のように記載することで同じ階層の`sample2.md`へのリンクを表し、GitHubのコード画面や賢いエディタであれば適切に遷移することが可能です。

```md:docs/sample2.md
---
title: Sample1
nav_order: 1
---

# Hello document 1

ほげほげ

[リンク](./sample2.md)
```

しかし、ビルドすると記載していたリンクの拡張子はmdのままにも関わらず、ページはhtmlファイルとして生成されるため、リンクが不通となってしまいます。

Jekyllはテンプレート用言語のLiquidをサポートしているため、下記のような記述に変更すればビルド後にhtmlファイルへのリンクに変換されて正常に遷移できるのですが、逆に元のmdファイルのリンクが不通となります。

```md
[リンク]({% link ./sample2.md %})
```

そのような場合に便利なのが[jekyll-relative-links](https://rubygems.org/gems/jekyll-relative-links)というプラグインになります。
これを導入することで自動でmdファイルへのリンクがhtmlファイルへのリンクに変換されます。

```rb:Gemfile
group :development do
  ~~ 既存の設定値  ~~
  gem 'jekyll-relative-links'
end
```

```yaml:docs/_config.yml
# ~~ 既存の設定値 ~~
plugins:
  - jekyll-relative-links
```

## favicon設定

ドキュメントページはできるだけ簡単にアクセスしてもらいたいため、いつでも見られるようにお気に入りやブックマークへの登録を推奨したいと考えていました。
しかし、デフォルトのままだとページにはfaviconが設定されていないため、ブックマークを作成しても一見して判別しにくいです。
よって、faviconを設定することにしました。

`_config.yml`にfaviconへのパスを記載し、適当な画像を作成して該当のパスに配置するとJust the Docs側で自動的に反映してくれます。
今回は`docs/assets/images/favicon.ico`にファイルを配置しました。
参考：https://just-the-docs.com/docs/configuration/#site-favicon

```yml:docs/_config.yml
# ~~ 既存の設定値 ~~
favicon_ico: /assets/images/favicon.ico
```

ビルドし直すとちゃんと反映されました。

![スクリーンショット 2024-11-20 11.15.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401312/fbdf48bb-d77e-556b-09c5-3a67bfd9d79c.png)

## CSS適用

Just the Docsのデザインは利用しやすく基本的には問題無いのですが、触っているうちに細かな点を調整したくなって来ました。
Just the Docsでは既存のデザインに対しての調整手段も用意されているため、これに従ってscssファイルを配置することで見た目を自分好みに調整することができます。

例えば、メインコンテンツの幅をもう少し広くしたいとします。
コンテンツ幅はJust the Docsのデザイン用の定義値として用意されているため、これを書き換える`docs/_sass/custom/setup.scss`を配置することで調整が可能です。
参考：https://just-the-docs.com/docs/customization/#override-and-define-new-variables

```scss:docs/_sass/custom/setup.scss
$content-width: 100rem;
```

また、デザイン用の定義値が無い場合はdocs/_sass/custom/custom.scssに直接記載することも可能です。
参考：https://just-the-docs.com/docs/customization/#override-and-completely-custom-styles

## 完成形

もろもろ設定を調整したりファイルを追加した結果が下記になります。
ダミー記述多めにはなりますが、参考にしていただければと思います。

```sh:tree
docs
├── _config.yml
├── _sass
│   └── custom
│       ├── custom.scss
│       └── setup.scss
├── assets
│   └── images
│       └── favicon.ico
├── index.md
├── sample1.md
├── sample2.md
```

```rb:Gemfile
group :development do
  # ~~ いろいろ ~~
  gem "jekyll"
  gem "just-the-docs", "0.10.0"
  gem 'jekyll-relative-links'
end
```

```yml:docs/_config:yml
title: sample docs
description: hogeリポジトリdocs配下のホスティング
theme: just-the-docs
plugins:
  - jekyll-relative-links
baseurl: docs

# For meta data
repository: hoge_organization/hoge_repository

# For favicon
favicon_ico: /assets/images/favicon.ico

# Set front matter default layout
defaults:
  - scope:
      path: "**/*.md"
    values:
      layout: "default"

# For navigation
nav_external_links:
  - title: additional page
    url: ../hoge

# For header
aux_links:
  "hoge_repository on GitHub":
    - "https://github.com/hoge_organization/hoge_repository"

# For footer
back_to_top: true
back_to_top_text: "Back to top"
last_edit_timestamp: true # show or hide edit time - page must have `last_modified_date` defined in the frontmatter
last_edit_time_format: "%Y-%m-%d %H:%M:%S" # uses ruby's time format: https://ruby-doc.org/stdlib-2.7.0/libdoc/time/rdoc/Time.html
gh_edit_link: true # show or hide edit this page link
gh_edit_link_text: "Edit this page on GitHub."
gh_edit_repository: "https://github.com/hoge_organization/hoge_repository" # the github URL for your repo
gh_edit_branch: "master" # the branch that your docs is served from
gh_edit_source: docs # the source that your files originate from
gh_edit_view_mode: "tree" # "tree" or "edit" if you want the user to jump into the editor immediately

# By default, consuming the theme as a gem leaves mermaid disabled; it is opt-in
mermaid:
  # Version of mermaid library
  # Pick an available version from https://cdn.jsdelivr.net/npm/mermaid/
  version: "10.9.1"
```

# デプロイ

## 基本的な手順

mdファイルのhtmlビルドまでできたので、これをGitHub Pagesにデプロイを行います。

まず、リポジトリのGitHub Pages設定を活性化します。
リポジトリ画面から`Setting > Pages`と遷移すると`Build and deployment`という設定項目があるため、`source`に`GitHub Actions`を選択します。
さらに、`Visibility`という設定項目には`private`を設定します。
これで、GitHub Actionsを使ってGitHub Pagesをprivateに公開することができるようになります。

次にdeploy用のGitHub Actionsの構築です。
リポジトリ内に`.github/workflows`ディレクトリを作成し、その内にワークフロー定義ファイルを用意します。
今回は`deploy_github_pages.yml`という名前で作成してみました。
作成したファイルは後述します。

参考：https://docs.github.com/ja/pages/getting-started-with-github-pages/using-custom-workflows-with-github-pages

## カスタムドメインの設定

organizationで管理されているGitHub Pagesは、デフォルトではURLが下記のように設定されます。
`http(s)://<organization>.github.io/<repository>`

ページ閲覧をする上では特に問題無いのですが、今後ホスティング先が変わった場合に`github.io`ドメインが使えなくなることでリンク切れになってしまう恐れがあります。

そこで、今回はAmazon Route 53で用意したドメインをGitHub Pagesのカスタムドメインとして設定を行いました。

方法としては下記リンク先に詳しく記載があるので、参考にしてみてください。
もしかしたら別記事を書くかもしれないので、気になる方はフォローしておいていただけると助かります。

参考：https://docs.github.com/ja/pages/configuring-a-custom-domain-for-your-github-pages-site/about-custom-domains-and-github-pages
参考：https://docs.github.com/ja/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site

## 完成形

デプロイに際してワークフローファイルを追加したので、リポジトリとしては下記のような構造になります。
他にもワークフローを定義していれば、`deploy_github_pages.yml`と同階層に並びます。

```sh:tree
.
├── ~~ いろいろ ~~
├── docs
├── .github
│   └── workflows
│       └── deploy_github_pages.yml
```

作成したワークフロー定義が下記になります。

```yml:.github/workflows/deploy_github_pages.yml
name: deploy github pages

on:
  schedule:
    - cron: "0 0 * * 1-5" # 月-金09:00(JST)
  workflow_dispatch:

permissions:
  contents: read
  pages: write
  id-token: write

concurrency:
  group: "pages"
  cancel-in-progress: false

env:
  BUNDLE_GITHUB__COM: hogehogehoge # 認証用情報を設定
  NODE_VERSION: 20

jobs:
  build:
    runs-on: ubuntu-latest
    timeout-minutes: 20

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      # RUBY_VERSIONを.ruby-versionから取得して更新
      - name: Set Ruby Version
        run: echo "RUBY_VERSION=$(cat .ruby-version)" >> $GITHUB_ENV

      # Rubyのセットアップ（jekyll実行用）
      - name: Setup Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: ${{ env.RUBY_VERSION }}

      # Rubyの依存関係をキャッシュ
      - name: Cache Ruby gems
        uses: actions/cache@v4
        with:
          path: vendor/bundle
          key: ${{ runner.os }}-ruby${{ env.RUBY_VERSION }}-gem-${{ hashFiles('**/Gemfile.lock') }}
          restore-keys: |
            ${{ runner.os }}-ruby${{ env.RUBY_VERSION }}-gem-

      # Rubyの依存関係のインストール
      - name: Install Ruby dependencies
        run: |
          # see: Gemfile
          bundle config github.https true
          bundle config path vendor/bundle
          bundle install --jobs 4 --retry 3

      # 生成物格納用ディレクトリを用意
      - name: Create output directory
        run: mkdir output

      # カスタムドメインを記載したCNAMEファイルを作成
      - name: Create CNAME file
        run: echo 'hogehogehogehoge.com' > output/CNAME

      # rootにアクセスした場合にdocsディレクトリへリダイレクトするindexファイルを作成
      - name: Create index.html
        run: echo '<meta http-equiv="refresh" content="0; URL=docs/">' > output/index.html

      # jekyllでdocs配下のビルド
      - name: Build with Jekyll
        run: bundle exec jekyll build -s docs -d output/docs

      # 他にGitHub Pages向けにビルドしたいものがあればここで生成してoutputディレクトリに格納する

      # GitHub Pagesの設定を実施
      - name: Setup Pages
        id: pages
        uses: actions/configure-pages@v5

      # 生成物をまとめてartifactへアップロード
      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: output

  deploy:
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    needs: build
    timeout-minutes: 5

    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4
```

ドキュメントの更新は平日の朝9時にスケジュール実行し、必要があればGitHub Actionsの画面からも実行できるように`workflow_dispatch`を追記しています。

また、カスタムドメインを設定するためにCNAMEファイルを設置する必要があるため、ワークフローの中でファイルの生成も行っています。

それ以外の内容は特に目新しいものが
無いかもしれませんが、ビルド時間を短縮するためにキャッシュを行ったり、Jekyll以外のビルド成果物もまとめられるようにoutputディレクトリを用意したり、細かな調整を盛り込んでみました。

ワークフロー定義mainブランチに取り込み、正常にGitHub Actionsが起動すると、GitHub Pagesにビルドしたドキュメントページがデプロイされるようになります。

![スクリーンショット 2024-11-20 11.16.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401312/155fb93d-2d56-3a9d-e8bd-123604167dd1.png)

# その他の開発：Slack通知のプレビューが見づらくなる

構築したドキュメントページのURLを社内用Slackに投稿したところ、プレビューがこのような表示になってしまいました。（黒塗り部分がURLです）

![スクリーンショット 2024-11-20 0.40.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401312/17050607-c7ba-12ba-6da9-4dbaab7ef243.png)

GitHubについてのOGPが表示されていて、正直とても邪魔に感じました。
プレビューを削除するボタンを押すことで非表示にできますが、URLをシェアする度にワンクリックしなければならないのも地味に面倒です。

調べてみたところ、Slackのプレビュー機能はunfurling linksというものの挙動に依存するようです。
参考：https://api.slack.com/reference/messaging/link-unfurling#classic_unfurl

要するに、ドキュメントページはprivateに公開をしているため、SlackはシェアされたURL先のメタ情報を取得できず、認証突破失敗ページのGitHubサイト自体のメタ情報を元にした情報を表示しているようです。

可能であれば認証を突破してページ自体のメタ情報を元にしたプレビュー画像を表示したいのですが、残念ながらGitHub PagesにはGitHubアカウントの参照権限以外にアクセスを許可する方法がなく、Slackからのfetch時にレスポンスを返すことができません。
そのため、少しでも見やすくするためにプレビューを非表示することにしました。

`link_shared`のイベントをsubscribeして、特定のURLのシェア時にはunfurlを削除してレスポンスを返すようなSlackアプリを構築し、ワークスペースにインストールしています。
こちらについても、もしかしたら別記事を書くかもしれないので、気になる方はフォローしておいていただけると助かります。

# 効果

社内用ドキュメントページを作った結果、観測している範囲で下記のような効果が現れてきたように感じています。

## 社内コミュニケーションで利用されるようになった

Slackで「◯◯の仕様ってどうなってるんだっけ？」という問いかけにで返答する時、これまでは情報ソースとしてGitHubの該当ファイルや該当行のパーマネントリンクを併記していました。

社内用ドキュメントページができてからは、該当ページのURLがシェアに用いられるようになってきたように感じます。

おそらく、社内用ドキュメントページとして認識されたことでアクセスしやすくしやすくなった、項目ごとにアンカーリンクが生成されていてシェアしやすくなった、という主に二点の理由によるものだと考えています。

## 自然とドキュメントが集約されてきた

ドキュメントページ構築当初は、ほぼコード規約や開発時の注意事項等のドキュメントだったのですが、定時実行タスクの仕様やシーケンス情報がまとまったドキュメントが増えてきました。

また、手動更新のドキュメントだけでなく、**自動生成ドキュメント**もGitHub Pagesにホスティングすることで、社内用ドキュメントページをホームディレクトリとして、各種ページにアクセスすることができるように活用され始めました。

現状では下記の情報が自動生成されています。

- コードオーナー資料（コードごとの管理グループのマッピング資料）
- openapiビューワー
- rubocopレポート

# 実は

GitHub Pagesでのドキュメントページの構築についてもろもろ記載させていただきましたが、実はこのドキュメントページは記事投稿の5ヶ月ほど前に完成していました。

5ヶ月間運用したところ、「GitHubアカウントを持たない（エンジニア以外の）ビジネス職社員もドキュメントを閲覧したい」という要望が上がってくるようになりました。
エンジニアの中でドキュメントページが活発に利用されたことによって、ビジネスサイドとのコミュニケーションにも活用したいと思えてもらえたのかな、と考えています。

なるべく簡単にドキュメントページを構築することを考慮してGitHub Pagesを選択しましたが、より円滑なコミュニケーションを実現するために、現在GitHub Pagesではない別の環境へのホスティングを検討しています。

# 終わりに

ホスティング先の変更という宿題が残ったとはいえ、色々試せてとても楽しい経験でした。
引っかかりやすいポイントや解消する方法など、社内にも共有でき、学びが多かったなと感じています。

ここまで読んでいただきありがとうございました。
この記事が少しでも皆様の参考になることを祈っています。