// 下階層用テンプレート
#topicpath
----


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


//ここにコンテンツを記述します。
#contents


まずは [[Plugin tutorial - 日本語 - Jenkins Wiki>https://wiki.jenkins-ci.org/display/JA/Plugin+tutorial]] に従って、Jenkinsのプラグイン開発を体験してみたいと思います。

**やってみる [#cbc98b72]
環境は Macに [[Mac OS X に maven3 (3.0.5) をインストールする手順 | OSCALOG>http://oscasierra.net/2013/05/install-maven-3-0-mac/]] をつかってMavenなどをインストール済みです。((もしくはMacPortsを使ってインストールでもよいとおもいます。))

***事前の設定 [#q1e08a3e]
 # 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>

*** プロジェクトの作成 [#c13d53c2]
 # 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 というファイルができました。これがプラグインの実体になります。

***ローカルのリポジトリにインストール [#e9b84366]
 # mvn install

*** 動かしてみる。 [#wf780ce2]
 # 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的なプラグインがインストールされているのが分かります。

#ref(Jenkins_設定.png)
プラグイン全体に関する設定画面 にHello World Builderというプラグインが。

#ref(フ&#12442;ロシ&#12441;ェクトの設定.png)
各プラグインの画面にも設定が追加されている。

#ref(ヒ&#12441;ルト&#12441;のコンソール結果.png)
ビルドを実行した結果でプラグインが動いているのが分かる。

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

#ref(project_setting.png)


***HelloWorldBuilder [#e900c1c7]
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 [#hbbcec35]
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 [#q178448f]
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);
        }
  }
ここからアクセス可能なようです。

**いじってみる [#c870c7a0]
*** システムの設定画面に、テキストボックスを追加してみる。 [#m50ff9e9]
システムの設定に表示される設定画面に、新たにテキストボックスを追加してみます。追加する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を再起動したところ、、、、、

#ref(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の情報が画面に反映されます。

***まとめると [#kbaf9625]
まとめると、画面( global.jelly )に「field="textData"」というデータを定義した際は、
 formData.getString("textData");
で画面上のデータにアクセスし(してデータをsaveしておいて)、画面を開く際は
 public String getTextData(){
   return text;
 }
という名前でさっき保存したデータを返すようにしておく、というのが定石のようです。((ココのtextに値を入れるMementoぽい動きは、Jenkinsがやってるぽい。シリアライズしといてJenkins再起動時はそっから書き戻してんだと思うけど))

そしてジョブからそのデータを使用する場合は、プログラム上から
 @Override
 public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) {
   listener.getLogger().print( getDescriptor().getTextData() + ":" + name + "!");
   return true;
 }
などと指定すればOKですね。


***ジョブの設定画面に、テキストボックスを追加してみる。 [#p9ad086d]
次は、各ジョブごとの設定画面に、テキストボックス(テキストエリア)を追加します。追加する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を再起動したところ、、、、、

#ref(configtext.png)

追加されました!!!

ちなみにこの*.jellyに書くタグは
[[Jelly Taglib references>https://jenkins-ci.org/maven-site/jenkins-core/jelly-taglib-ref.html]]  などにリファレンスがあったりします。

さてこの追加された画面の情報をどのようにプラグインが保存するかですが、画面の入力情報は、
 @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;
 }

とアクセス可能です。




**関連リンク [#lce0dfb1]
-[[Welcome to Jenkins CI! | Jenkins CI>http://jenkins-ci.org/]]
-[[Plugin tutorial - 日本語 - Jenkins Wiki>https://wiki.jenkins-ci.org/display/JA/Plugin+tutorial]] プラグイン開発の入り口。。
-[[Hudsonプラグイン開発 - wyukawa’s blog>http://d.hatena.ne.jp/wyukawa/20090605/1244210141]]
-[[継続的インテグレーションツール Hudson のプラグインを作成 - なんとなくな Developer のメモ>http://fits.hatenablog.com/entry/20080502/1209726765]]
-[[Plugins - Jenkins - Jenkins Wiki>https://wiki.jenkins-ci.org/display/JENKINS/Plugins]] さまざまなPluginの一覧。ここからソースにもアクセスできるので、困ったらここからプラグインのソースを落としたりしよう。
-[[Extension points - Jenkins - Jenkins Wiki>https://wiki.jenkins-ci.org/display/JENKINS/Extension+points]] これも同様にいいサンプルになる。
-[[JSTL Core <fmt:formatDate> Tag>http://www.tutorialspoint.com/jsp/jstl_format_formatdate_tag.htm]] Viewの表示にはJSTLぽいJellyを用いるので。。ただ完全互換じゃないぽい。。formatNumberとかつかえないぽいしな。。
-[[Jelly Taglib references>https://jenkins-ci.org/maven-site/jenkins-core/jelly-taglib-ref.html]] とおもったらあった。Taglibのリファレンス
-[[Extend Jenkins - Jenkins - Jenkins Wiki>https://wiki.jenkins-ci.org/display/JENKINS/Extend+Jenkins]] 本家のサイト。
-[[Basic guide to Jelly usage in Jenkins - Jenkins - Jenkins Wiki>https://wiki.jenkins-ci.org/display/JENKINS/Basic+guide+to+Jelly+usage+in+Jenkins#BasicguidetoJellyusageinJenkins-LoadingYourFirst%5C.jelly]] 本家のサイト。UIの作り方とか。
-[[Hyperlinks in HTML - Jenkins - Jenkins Wiki>https://wiki.jenkins-ci.org/display/JENKINS/Hyperlinks+in+HTML]] 本家。リンクのはりかた。
-[[Jelly form controls - Jenkins - Jenkins Wiki>https://wiki.jenkins-ci.org/display/JENKINS/Jelly+form+controls]] 本家。設定画面のフォームの作り方。
-[[Jenkinsプラグインを作成する際のJellyテンプレートでのハマりポイント | Oreradio.memo>http://www.oreradio.com/2012/11/19.php]]
-[[Hudsonを利用してファイルI/Fを実現したい(5) - 履歴>http://d.hatena.ne.jp/sikakura/20110301/1298967274]]
-[[Jenkinsを拡張してファイルI/Fを実現したい - 履歴>http://d.hatena.ne.jp/sikakura/20110315/1300157454]]
-[[jenkins_plugin_samples>https://github.com/yasu-s/jenkins_plugin_samples]]


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

#comment
#topicpath


SIZE(10){現在のアクセス:&counter;}

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS