GitLab CI/CDを使ってマージリクエスト作成時に自動でテストジョブを実行する環境を構築する

2023/04/07に公開

自身の勉強と所属している会社内への布教を兼ねて、GitLabのCI/CD環境を構築をして導入ドキュメントも兼ねてまとめることにしました。公式ドキュメントを参考にしましたが我流なので「CI/CDについて何も知らない」&「何でも許せる人」向けとしてよろしくお願いします。(CI/CDと言ってますが今回はテスト自動化の構築のみになります。)

この記事でやっていくこと

  • GitLab CI/CD、GitLab Runnerを既存のプロジェクトに導入します。
  • マージリクエスト作成時に自動でテストジョブを実行する仕組みを作ります。
  • 最終的にテストCIが成功していないとマージを出来ないようにします。

    上の図はマージリクエスト画面で、テストCIが失敗してマージができないようになっています

前提

  • Ubuntu(WSL2)で構築してます。
     - (Debian系Linux以外の方は適宜コマンドを変えて進めてください)
  • バックエンド言語はPHPを使用してます。
     - (他の言語でも本手順は有効です。適宜コマンドを変えて進めてください)
  • 事前にDockerがインストールされている事。
     - Dockerインストール公式ドキュメント

目次

  • GitLab Runner をインストール
  • ランナーの作成
  • ランナーをGitLabに登録する
  • .gitlab-ci.yml、テスト用ファイルを追加する
  • マージリクエスト作成時、CI成功しないとMerge出来ないようにする

GitLab Runner をインストール

まずGitLab Runnerとは→ 「GitLab Runner は、GitLab CI/CD と連携してパイプラインでジョブを実行するアプリケーションです。」(公式ドキュメント抜粋)

ざっくりと説明すると、ランナーとは様々なジョブを実行を制御する環境、ジョブとはテストなど予め定義されたどのような条件で実行されるべきかを示す処理です。
 
まずは、公式ドキュメント を参考にGitLab Runnerをインストールしていきます。

### 公式の GitLab リポジトリを追加します。
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash
### GitLab Runner の最新バージョンをインストールする
sudo apt-get install gitlab-runner

バージョン情報が表示されたらGitLab Runnerのインストール成功

yoneko@DESKTOP-JUAPL98:~/gitlab_ci$  gitlab-runner -v
Version:      15.9.1
Git revision: d540b510
Git branch:   15-9-stable
GO version:   go1.18.10
Built:        2023-02-20T21:03:05+0000
OS/Arch:      linux/amd64

ランナーの作成

次に、ランナーを作成していきます。ここで言うランナーは実際にテストツールを実行するマシンのことを指しています。今回は、WSL2上に新たにUbuntuコンテナを起動してその中にPGM言語やテストツールをインストールする方法でランナーを作っていきます。

ランナーとなるUbuntuコンテナの用意

### Ubuntuコンテナを名前を付けて起動します。(イメージがない場合はpullしてきます)
docker run -it -d --name gitlab_runner ubuntu:22.04
### コンテナが起動していたら成功
yoneko@DESKTOP-JUAPL98:~/gitlab-ci$ docker ps
CONTAINER ID   IMAGE          COMMAND       CREATED         STATUS         PORTS     NAMES
f5e674faa197   ubuntu:22.04   "/bin/bash"   7 seconds ago   Up 5 seconds             gitlab_runner
### コンテナにログインします。
yoneko@DESKTOP-JUAPL98:~/gitlab_ci$ docker exec -it gitlab_runner /bin/bash
root@f5e674faa197:/#

ここから先の手順はUbuntuコンテナ内の操作になります。

バックエンド言語、テストツールのインストール

UbuntuコンテナにPHP、PHPUnitをインストールしていきます。
※他の言語を使用したい方は適宜コマンドを変えて進めてください。

root@f5e674faa197:/$ cd
### Ubuntuコンテナはデフォルトで名前解決ができないのでnameserverをGoogleのDNS変更します。
root@f5e674faa197:~$ sh -c "echo 'nameserver 8.8.8.8' > /etc/resolv.conf"
### PHPUnitのインストールに必要なものをインストールします。
root@f5e674faa197:~$ apt update
root@f5e674faa197:~$ apt install curl php-cli php-mbstring git unzip php-xml vim

composer公式ドキュメントを参考にcomposerをインストールしていきます。

root@f5e674faa197:~$ php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
root@f5e674faa197:~$ php -r "if (hash_file('sha384', 'composer-setup.php') === '55ce33d7678c5a611085589f1f3ddf8b3c52d662cd01d4ba75c0ee0459970c2200a51f492d557530c71c15d8dba01eae') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
root@f5e674faa197:~$ php composer-setup.php
root@f5e674faa197:~$ php -r "unlink('composer-setup.php');"
### 任意のディレクトリから簡単に呼び出しためにcomposer.pharを移動する。
root@f5e674faa197:~$ mv composer.phar /usr/local/bin/composer

composer.jsonを生成します。対話型のコマンドですが基本はデフォルトで大丈夫です。

root@f5e674faa197:~$ composer ini

                                            
  Welcome to the Composer config generator  
                                            


This command will guide you through creating your composer.json config.

Package name (<vendor>/<name>) [root/root]: 
Description []: 
Author [n to skip]: n
Minimum Stability []: 
Package Type (e.g. library, project, metapackage, composer-plugin) []: 
License []: 

Define your dependencies.

Would you like to define your dependencies (require) interactively [yes]? 
Search for a package: 
Would you like to define your dev dependencies (require-dev) interactively [yes]? 
Search for a package: 
Add PSR-4 autoload mapping? Maps namespace "Root\Root" to the entered relative path. [src/, n to skip]: 

{
    "name": "root/root",
    "autoload": {
        "psr-4": {
            "Root\\Root\\": "src/"
        }
    },
    "require": {}
}

Do you confirm generation [yes]? yes
Generating autoload files
Generated autoload files
PSR-4 autoloading configured. Use "namespace Root\Root;" in src/
Include the Composer autoloader with: require 'vendor/autoload.php';
root@f5e674faa197:~# 

viコマンドでPHPのオートロードの設定を修正するため、
composer.json"Root\\Root\\": "src/""App\\": "./"に修正します。

composer.json
{
    "name": "root/root",
    "autoload": {
        "psr-4": {
            "App\\": "./"
        }
    },
    "require": {}
}

修正したらcomposer dump-autoloadで修正を反映させる。

root@f5e674faa197:~$ composer dump-autoload

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

root@f5e674faa197:~$ composer require --dev phpunit/phpunit ^8

PHPUnitがインストールされていたら成功。ひとまずコンテナ内の作業は一旦終了です。

root@f5e674faa197:~$ /root/vendor/bin/phpunit --v
PHPUnit 8.5.33 by Sebastian Bergmann and contributors.

option --v is ambiguous

次に、これまでに行ったコンテナ内の作業を保存します。
一度、コンテナからデタッチ(CTRL+P → CTRL+Qの順に押す)するか別ターミナルからdocker commitでUbuntu内の状況を保存します。

### 別ターミナルを起動してコンテナIDを確認しておく。
yoneko@DESKTOP-JUAPL98:~/gitlab_ci$  docker ps
CONTAINER ID   IMAGE          COMMAND       CREATED        STATUS        PORTS     NAMES
f5e674faa197   ubuntu:22.04   "/bin/bash"   8 hours ago   Up 8 hours             gitlab_runner
### PHPUnitをインストールしたUbuntuコンテナをphpunitとという名前で保存する。
yoneko@DESKTOP-JUAPL98:~/gitlab_ci$  sudo docker commit f5e674faa197 phpunit
sha256:49d865acd97e96cba349286a56def64c1cead306145f20ab8e296793063d3ff9
### phpunitというイメージが出来ていたら保存成功。
yoneko@DESKTOP-JUAPL98:~/gitlab_ci$  docker images
REPOSITORY             TAG       IMAGE ID       CREATED          SIZE
phpunit                latest    49d865acd97e   21 seconds ago   330MB

コンテナが保存出来たらランナーの作成はいったん終了です。

ランナーをGitLabに登録する

ここからは、GitLabにランナーを登録していきます。
登録するにはGitLabのプロジェクト内にある登録トークンが必要なので控えておきます。

1, GitLabのプロジェクト内の、[Settings] → [CI/CD] → [Runners]画面のRegister the runner with this URL:And this registration token:の下の文字列を控えてきます。
2, 併せてEnable shared runners for this projectのチェックを外しておきます。

公式ドキュメントを参考にして次のコマンドを実行します。
ランナーの登録はsudo gitlab-runner registerコマンドを使用します。対話型コマンドになっており必要な情報を入力していきます。

yoneko@DESKTOP-JUAPL98:~/gitlab_ci$ sudo gitlab-runner register
Runtime platform                                    arch=amd64 os=linux pid=13818 revision=d540b510 version=15.9.1
Running in system-mode.

Enter the GitLab instance URL (for example, https://gitlab.com/):
https://gitlab.com/
Enter the registration token:
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Enter a description for the runner:
[DESKTOP-JUAPL98]: ci sample
Enter tags for the runner (comma-separated):

Enter optional maintenance note for the runner:

WARNING: Support for registration tokens and runner parameters in the 'register' command has been deprecated in GitLab Runner 15.6 and will be replaced with support for authentication tokens. For more information, see https://gitlab.com/gitlab-org/gitlab/-/issues/380872
Registering runner... succeeded                     runner=GR1348941roJkAcrN
Enter an executor: docker-ssh+machine, instance, custom, docker, docker-ssh, virtualbox, kubernetes, parallels, shell, ssh, docker+machine:
docker
Enter the default Docker image (for example, ruby:2.7):
phpunit
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!

Configuration (with the authentication token) was saved in "/etc/gitlab-runner/config.toml"
対話型コマンドの入力説明

Enter the GitLab instance URL (for example, https://gitlab.com/):
GitLabのURL。控えていた「Register the runner with this URL:」の文字列。
Enter the registration token:
控えていた「And this registration token:」の文字列。
Enter a description for the runner:
ランナーの説明。任意入力のためスキップ可能。
Enter tags for the runner (comma-separated):
ランナーに付けるタグ名。任意入力のためスキップ可能。
Enter optional maintenance note for the runner:
メンテナンスノートを任意に指定します。任意入力のためスキップ可能。
Enter an executor: custom, docker-ssh, parallels, instance, docker, shell, ssh, virtualbox, docker+machine, docker-ssh+machine, kubernetes:
ランナーの種類。今回はDockerコンテナなのでDockerを指定する。
Enter the default Docker image (for example, ruby:2.7):
ランナーとして使用するイメージ。今回はdocker commitで指定したphpunitを指定します。

登録が終わったら、ランナーを起動します。

yoneko@DESKTOP-JUAPL98:~/gitlab_ci$  sudo gitlab-runner run

ランナーを起動後、GitLabのプロジェクト内の[Runners]画面に登録されたランナーが表示されました。

ランナー設定ファイルを編集する。

登録が成功すると、/etc/gitlab-runner/config.tomlに登録したランナーの設定値が書き込まれます。デフォルトの設定ではdocker commitで保存したイメージをpullしてこられないので、config.tomlrunners.dockernetwork_modepull_policyを追加します。

/etc/gitlab-runner/config.toml
yoneko@DESKTOP-JUAPL98:~/gitlab_ci$ sudo cat /etc/gitlab-runner/config.toml
concurrent = 1
check_interval = 0
shutdown_timeout = 0

[session_server]
  session_timeout = 1800

[[runners]]
  name = "ci sample"
  url = "https://gitlab.com/"
  id = 22484451
  token = "XXXXXXXXXXXXXXXXXX"
  token_obtained_at = 2023-04-02T18:24:19Z
  token_expires_at = 0001-01-01T00:00:00Z
  executor = "docker"
  [runners.cache]
    MaxUploadedArchiveSize = 0
  [runners.docker]
    tls_verify = false
    image = "phpunit"
    privileged = false
    disable_entrypoint_overwrite = false
    oom_kill_disable = false
    disable_cache = false
    volumes = ["/cache"]
    network_mode = "host"                 #この行を追加する
    pull_policy = "if-not-present"        #この行を追加する
    shm_size = 0

config.tomlを追加した後設定を反映させます。

yoneko@DESKTOP-JUAPL98:~/gitlab_ci$ sudo gitlab-runner restart
Runtime platform                                    arch=amd64 os=linux pid=19958 revision=d540b510 version=15.9.1

これでGitLab側のランナーの設定は終わりです。

.gitlab-ci.yml、テスト用ファイルを追加する。

ここからは、.gitlab-ci.ymlとテスト用のソースファイルを用意します。
.gitlab-ci.ymlとは、実行するスクリプトやアプリケーションをデプロイする場所などを示すパイプラインの定義ファイルです(公式ドキュメント
 今回は、テストツールを実行するだけなので以下のように作成します。

.gitlab-ci.yml
image: 
 name: phpunit
stages:
 - test
phpunit_job:
 stage: test
 script:
   - /root/vendor/bin/phpunit test

続いて、テスト用のソースファイルと、テストファイルを作って生きます。

src/Sample.php
<?php

namespace App\src;

class Sample 
{
   public function hello() 
   {
       return "Hello";
   }
}
test/SampleTest.php
<?php

use App\src\Sample;

class SampleTest extends PHPUnit\Framework\TestCase
{
   public function testHello()
   {
       $sample = new Sample();
       
       $result = $sample->hello();
       
       //Sample.phpのhello()の返り値が Hello ならテストOK
       $this->assertEquals("Hello", $result);
   }
}

今回のファイルの階層はこのようになります。

└── App
    ├── src
    │   └── Sample.php
    ├── test
    │   └── SampleTest.php
    ├── .gitlab-ci.yml
    └── README.md

ファイル追加後、手動でパイプラインを実行する。

ファイルを追加出来たら、CIが機能するかどうか確かめます。GitLabのプロジェクト内の、[CI/CD] → [Pipelines]画面内右上にあるRun pipelinesを押してCIが成功するか確認します。

CI成功!

マージリクエスト作成時、CI成功時でないとMerge出来ないようにする

より実用的な運用を想定して、マージリクエスト時にCIを実行して自動テストが成功していないとMerge出来ないようにしていきます。
 まずは、公式ドキュメントを参考に.gitlab-ci.ymlを編集します。マージリクエスト作成時のみパイプラインが実行されるようにします。

.gitlab-ci.yml
image: 
 name: phpunit
stages:
 - test
phpunit_job:
 stage: test
 script:
   - /root/vendor/bin/phpunit test
 only:                     #この行を追加する
   - merge_requests        #この行を追加する

次に、GitLab側のマージリクエスト設定で、マージするときはCI成功を必須にする設定を行います。
 GitLabのプロジェクト内の、[Settings] → [Merge requests]画面内で Pipelines must succeedにチェックを入れます。GitLab側の設定はこれで終わりです。

次に、ソースを修正して、マージリクエストを作成していきます。
 わざとCIが失敗するように、$this->assertEqualsの比較する文字列を変えます。そのあとリモートにpushします。

pushした後、GitLab側でマージリクエストを作成します。New merge request画面でCreate merge requestボタンを押します

CI失敗時

マージリクエスト作成後、パイプラインが実行されます。CIが失敗したら画像のようにマージボタンが非表示になります。

CI成功時

同じ手順で今度はCIが成功するように、ソースを修正してマージリクエストを作成していきます。CI成功時はマージボタンが表示されます。

以上になります。最後までご覧いただきありがとうございました。

Discussion