はじめに

GitlabとJenkinsを連携させて、MergeRequest(以下、MR)のアクセプト前に、 マージのチェックと単体テストの実行を自動でできるようにしたのでまとめがてら残しておく。

環境

  • Gitlab 9.x (ubuntu上で稼働)
  • Jenkins 2.x (Windows上で稼働)

どちらもオンプレ。

やったこと

  • MRをOpenしたタイミングで自動で以下の処理を実行する
    • feature branchをmain branchに正常にマージできるかチェックする
    • マージ後のソースコードで maven test を行う
  • 実行の結果をMRの画面で確認できるようにする
  • 自動でだけでなく手動でも実行できるようにする
  • ジョブの設定はJenkinsfile(Decreative pipeline)で管理
  • ビルド対象のGitリポジトリとJenkinsfileのGitリポジトリは別々の箇所で管理
    • ビルド対象のブランチにJenkinsfileを置くやり方ではない

設定方法

  • Jenkinsへの設定
    • ジョブを動かすための事前準備
    • ジョブ自体の準備
      • Jenkinsfileの準備
      • ジョブの設定
  • Gitlabへの設定
    • Gitlabへの操作をトリガーにJenkinsのジョブをキックするよう設定

Jenkinsへの設定

ジョブを動かすための事前準備

Global Configuration Tool
  • JDK
    • Oracle JDKを利用する場合、Oracleアカウントが必要。
    • 指定した名前を後で利用する
  • Maven
    • 指定した名前を後で利用する
Plugin

認証情報

Gitlabへのアクセス
  • Gitlabへアクセスするための認証情報の設定
    • ジョブの中で利用
Jenkinsへのアクセス
  • Jenkinsジョブを実行するための認証情報の設定

  • リモートからジョブをキックできるようにユーザーのAPI tokenを発行する

ジョブ自体の準備

  • Jenkinsfileの準備
    • Jenkinsジョブの内容自体はJenkinsfileで管理
      • 設定をコードとして管理できる
  • ジョブの設定
    • ジョブの設定内容としてJenkinsfileを参照するように設定

Jenkinsfileの準備

実際に利用しているのとほぼ同じJenkinsfileは以下の通り。 Jenkinsfile

Jenkinsfileの書き方等は以下を参照。 https://jenkins.io/doc/book/pipeline/jenkinsfile/ https://jenkins.io/doc/book/pipeline/syntax/

agent
agent { label 'master' }
1

今回はすべて master で行ったので、最初の master を指定。
stage毎にagentを変えることも可能。

tools
tools {
  maven 'Maven3'
  jdk 'jdk8'
}
1
2
3
4

ジョブの中で利用するツールを指定。
今回は、 Global Tool Configuration で設定したものを利用した。設定の名前と一致するように記述する。

environment
environment {
  CREDENTIAL_ID = 'xxxxxxxxxx'
  MAVEN_OPTS = '-Xmx512m -Xms512m -XX:PermSize=256m'
  GIT_URL = 'git@gitlab.local'
}
1
2
3
4
5

ジョブの中で利用する変数の指定。
これはジョブの中で利用する定数を最初に宣言しているだけ。

  • CREDENTIAL_ID

    • Jenkins内で設定したGitlabへの認証情報のIDを指定
      • Jenkinsで認証情報の設定を行うと、自動で割り振られる
  • MAVEN_OPTS

    • maven コマンドの実行時に与える引数。
  • GIT_URL

    • gitlabのURL
options
options {
  gitLabConnection('gitlabserver')
  gitlabBuilds(builds: ['Merge', 'UnitTest'])
}
1
2
3
4
  • gitLabConnection
    • Jenkinsに予め設定しているGitlabの接続情報の名前を指定。
  • gitlabBuilds
    • Gitlab側にJenkinsのジョブの状況を表示できる。
    • 後述の updateGitlabCommitStatus と併用することでGitlabのMR画面にステータスをいい感じに表示できる
triggers
triggers {
  gitlab(
    triggerOnPush: false,
    triggerOnMergeRequest: true,
    triggerOpenMergeRequestOnPush: 'source',
    triggerOnClosedMergeRequest: false,
    addCiMessage: true,
    branchFilterType: 'RegexBasedFilter',
    targetBranchRegex: "mainbranch1|mainbranch2"
  )
}
1
2
3
4
5
6
7
8
9
10
11

何をトリガーにしてJenkinsジョブを実行するかを指定する。
  今回は、gitlabの操作のみをトリガーとするので、gitlab と書かれたセクションしか存在しない。
gitlabのwebhookのイベントの設定とトリガーに指定する内容がずれていると意図したとおりにジョブがキックされないので注意。
(例えば、Merge requestのOpenをトリガーにしたいのに、webhookのイベントで、Merge request eventにチェックを入れていないとか)

基本は、公式ドキュメント読めばいいが、情報が薄いので、ソースも見つつ読み解くのがいい気がする。
https://github.com/jenkinsci/gitlab-plugin#job-trigger-configuration

MRによって自動で動かす

triggers {
    gitlab(
      …
      triggerOnMergeRequest: true,
      triggerOpenMergeRequestOnPush: 'source',
      branchFilterType: 'RegexBasedFilter',
      targetBranchRegex: "mainbranch1|mainbranch2",
      …
    )
}
1
2
3
4
5
6
7
8
9
10
  • Merge requestをOpenしたタイミングで自動で動かす
  • MRがOpenしている状態でfeature branchが新たにpushされたら自動でジョブを実行
post
post {
    success {
        junit 'target/surefire-reports/*.xml'
        updateGitlabCommitStatus name: 'Merge', state: 'success'
        updateGitlabCommitStatus name: 'UnitTest', state: 'success'
        cleanWs()
    }
    failure {
        junit 'target/surefire-reports/*.xml'
        updateGitlabCommitStatus name: 'Merge', state: 'failed'
        updateGitlabCommitStatus name: 'UnitTest', state: 'failed'
        cleanWs()
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

ジョブの成功時や失敗時に追加で何を行うか指定できる。

  • junit
    • jUnitの実行結果をサマリーするために利用
  • updateGitlabCommitStatus
    • これ使うことで、Gitlab上に表示したステータスの状態を更新できる。
  • cleanWs()
    • Workspaceの中身を消してくれる。
    • Fetchしてきたソースなどがworkspaceに残ってしまうとゴミになってしまうことがあるので、workspaceを消すようにしている。
stages
stages {
  stage('Merge') {
    steps {
        script {
          …    
        }
    }
    checkout([
      …
    ])
  }
  stage('UnitTest') {
    steps {
      sh 'mvn -B clean test -Dunit-test=true -Dfile.encoding=UTF8'
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

stegeを2つ用意して処理を行っている。

  • Merge
    • 詳細は割愛しているが対象のブランチをマージする処理を行う
  • UnitTest
    • mavenコマンドを実行している

ジョブの設定

  • ジョブの新規作成から パイプライン を選択して、ジョブを作成する
  • PipelineのDefinitionで Pipeline script from SCM を選択する
  • Jenkinsfileを置いてあるGitの情報を入力する
    • Jenkinsファイルとビルド対象のGitは異なるGitでも構わない
    • やり方によっては、ビルド対象のブランチにJenkinsfileを置く必要がある。そっちのほうがメジャー?

Gitlabへの設定

  • Jenkinsジョブをキックするための設定

webhookの設定

  • トリガーとする操作を行うプロジェクトを開き、settings -> integrationsへ進む
  • キックするJenkinsジョブのリモート実行用のURLを入力する
  • トリガーに利用したいイベントにチェックを入れる
    • Merge requests event にチェックを入れることで、Merge request関連のイベントによってJenkins側にリクエストが飛ぶ

URLについて

JenkinsジョブのURLは、
http://{JenkinsのURL}/projects/{ジョブの名前}/
みたいな感じになっていると思う。
(Jenkinsの設定画面から確認可能)

ジョブが認証を必要とする場合、
http://{user}:{apiToken}@{JenkinsのURL}/projects/{ジョブの名前}/
のような感じ。

結果

設定がうまくいけば、MRをOpenしたタイミングで、ジョブが実行される。

工夫ポイント

手動にも自動にも対応

ジョブの内容的に自動での実行に加えて、手動で動かした場合もある。

手動で動かしたい場合

Jenkinsのジョブの画面から実行
parameters {
        string(name: 'targetRepoName', defaultValue: '', description: 'Which git project do you want to build ?')
        string(name: 'sourceBranch', defaultValue: '', description: 'Which branch do you want build ?')
        string(name: 'targetBranch', defaultValue: '', description: 'Which branch do you want build ?')
}
stages {
  stage('Merge') {
      steps {
          script {
                if("${params.targetRepoName}" == "") {
                    env.targetRepoName = "${gitlabTargetRepoName}"
                    env.sourceBranch = "${gitlabSourceBranch}"
                    env.targetBranch = "${gitlabTargetBranch}"
                } else {
                    env.targetRepoName = "${params.targetRepoName}"
                    env.sourceBranch = "${params.sourceBranch}"
                    env.targetBranch = "${params.targetBranch}"
                }
          }
        …
      }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  • ジョブを画面から実行するときにパラメータを指定できるようにしておく
    • Jenkinsの画面からJenkinsジョブを自分で実行可能
MRから再度実行
triggers {
    gitlab(
      …
      triggerOnNoteRequest: true,
      noteRegex: "rebuild",
      …
    )
}
1
2
3
4
5
6
7
8
  • 自動での実行が何かしらの問題で失敗した場合、再度手動で行いたい
    • MRのDescussionエリアで特定のコメント rebuild をつけることで、ジョブをキックできる
  • Gitlabに表示されているジョブのステータスを更新できる