読者です 読者をやめる 読者になる 読者になる

スクリプトコンソールのススメ

この記事は、Jenkins Advent Calendar 2日目の記事です。

http://jenkins-ci.org/sites/default/files/jenkins_logo.png


Jenkinsには、外部から操作するための口としてスクリプトコンソールCLIRemote Access APIなどが用意されています。ここでは、僕が一番使い慣れている(けど多分あまりメジャーでない)スクリプトコンソールについて紹介します。

スクリプトコンソールとは

ブラウザ上からGroovyコードを記述して、そのコードをサーバ上で実行することができます。トラブルシューティングやジョブの一括編集などに力を発揮します。スクリプトコンソールを使うには、[Jenkinsの管理 > スクリプトコンソール]から、もしくは http://localhost:8080/script を直接入力してみてください。

f:id:ikikko:20111202233315p:image

僕は今の現場では100超のジョブをお守りしているのですが、一つ一つGUI上からポチポチ編集していくと拉致があかないので、大体はこれを使って一括編集しています。

サンプルコード

以下、簡単なサンプルコードを交えて、どんなことができるかを説明していきます。興味を持たれた方は、お使いのJenkins上でコードをコピペして試してみてください。なお、スクリプトコンソール上では"Ctrl+Enter"(Mac上では"Cmd+Enter")でサブミットできるようなショートカットが用意されています。

ジョブ数のカウント

以下のコード1行で、Jenkins上のジョブ数が即座に分かります。下半分は結果です。

jenkins.model.Jenkins.instance.items.size()

--

Result: 6

画面上の例では"println(hudson.model.Hudson.instance.pluginManager.plugins)"のようにクラス名がHudsonですが、Jenkinsでも構いません。

GroovyではJavaの記法が大体通るので、こんな感じでJavaで書くこともできます。Javaコードを見てのとおり、Groovyでは"getHoge()"を"hoge"でアクセスできるんですね。

jenkins.model.Jenkins.getInstance().getItems().size()
ビルドキューにたまっているジョブをクリア

全てがイヤになって 優先して実行したいジョブがあるけど、キューにジョブがいっぱいたまっててビルドが回ってくるまでに時間がかかるような場合に使えるコマンド。なお、このコマンドではすでに実行しているジョブはキャンセルされません*1

jenkins.model.Jenkins.instance.queue.clear()

これを推し進めて、以下のコードをブックマークレットとして保存しておくと、ブラウザ上から1クリックでビルドキューをクリアできるようになります。コード中の”localhost:8080”は適宜変更してください。

javascript:(function(u,o){var w=window,d=document,f=d.createElement('form'),e,i;(location=='about:blank'?w:open()||w).document.body.appendChild(f);f.action=u;f.method='POST';for(i in o){e=d.createElement('input');e.name=i;e.value=o[i];e.type='hidden';f.appendChild(e);}f.submit();})('http://localhost:8080/script',{'script':'jenkins.model.Jenkins.instance.queue.clear()'});

まあ、CLI使ってもほぼ同じことができるのですが*2、僕があまりCLIに慣れていないのと、ブックマークレット化しておくとマウスだけでできるので楽っちゃ楽です。

ジョブの情報を参照・編集

ジョブの情報を1行ずつ表示するには、下記のコード1行+αでいけます。

jenkins.model.Jenkins.instance.items.each { println it }

null

--

hudson.model.FreeStyleProject@7e1eb84f[helloArtifactory]
hudson.model.FreeStyleProject@12d68b39[helloArtifactoryGradle]
hudson.model.FreeStyleProject@434e54d8[helloArtifactoryIvy]
hudson.maven.MavenModuleSet@4ba4536d[helloArtifactoryMavenRelase]
hudson.model.FreeStyleProject@11613fe7[helloNcss]
hudson.model.FreeStyleProject@7aa5f9b[helloArtifactExtractor]

スクリプトコンソール上では、最後に評価された式を結果として出力します。今回はeachのクロージャの中でprintlnして出力しているので、最後に無駄に評価されて出力しないように、nullを追加しています。

情報を参照するだけでなく、ジョブを変更することもできます。例えば、ビルドエラー時の通知メールで、全ジョブに特定の宛先を追加したくなった場合。この例では、"foo@example.com"をメール通知の設定がされているジョブ全部に追加します。

jenkins.model.Jenkins.instance.items.findAll {
  it.publishersList.get(hudson.tasks.Mailer.class) != null
}.each { 
  println it
  
  def mailer = it.publishersList.get(hudson.tasks.Mailer.class)
  mailer?.recipients += ' foo@example.com'
}

null

--

hudson.model.FreeStyleProject@7e1eb84f[helloArtifactory]
hudson.model.FreeStyleProject@12d68b39[helloArtifactoryGradle]

実際のコーディングの進め方

僕も上記のコードを最初から間違いなく記述できるわけではありません。スクリプトコンソールのいいところは、実際に動いているものを確認しながら少しずつコードを作っていけるところです。こんなときは、groovyのdump()メソッドを使ってどんなプロパティがあるのか確かめつつ、JenkinsのJavadocも参考に進みます。

実際に、上記の「通知メールに宛先を追加したい場合」を例にあげてみましょう。まずは、"items"でジョブ一覧が取れるけど、実際にジョブの中身がどうなってるかを確認します。

jenkins.model.Jenkins.instance.items[0].dump()

--

Result: <hudson.model.FreeStyleProject@7e1eb84f builders=hudson.util.DescribableList@1759e38d publishers=hudson.util.DescribableList@e6a49f5 buildWrappers=hudson.util.DescribableList@641d7b37 scm=hudson.scm.SubversionSCM@4b848b3a pollingBaseline=null builds=[10:helloArtifactory #10, 9:helloArtifactory #9, 8:helloArtifactory #8, 7:helloArtifactory #7, 6:helloArtifactory #6, 5:helloArtifactory #5, 4:helloArtifactory #4, 3:helloArtifactory #3, 2:helloArtifactory #2, 1:helloArtifactory #1] quietPeriod=null scmCheckoutRetryCount=null assignedNode=null canRoam=true disabled=false blockBuildWhenDownstreamBuilding=false blockBuildWhenUpstreamBuilding=false jdk=null authToken=null triggers=[] transientActions=[org.jenkinsci.plugins.all_changes.AllChangesAction@a01330f, org.jfrog.hudson.action.ArtifactoryProjectAction@54bf22ea] concurrentBuild=false customWorkspace=null lastBuildStartTime=0 nextBuildNumber=11 holdOffBuildUntilSave=false logRotator=null cachedBuildHealthReportsBuildNumber=null cachedBuildHealthReports=null keepDependencies=false properties=hudson.util.CopyOnWriteList@36c8c63e name=helloArtifactory description= parent=hudson.model.Hudson@4d12ee4f actions=[]>

実行結果を見て、ビルド実行後のアクションを表すのがpublishersではないかと当たりをつけます。で、FreeStyleProjectのjavadocを見て、publishersを扱っているメソッドがいくつか有りますが、試行錯誤の末getPublishersList()が望みのものだと分かりました。

jenkins.model.Jenkins.instance.items[0].publishersList.each {println it}

null

--

hudson.tasks.junit.JUnitResultArchiver@336215d4
hudson.tasks.JavadocArchiver@362e3cb1
hudson.tasks.ArtifactArchiver@4e5db277
hudson.tasks.Mailer@2a6cd712

上記の結果を見ると、"hudson.tasks.Mailer"というクラスがメール関係っぽいですね。publishersListの型であるDescribableのJavadocを見ると、get()の引数でクラスを与えると、そのクラスが取得できるとあります。

jenkins.model.Jenkins.instance.items[0].publishersList.get(hudson.tasks.Mailer.class).dump()

--

Result: <hudson.tasks.Mailer@48c12420 recipients=hoge@example.com dontNotifyEveryUnstableBuild=false sendToIndividuals=false>

ここまで来ると、recipientsがメール通知の宛先を表しているというのは何となく分かりますね。あとは、サンプルコードにあるように、recipientsを編集してやればいいということになります。

編集時の注意点

メール通知の宛先の場合はrecipientsプロパティを直接編集できましたが、場合によってはfinalが付けられていて後から編集することができないこともあります。そのような場合には、インスタンスを自分で生成して入れ替えるようにしてください。

jenkins.model.Jenkins.instance.items.findAll {
  it.publishersList.get(hudson.tasks.Mailer.class) != null
}.each { 
  println it

  def origMailer = it.publishersList.get(hudson.tasks.Mailer.class)

  def mailer = new hudson.tasks.Mailer()
  mailer.recipients = origMailer.recipients + ' foo@example.com'
  mailer.dontNotifyEveryUnstableBuild = origMailer.dontNotifyEveryUnstableBuild
  mailer.sendToIndividuals = origMailer.sendToIndividuals

  it.publishersList.replace(mailer)
}

null


それでは、みなさんよいJenkinsライフを!

*1:すでに実行しているジョブをキャンセルすると、ビルド結果が灰色のAbortedになって履歴に残りますしね

*2:CLIからでもスクリプトコンソールからでも、ソースの同じところが呼び出される