GitLab CI/CD 実践編 ~GitLab Pagesを使ったドキュメントのホスティング~

こんばんは。インサイトテクノロジー札幌R&Dセンターの笹谷です。
好きなボトラーはモリソン&マッカイとエリクサーディスティラーズ、好きなカスクタイプはEXバーボンカスクとPXカスクです。
業務ではGitlabを用いたCI/CDの構築、開発部門の業務プロセスの改善タスクを担当しております。

前回は、Gitlabが持つCI/CDの機能であるGitLab CI/CDについて、基本的な使い方と最小限の例についてお話しました。
今回はその続きで、実践編と称し、GitLab Pagesと組み合わせることで、

  1. テストのcoverageをHTMLを公開
  2. markdown文章をHTMLに変換して公開

する流れについてそれぞれお話します。

シーン1: テストのcoverageのHTMLを公開する

概要説明

ローカルのgit repositoryからGitlabのremote repositoryへのpushをトリガに、ドキュメントをホストするjobが動きます。
jobの内部では、

  • pytestを実施、code coverageをHTML出力
  • 出力されたHTMLをGitLab Pagesの仕組みで公開

の2つがそれぞれ実行されています。

ディレクトリ構成

GitLab CI/CDをお試しするために、最低限必要なレポジトリのディレクトリ構成は以下です。
main/以下のtestをするtest/以下があり、pytest実行時にpytest-covを使い、
pytestの実行結果からcoverageを算出し、HTMLで出力します。
job内でpublic/という名前でディレクトリを作成し、そちらに上記で出力されたcoverageの結果をコピーし配置します。
以下のツリーの点線以下はそれを示しています。

sample-repository/
 ├── .gitlab-ci.yml
 ├── main/
 │   ├── module.py
 │   └── main.py
 │
 ├── tests/
 │   ├── pytest/
 │   │   ├─── test_module.py
 │   │   └─── test_main.py
 │   ├── coverage/
 │   │   └─── coverage.html
 │   └── coverage_utils/
 │       └─── heatmap.js
 :    
 :
 └── public/
     └── coverage/
         └─── coverage.html

.gitlab-ci.yml

GitLab CI/CDの設定ファイルは以下のように記述します。

# .gitlab-ci.yml
stages:
    - test
    - deploy

unittest-master-with-coverage:
  stage: test
  script:
  - pip install pytest-cov==2.10.0
  - pytest -v ./tests/ --cov-report html:cov_html --cov-report term --cov=.
  # - ここで一部生成されたhtmlにheatmap.jsが使えるようsedで追記する行を入れる
  - mkdir coverage/
  - cp -R cov_html/ coverage/
  - mv coverage/cov_html coverage/coverage
  - cp tests/coverage_utils/heatmap.js coverage/coverage
  artifacts:
    paths:
      - coverage/
  rules:
    - if: '$CI_COMMIT_REF_NAME == "master"'
      when: on_success
    - when: never

pages:
  image: centos:7.7.1908
  stage: deploy
  dependencies:
    - unittest-master-with-coverage
  script:
    - mv coverage/ public/
  artifacts:
    paths:
      - public/
    # pages上のartifactsを期限付きで削除する場合コメントを外す
    # expire_in: 30 days
  rules:
    - if: '$CI_COMMIT_REF_NAME == "master"'
      when: on_success
    - when: never

GitLab Pagesについて:

上記jobの設定の説明に入る前に、job内で利用しているGitLab Pagesについてお話します。
かいつまんで言うと、”GitLab CI/CDの枠組みから利用できる、静的サイト公開機能”です。
静的サイトを気軽に公開する仕組みとしては、GitHub Pagesや、Netlifyなどがありますね。
Netlifyは、それ自身がリモートレポジトリを有していないため、GitHubと連携した上で、変更が加えられると都度更新する点で、GitLab Pagesとは(GitHub Pagesとも)大きく異なります。

また、GitHub Pagesは、リモートレポジトリを有しているのは同様ですが、GitHub Pages上で、特定のディレクトリ以下を対象にGitHub Pagesで公開する、というような、設定変更だけで静的ホストを公開できるポイントです。

対して、GitLab Pagesは、設定変更だけでリモートレポジトリから静的サイトの公開ができるわけではありませんが.gitlab-ci.ymlに正しい設定を加え、リモートレポジトリへのpushやWeb UI上での操作をトリガにCIを実施することで、静的なHTMLサイトが公開できます。

もちろん、GitHub Pagesでも、GitHub Actionsと組み合わせることで、GitLab Pagesと同様のことが可能です。 (参考:GitHub Actions for GitHub Pages

GitHub Pagesは、既存のレポジトリのHTMLから静的サイトを公開できる手軽さと、GitHubActionsと連携することによりCI/CDも可能になるフレキシブルな点が長所です。
一方、GitLab Pagesは、デフォルト使用の時点で、公開する静的サイトについての設定がコード化され、リモート/ローカルレポジトリ自身で管理できる点が長所と言えるでしょう。

説明

それでは設定内容の説明に戻ります。

stages:では、testに関するjobとdeployに関するjobの2種類を使う設定をし、実行順序をtest -> deployの形で定義しています。
(.yml上の記述に関する詳細な説明は、前回をご参照ください。)

unittest-master-with-coverage:は、masterbranch へのmergeをトリガに、coverage付きのtestが実施され、結果が入ったcoverage/ディレクトリをartifactsにuploadします。

pages:は、先述したGitLab Pagesの設定の具体例にあたります。 unittest-master-with-coverage:jobで出力され、pagesのartifactとしてディレクトリに出力されたcoverage.htmlを、publicディレクトリ以下に配置し、jobを実行することで、coverage.htmlが公開されます。

ここで注意が必要なのは、

  • GitLab Pagesに静的サイトを公開するためのjob名はpagesでなければならない
  • GitLab Pagesに静的サイトを公開する対象ディレクトリのrootはpublicでなければならない

以上の2点です。
ちなみに、artifactsにuploadするだけであれば、job名もroot dir名も任意のものが設定可能ですが、artifacts単体では、個別のファイルをブラウザ上で確認できるだけで、ページ遷移などはできないようです。

成果物

こうして、GitLab Pagesを利用することで、testのcoverageは以下のようなイメージでチームメンバーに公開されます。


ちなみに、pytest-cov単体ではgcovのように色分けされないので、元のcoverage.htmlにjsをちょっと噛ませています。

備考

なお、GitLab Pagesを利用した静的サイトの公開範囲は、Setting -> General -> Visibilityから、Pagesの項目において、プルダウンにて設定できます。デフォルトはレポジトリにアクセス可能なメンバー限定になっています。

シーン2: markdown文章をHTMLに変換して公開

概要説明

ローカルのgit repositoryから、Gitlabのremote repositoryへのpushをトリガに、ドキュメントをホストするjobが動きます。
GitLab CI/CDでは、特定のディレクトリ(ここでは)docs以下への変更を含むpushを対象にjobが動くよう、ymlファイルで設定します。
jobの内部では、

  • 指定ディレクトリ以下に差分があった場合、*.mdに対し、mkdocsを実行し、Markdown文章をHTML変換する
  • 出力されたHTMLをGitLab Pagesの仕組みで公開

の2つがそれぞれ実行されています。

ディレクトリ構成

GitLab CI/CDをお試しするために、最低限必要なレポジトリのディレクトリ構成は以下です。
変換対象は、docs/markdown/以下で、後ほどご紹介する.gitlab-ci.ymlにて、docsディレクトリ以下に変更があった場合のみ、mkdocsによるHTML変換が実施されたものを、シーン1と同様、publicディレクトリ以下に配置し、GitLab Pagesの仕組みで静的サイトを公開するイメージとなります。

sample-repository/
├── .gitlab-ci.yml
├── docs/
│   ├── mkdocs.yml
│   └── markdown/
│       ├── README.md
│       ├── requirements.md
│       └── specification.md
│    
:
:
└── public/
    └── markdown/
        ├──README.HTML
        ├── requirements.HTML
        └── specification.HTML

.gitlab-ci.yml

GitLab CI/CDの設定ファイルは以下のように記述します。

# .gitlab-ci.yml

# 実行するjobをカテゴライズ
stages:
    - test
    - deploy

before_script:
  - pip install -r docs/requirements.txt
  - pip install mkdocs-material

# test jobでuploadされたartifactsをbrowseすることで.mdから生成されたhtmlを確認する
test:
  image: python:3.8-buster
  stage: test
  script:
    - cd docs/
    - mkdocs build --strict --verbose --site-dir test
    - cd ../
    - cp -r docs/test ./test
    - cp -r docs/data ./test/data
    - cd test && ls -al
  # artifact/testディレクトリ
  artifacts:
    paths:
    - test
  rules:
    - if: $CI_MERGE_REQUEST_ID
      when: never
    - if: '$CI_MERGE_REQUEST_EVENT_TYPE == "detached"'
      when: never
    - changes:
      - "docs/*"
      - "docs/markdown/*"
      - "docs/markdown/**/*"
      if:$CI_COMMIT_REF_NAME == "master"'
      when: never
    - changes:
      - "docs/markdown/*"
      - "docs/markdown/**/*"
      - "docs/*"
      when: on_success


# Pagesへのdeployのjob定義
pages:
  image: python:3.8-buster
  stage: deploy
  script:
    # mkdocsの設定ファイルはひとまずdashboard prjのものを一括して利用する形
    # 指標の反映もAPIcallのタイミングと合わせる設定
    - cd docs/
    - mkdocs build --strict --verbose --site-dir public
    - cd ../
    - mkdir public/
    - cp -r docs/public ./public/documents/
    - cp -r docs/data ./public/data
    - cd public && ls -al
  artifacts:
    paths:
    - public/
  rules:
    - if: $CI_MERGE_REQUEST_ID
      when: never
    - if: '$CI_MERGE_REQUEST_EVENT_TYPE == "detached"'
      when: never
    - changes:
      - "docs/*"
      - "docs/markdown/*"
      - "docs/markdown/**/*"
      if: '$CI_COMMIT_REF_NAME == "master"'
      when: on_success

説明

stages:では、シーン1と同様に、testに関するjobとdeployに関するjobの2種類を使う設定をしています。

testsおよびpages:mkdocs build --strict --verbose --site-dir ****の部分で、mkdocsを使い、指定したディレクトリにmarkdownからHTMLを生成しています。これ以降に関しては、artifactsにuploadする、あるいはGitLab PagesでHTMLを公開するための処理です。

最後にrules:節ですが、この節のchanges:で、「docsディレクトリ以下に変更があった場合」、「masterへのpush時以外のタイミングに」(test job)または「masterへのpush時に」(pages job)jobが動くように制限しています。

成果物

mkdocsがmarkdownからHTMLに変換してくれたものは、以下の画像のようにGitLab Pagesで公開されます。


備考

 rules:
    - if: $CI_MERGE_REQUEST_ID
      when: never
    - if: '$CI_MERGE_REQUEST_EVENT_TYPE == "detached"'
      when: never

上記のrules:直下の2つのif:節は、特定条件下でjobが二重に動く問題に暫定的に対処するための記述です。

終わりに

今回は実践編として、GitLab Pagesの仕組みを使って、テスト結果のcoverageと、markdownをベースにしたHTMLを公開する流れをご紹介しました。
次回はGitlabRunnerの使い方をメインに投稿いたします。

GitLab CI/CD シリーズ

関連最新記事

TOP インサイトブログ 開発 GitLab CI/CD 実践編 ~GitLab Pagesを使ったドキュメントのホスティング~

Recruit 採用情報

Contact お問い合わせ

  購入済みの製品サポートはこちら