こんにちは。テクマトリックス株式会社、新人の奥村です。

今回タイトルの通り、Subversionのhooksを使ってJenkinsを動かす方法を試しましたので、方法を共有します。
(以前、GitとJenkinsを連携したので、Subversionでも同じようなことができるのか?という試みです。)

今回のゴール

「Subversionのリポジトリに変更されたソースコードがコミットされたこと」をトリガとして、Jenkinsのジョブを実行する

なぜhooksを使うのか

今回、hooks(フックスクリプト)を使った理由について。

Subversionのコミットをトリガとする方法はいくつかあります。

トリガ概要
hooksコミットなどの特定のアクションによってJenkinsのジョブを実行する
pollingコードの変更が無いか確認し、変更があればJenkinsのジョブを実行する
[23/2/3 追記]
指定した時間ごとにコードの変更を確認する(Jenkinsでは最高頻度で1分毎に設定できる)
Pipeline: SCM Step | Jenkins plugin

今回のゴールの要件である「Subversionのコミットをトリガとして」はhooksで実現されるため、こちらを採用しました。

具体的には、Subversion hooksの一つ「post-commit」にJenkinsのジョブを実行するコマンドを仕込みます。

コミットに関係する hooks

hooks 概要
start-commit コミットトランザクション作成前に実行される
pre-commit トランザクションが作成され、コミットの前に実行される
post-commit トランザクションがコミットされ、新しいリビジョンが 作られた後に実行される(コミットの後)

詳しくは以下のSubversionドキュメントを参照してください。

フックスクリプト

リポジトリの作成と設定 | Subversion によるバージョン管理 For Subversion 1.2 (jtdan.com)

動作環境

OS Windows10
Java openjdk 11.0.17(実施時のJava11最新バージョン)
Jenkins 2.375.1
Jenkins : Subversion Plugin 2.16.0(実施時の最新バージョン)

前提条件

Subvesion

  1. リポジトリ、クライアントが作成されていること (※補足:Subversionの構築

Jenkins

  1. Subversion Pluginが導入されていること
  2. Jenkinsのジョブが作成されていること(フリースタイルプロジェクト、パイプライン可)
    • SubversionのリポジトリURLの設定
    • Credentials(Subversionのサーバーにログインするためのユーザー名とパスワード)の設定
  3. Jenkinsユーザーとそれに紐づいたAPIトークン(30文字程度ある半角英数字で構成された文字列)が発行されていること
  4. 上記のユーザーに少なくとも以下の権限があること
    • 全体/Read
    • ジョブ/Build
    • ジョブ/Read
  5. (「Jenkinsのジョブに対してJenkinsCLIのコマンドを送る」の時のみ)jenkins-cli.jarのダウンロード、配置してあること

SubversionのリポジトリURLについて

Webサーバーの場合 「http://」で始まるURLを指定する (例)http://localhost/repo
ローカルの場合 「file:///」で始まるURLを指定する (例)file:///C:/svn/repo

JenkinsユーザにAPIトークンを発行する

  1. Jenkinsダッシュボード画面の右上部にある「(ユーザー)」をクリックする
  2. ユーザー画面の左部の「設定」をクリックする
  3. 「APIトークン」の「トークン新規追加」をクリックする
  4. 「Default name」の欄に適当なトークン名を入力し、「生成」をクリックする
  5. 発行されたAPIトークンを分かるように保存しておく

手順

Jenkinsのジョブに対してHTTPリクエストを送る方法

①「Jenkins : Subversion Plugin」公式ページのやり方 [23/2/3 追記]

Windows specific post-commit hook

Jenkins : Subversion Plugin

  1. Jenkinsジョブの設定を変更する
    • 設定 > ビルドトリガ > 「SCMをポーリング」にチェックを入れる


  1. post-commit-hook-jenkins.vbsを作成する
    • Subversionリポジトリ直下にpost-commit-hook-jenkins.vbsを作成する
    • 以下のスクリプトをコピー&ペーストする(内容はそのまま)
repos = WScript.Arguments.Item(0)
rev = WScript.Arguments.Item(1)
svnlook = WScript.Arguments.Item(2)
jenkins = WScript.Arguments.Item(3)
authorization = WScript.Arguments.Item(4)
Set shell = WScript.CreateObject("WScript.Shell")
Set uuidExec = shell.Exec(svnlook & " uuid " & repos)
Do Until uuidExec.StdOut.AtEndOfStream
 uuid = uuidExec.StdOut.ReadLine()
Loop
Wscript.Echo "uuid=" & uuid
Set changedExec = shell.Exec(svnlook & " changed --revision " & rev & " " & repos)
Do Until changedExec.StdOut.AtEndOfStream
 changed = changed + changedExec.StdOut.ReadLine() + Chr(10)
Loop
Wscript.Echo "changed=" & changed
url = jenkins + "crumbIssuer/api/xml?xpath=concat(//crumbRequestField,"":"",//crumb)"
Set http = CreateObject("Microsoft.XMLHTTP")
http.open "GET", url, False
http.setRequestHeader "Content-Type", "text/plain;charset=UTF-8"
if not authorization = "" then
 http.setRequestHeader "Authorization", "Basic " + authorization
end if
http.send
crumb = null
if http.status = 200 then
 crumb = split(http.responseText,":")
end if
url = jenkins + "subversion/" + uuid + "/notifyCommit?rev=" + rev
Wscript.Echo url
Set http = CreateObject("Microsoft.XMLHTTP")
http.open "POST", url, False
http.setRequestHeader "Content-Type", "text/plain;charset=UTF-8"
if not authorization = "" then
 http.setRequestHeader "Authorization", "Basic " + authorization
end if
if not isnull(crumb) then 
 http.setRequestHeader crumb(0),crumb(1)
end if
http.send changed
if http.status <> 200 then
 Wscript.Echo "Error. HTTP Status: " & http.status & ". Body: " & http.responseText
end if


  1. post-commit.batを作成する
    • (Subversionリポジトリ)\hooks にpost-commit.batを作成する
    • 以下のスクリプトをコピー&ペーストし、
      • SET CSCRIPT
      • SET VBSCRIPT
      • SET SVNLOOK
      • SET JENKINS (※注意点1
      • SET AUTHORIZATION (※注意点2
        に任意の情報を記入する
SET REPOS=%1
SET REV=%2
SET CSCRIPT=
SET VBSCRIPT=
SET SVNLOOK=
SET JENKINS=
REM AUTHORIZATION: Set to "" for anonymous acceess
REM AUTHORIZATION: Set to encoded Base64 string, generated from "user_id:api_token" 
REM found on Jenkins under "user/configure/API token"
REM User needs "Job/Read" permission on Jenkins
SET AUTHORIZATION=""
"%CSCRIPT%" "%VBSCRIPT%" "%REPOS%" %2 "%SVNLOOK%" %JENKINS% %AUTHORIZATION%

(例)

SET REPOS=%1
SET REV=%2
SET CSCRIPT=C:\Windows\System32\cscript.exe
SET VBSCRIPT=C:\Repos\post-commit-hook-jenkins.vbs
SET SVNLOOK=C:\Subversion\svnlook.exe
SET JENKINS=http://localhost:8080/job/TestJob/build/
REM AUTHORIZATION: Set to "" for anonymous acceess
REM AUTHORIZATION: Set to encoded Base64 string, generated from "user_id:api_token" 
REM found on Jenkins under "user/configure/API token"
REM User needs "Job/Read" permission on Jenkins
SET AUTHORIZATION="○○○○○○○○○○○○○○○○○○○○"
"%CSCRIPT%" "%VBSCRIPT%" "%REPOS%" %2 "%SVNLOOK%" %JENKINS% %AUTHORIZATION%


  1. 対応するSubversionリポジトリにコミットする

post-commit.bat の注意点 [23/2/3 追記]

※注意点1 SET JENKINS

  • ジョブを指定したURLを設定する
    Jenkins URLのみを指定することでも実行自体は可能です。
    しかし、実行できるジョブが複数存在した場合すべてが実行されてしまうため、ジョブを指定してしまった方が無難です。
    指定方法に関しては以下のドキュメントを参照してください。

Jobs without parameters

Remote Access API (jenkins.io)

※注意点2 SET AUTHORIZATION

  • (Jenkinsユーザー):(APIトークン) をBase64でエンコーディングした文字列を設定する
    スクリプト内のコメントの通りですが、個人的には落とし穴だと思っています。
    「base64 エンコード ツール」などで検索し、エンコードしてください。

    (Jenkinsユーザー):(APIトークン) をそのまま記入した場合は、Jenkinsのシステムログで以下のようなエラーが出ます。

Corrupted data
java.lang.IllegalArgumentException: Illegal base64 character 3a
…



② hooksでJenkinsのジョブを実行するcurlコマンドを叩く

  1. (Subversionリポジトリ)\hooks にpost-commit.batを作成する
  2. 以下のコマンドを記述する

curl -X POST -u (Jenkinsユーザー):(APIトークン) (Jenkins URL)/job/(ジョブ)/build

(例)

curl -X POST -u user1:○○○○○○○... http://localhost:8080/job/TestJob/build

  1. 対応するSubversionリポジトリにコミットする

curlのオプションについて

オプション 概要
-X POST 送信するデータが無いPOSTメソッドのリクエスト
-u アカウント名とパスワードを指定してリクエスト

Jenkinsのジョブに対してJenkinsCLIのコマンドを送る方法

  1. (Subversionリポジトリ)\hooks にpost-commit.batを作成する
  2. 以下のコマンドを記述する

java -jar (jenkins-cli.jarのフルパス) -s (Jenkins URL) -auth (Jenkinsユーザー):(APIトークン) -webSocket build (ジョブ) -c

(例)

java -jar C:\JenkinsCLI\jenkins-cli.jar -s http://localhost:8080/ -auth user1:○○○○○○○... -webSocket build TestJob -c

  1. 対応するSubversionリポジトリにコミットする

JenkinsCLIオプションについて

※以下のコマンドでビルドのオプションを確認できます

java -jar (jenkins-cli.jarのフルパス) -s (Jenkins URL) -auth (Jenkinsユーザー):(APIトークン) help build
オプション 概要
-webSocket WebSocket接続モードになり、リバースプロキシやプロキシ構成の問題を回避してくれる
-c リポジトリに差分がある時のみ実行する

感想

今回はSubversionのhooksを使ってJenkinsを動かす方法をまとめました。
調査する前は「Subversionとは何か」も分からない状態でしたので、まずその実態を把握することから行いました。分かり辛かった部分含め次の章で構築方法をまとめているので、ぜひお役立てください。

また、 Subversion Plugin公式ページのやり方ではできないと判断し、別の方法を試そうとなるまでに時間がかかってしまいました。公式ページに掲載されている方法でも環境によっては実行出来ない、何かの判断基準で方針転換する必要があるということを学びました。

今回紹介している3つの方法の差別化点は以下です。適宜、使い分けていただけると良いと思います。

[23/2/3 追記]

  1. 「Jenkins : Subversion Plugin」公式ページのやり方
    • 公式ページで紹介されている方法である
  2.  Jenkinsのジョブを実行するcurlコマンドを叩く
    • JenkinsCLIのダウンロード、配置が不要である
    • コマンドが短い
  3. Jenkinsのジョブに対してJenkinsCLIのコマンドを送る方法
    • Jenkinsがサポートするコマンドラインインターフェースである
    • ビルドのオプションを使用できる
      (例えば「-c」を使うことで、ビルド開始前にSubversionの変更を確認し、変更がない場合の無駄なビルドを避けることができる)

補足:Subversionの構築

ゴールイメージ

リポジトリの構築

以下の構築方法のどちらかで作成してください。

  1. Webサーバーに作成する方法

WindowsにSubversion 1.12サーバーを構築する

https://blogs.osdn.jp/2019/06/14/subversion.html

  1. ローカルに作成する方法
    (試験的に実施する場合はこちらがおすすめです)

チェックアウト先(クライアント)の構築

  1. 任意のディレクトリを作成する (例)C:\WorkingCopy
  2. このディレクトリを右クリック → SVN チェックアウト…(ローカルにリポジトリのコピーを作成)
  3. チェックアウトの設定をする
    • リポジトリのURL を入力
      (※SubversionのリポジトリURLについて
    • チェックアウト先のディレクトリ のパスが指定したディレクトリのものであることを確認する
    • ok をクリックする

リポジトリへのコミット

  1. チェックアウト先に trunkディレクトリ を作成(例)C:\WorkingCopy\trunk
  2. trunkの直下にText.txtを作成する(例)C:\WorkingCopy\trunk\Text.txt
  3. Text.txt内に「test」と記述 → 保存
  4. チェックアウト先ディレクトリの空白部分を右クリック → SVN コミット…
  5. メッセージに「最初のコミット」と入力
  6. 変更した項目の「trunk/Text.txt」にチェックを入れる → OK をクリック
  7. コミット終了画面にて、「完了 リビジョン***となりました」と表示される
  8. チェックアウト先ディレクトリの空白部分を右クリック → リポジトリブラウザー でコミットが反映されていることを確認する

By okumura