Top / Java / Jenkins / プラグイン開発

プロジェクトでJenkinsを使ってますが、勉強も兼ねてJenkinsのプラグインを作ってみようと思いました。

まずは Plugin tutorial - 日本語 - Jenkins Wiki に従って、Jenkinsのプラグイン開発を体験してみたいと思います。

やってみる

環境は Macに Mac OS X に maven3 (3.0.5) をインストールする手順 | OSCALOG をつかってMavenなどをインストール済みです。*1

事前の設定

# view ~/.m2/settings.xml

下記のように作成しておきます。

# cat ~/.m2/settings.xml 
<settings>
  <pluginGroups>
    <pluginGroup>org.jenkins-ci.tools</pluginGroup>
  </pluginGroups>

  <profiles>
    <!-- Give access to Jenkins plugins -->
    <profile>
      <id>jenkins</id>
      <activation>
        <activeByDefault>true</activeByDefault>
       <!-- change this to false, if you don't like to have it on per default -->
      </activation>
      <repositories>
        <repository>
          <id>repo.jenkins-ci.org</id>
          <url>http://repo.jenkins-ci.org/public/</url>
        </repository>
      </repositories>
      <pluginRepositories>
        <pluginRepository>
          <id>repo.jenkins-ci.org</id>
          <url>http://repo.jenkins-ci.org/public/</url>
        </pluginRepository>
      </pluginRepositories>
    </profile>
  </profiles>
</settings>

プロジェクトの作成

# mvn -cpu hpi:create

プロジェクト名などを聞かれる。

 groupId: nu.mine.kino.jenkins.plugins
 artifactId: project-management

こうなるように答えました。

# cd project-management/
# mvn -DdownloadSources=true -DdownloadJavadocs=true eclipse:eclipse

Eclipseで利用可能になりました。このDownloadのオプションですが、結構な時間がかかりますので、場合によってはオプションを指定しない方がよさそうです。。

# mvn package

/project-management/target/project-management.hpi というファイルができました。これがプラグインの実体になります。

ローカルのリポジトリにインストール

# mvn install

動かしてみる。

# export MAVEN_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8000,suspend=n"
# mvn hpi:run
Windowsでは
set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8000,suspend=n

ローカルでJenkinsが起動します。http://localhost:8080/jenkins/ からアクセス可能! Hello World的なプラグインがインストールされているのが分かります。

Jenkins_設定.png

プラグイン全体に関する設定画面 にHello World Builderというプラグインが。

フ&#12442;ロシ&#12441;ェクトの設定.png

各プラグインの画面にも設定が追加されている。

ヒ&#12441;ルト&#12441;のコンソール結果.png

ビルドを実行した結果でプラグインが動いているのが分かる。

プロジェクトの構成

プロジェクトの構成はこんな感じになっています。

project_setting.png

HelloWorldBuilder?

HelloWorldBuilder? は Builderを拡張しているクラスで、Jenkinsのプロジェクトの「設定 > ビルド」の「ビルド手順の追加」に表示されます。 それで追加するとビルド時にこのクラスのメソッドが呼び出されるようになっています。

実際呼び出されるメソッドは、

   @Override
   public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) {
       // This is where you 'build' the project.
       // Since this is a dummy, we just say 'hello world' and call that a build.

       // This also shows how you can consult the global configuration of the builder
       if (getDescriptor().getUseFrench())
           listener.getLogger().println("Bonjour, "+name+"!");
       else
           listener.getLogger().println("Hello, "+name+"!");
       return true;
   }

これです。

config.jelly

config.jelly は ビルド に追加したときに画面に表示されるUIを制御します。

<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
  <!--
    This jelly script is used for per-project configuration.

    See global.jelly for a general discussion about jelly script.
  -->

  <!--
    Creates a text field that shows the value of the "name" property.
    When submitted, it will be passed to the corresponding constructor parameter.
  -->
  <f:entry title="Name" field="name">
    <f:textbox />
  </f:entry>
</j:jelly>

ここでセットされた値は HelloWorldBuilder? のコンストラクタ

// Fields in config.jelly must match the parameter names in the "DataBoundConstructor"
@DataBoundConstructor
public HelloWorldBuilder(String name) {
   this.name = name;
}

に渡されるようになっています。

global.jelly

global.jelly は 「Jenkinsの管理 > システムの設定」に表示されるUIを制御します。config.jelly と同じようにxmlでUIを定義します。

<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
  <!--
    This Jelly script is used to produce the global configuration option.

    Jenkins uses a set of tag libraries to provide uniformity in forms.
    To determine where this tag is defined, first check the namespace URI,
    and then look under $JENKINS/views/. For example, <f:section> is defined
    in $JENKINS/views/lib/form/section.jelly.

    It's also often useful to just check other similar scripts to see what
    tags they use. Views are always organized according to its owner class,
    so it should be straightforward to find them.
  -->
  <f:section title="Hello World Builder">
    <f:entry title="French" field="useFrench"
      description="Check if we should say hello in French">
      <f:checkbox />
    </f:entry>
  </f:section>
</j:jelly>

ここでセットされた値は、自動生成された

@Extension // This indicates to Jenkins that this is an implementation of an extension point.
 public static final class DescriptorImpl extends BuildStepDescriptor<Builder> {
       @Override
       public boolean configure(StaplerRequest req, JSONObject formData) throws FormException {
           // To persist global configuration information,
           // set that to properties and call save().
           useFrench = formData.getBoolean("useFrench");
           // ^Can also use req.bindJSON(this, formData);
           //  (easier when there are many fields; need set* methods for this, like setUseFrench)
           save();
           return super.configure(req,formData);
       }
 }

ここからアクセス可能なようです。

いじってみる

システムの設定画面に、テキストボックスを追加してみる。

システムの設定に表示される設定画面に、新たにテキストボックスを追加してみます。追加するUIを制御しているのは global.jelly でしたので、そこを下記のように追加します。

<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
   <f:section title="Hello World Builder">
    <f:entry title="French" field="useFrench"
      description="Check if we should say hello in French">
      <f:checkbox />
    </f:entry>
    <!-- 追加 -->
    <f:entry title="Text Data" field="textData">
      <f:textbox />
    </f:entry>
    <!-- 追加 -->
 </f:section>
</j:jelly>

Jenkinsを再起動したところ、、、、、

text.png

追加されました!!!

さて、この設定画面に追加されたテキストボックスの値ですが、画面で保存ボタンをクリックしたときに

private String text;
public boolean configure(StaplerRequest req, JSONObject formData) throws FormException {
    text = formData.getString("textData");
    save();
    return super.configure(req,formData);
}

などとアクセス可能なのでした。また、設定画面を開いた際には、global.jelly ファイルの field="textData" より

public String getTextData(){
  return text;
}

がコールされ、textの情報が画面に反映されます。

まとめると

まとめると、画面( global.jelly )に「field="textData"」というデータを定義した際は、

formData.getString("textData");

で画面上のデータにアクセスし(してデータをsaveしておいて)、画面を開く際は

public String getTextData(){
  return text;
}

という名前でさっき保存したデータを返すようにしておく、というのが定石のようです。*2

そしてジョブからそのデータを使用する場合は、プログラム上から

@Override
public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) {
  listener.getLogger().print( getDescriptor().getTextData() + ":" + name + "!");
  return true;
}

などと指定すればOKですね。

ジョブの設定画面に、テキストボックスを追加してみる。

次は、各ジョブごとの設定画面に、テキストボックス(テキストエリア)を追加します。追加するUIを制御しているのは config.jelly でしたので、そこに下記のように追加します。

<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" 
  xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
  <f:entry title="Name" field="name">
    <f:textbox />
  </f:entry>
 <!-- 追加 --> 
  <f:entry title="List" field="dataList">
    <f:textarea />
  </f:entry>
 <!-- 追加 --> 
</j:jelly>

Jenkinsを再起動したところ、、、、、

configtext.png

追加されました!!!

ちなみにこの*.jellyに書くタグは Jelly Taglib references などにリファレンスがあったりします。

さてこの追加された画面の情報をどのようにプラグインが保存するかですが、画面の入力情報は、

@DataBoundConstructor
public HelloWorldBuilder(String name) {
   this.name = name;
}

このアノテーションされたコンストラクタに渡されるのでした。なのでこのコンストラクタを以下のように書き換えます。

@DataBoundConstructor
public HelloWorldBuilder(String name, String dataList) {
  this.name = name;
  this.dataList = dataList;
}

private final String name;
private final String dataList;

また global.jelly 同様、画面を開いた際に field="dataList" よりgetterがコールされます。すなわち以下のgetterを追加します。

public String getDataList() {
  return dataList;
}

以上で、ジョブの設定画面の情報はJenkinsによってストアされ、必要に応じて

@Override
public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) {
  listener.getLogger().print(dataList);
  return true;
}

とアクセス可能です。

関連リンク


この記事は

選択肢 投票
おもしろかった 2  
そうでもない 0  
  • HelloWorldBuilder?はどのようにJenkinsに「登録」されるのかよく分からん。どうも DescriptorImpl? をちゃんと定義しておく必要がある? -- きの? 2014-10-14 (火) 21:03:06
  • ヒープの設定もする場合。。 export MAVEN_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8000,suspend=n -Xmx1024m -XX:MaxPermSize?=128m" -- きの? 2014-10-18 (土) 09:11:55
  • みんな数値のフォーマッティングはどうやってんのかな、、、。誤差がびよーんと表示されて大変なのだが。。。 -- きの? 2015-02-06 (金) 17:59:43

Top / Java / Jenkins / プラグイン開発

現在のアクセス:5571


*1 もしくはMacPortsを使ってインストールでもよいとおもいます。
*2 ココのtextに値を入れるMementoぽい動きは、Jenkinsがやってるぽい。シリアライズしといてJenkins再起動時はそっから書き戻してんだと思うけど

添付ファイル: fileconfigtext.png 188件 [詳細] filetext.png 188件 [詳細] fileproject_setting.png 262件 [詳細] fileヒ&#12441;ルト&#12441;のコンソール結果.png 177件 [詳細] fileJenkins_設定.png 231件 [詳細] fileフ&#12442;ロシ&#12441;ェクトの設定.png 204件 [詳細]

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2016-05-29 (日) 16:00:22 (932d)