Hudsonプラグイン開発の入門体験


継続的な統合(CI)は、build、deploy、test automationからcoverage分析まで、ソフトウェアプロジェクトプロセスの各段階を自動化し、毎日の手作業ではなく、すべて自動的に完了します.
迅速な開発の過程で、継続的な統合はチームの生産性を大幅に向上させ、開発とテスト担当者はコードとテスト例の作成に集中することができ、コンパイルと導入にあまり関心を持つ必要はありません.毎晩、継続的な統合の自動化された導入を行い、翌日には、前日のテスト効果とコードカバー率、迅速な開発の理念を組み合わせた適切なメリットのテストと分析をすぐに開始できます.
次はHudsonのプラグインを開発する方法を紹介します.
まずMaven 2とJDK 1が必要です.6以上、これは必須です.そしてあなたのMaven 2のsetting.xmlファイルに次のコードを追加
  <pluginGroups>
	<pluginGroup>org.jvnet.hudson.tools</pluginGroup>  
  </pluginGroups>

  <profiles>
	<profile>  
    <id>hudson</id>  
  
    <activation>  
      <activeByDefault />  
    </activation>  
  
    <pluginRepositories>  
      <pluginRepository>  
        <id>m.g.o-public</id>  
        <url>http://maven.glassfish.org/content/groups/public/</url>  
      </pluginRepository>  
    </pluginRepositories>  
    <repositories>  
      <repository>  
        <id>m.g.o-public</id>  
        <url>http://maven.glassfish.org/content/groups/public/</url>  
      </repository>  
    </repositories>  
  </profile> 
  </profiles>

  <activeProfiles> 
	<activeProfile>hudson</activeProfile>
  </activeProfiles> 

  
これにより、あなたのMavenはHudson-related Maven pluginsのある倉庫を指し、Hudson Maven pluginsの短い名前を使用して関連するプラグインを呼び出すことができます(例えば、org.jvnet.hudson.tools:maven-hpi-plugin:1.23:createの代わりにhpi:create). 
次にCMDに入力
mvn hpi:create

その後、groupIdやartifactIdなどの質問があります.groupIdはjavaコードを開発する一般的なpackage情報、例えばcomに記入します.webex.slim.hudsonplugin、artifactIdは、buildslimなどのhudsonプラグインを作成する名前です.完了すると、コンピュータは自動的にプロジェクトを作成し、テンプレートコードがいくつか入っています.Hudsonのプラグインを書く方法を学ぶことができます.後のコードはすべてテンプレートコードから来ています.Eclipseでプラグインを編集する必要がある場合は実行できます
mvn -DdownloadSources=true eclipse:eclipse

その後、Eclipseでこのプロジェクトをインポートして開発を開始することができます.Eclipseでmavenプロジェクトをインポートするには、mavenプラグインをインストールし、プロジェクトで既存のプロジェクトをインポートすることを選択します.
 
前のmavenコマンドを実行すると、指定したディレクトリの下にHudsonプラグインのプロジェクトフォルダが生成されました.このディレクトリは~/artifactId/であるはずです.Eclipseでこのプロジェクトをインポートすると、プロジェクトには次のような構造が表示されます.
+ src 
    + main 
        + java 
             +  full.package.name 
                    +- HelloWorldBuilder.java 
+resources 
             +  full.package.name 
                    +- config.jelly 
                    +- global.jelly 
                +- index.jelly 
        + webapp 
            +- help-globalConfig.html 
            +- help-projectConfig.html 

 HelloWorldBuilder.java 
このクラスは、ある拡張点を具体的に実現するクラスであり、ここではBuilderという拡張点を拡張するため、Builderというクラスを継承する.HudsonにはPublisher、Recorderなど、さまざまな種類の拡張点があります.詳しくはハドソンのサイトを参考にしてください. 
工事が完了した後、mavenが自動的に生成したhudsonプラグインのサンプルコードがいくつかあります.
次に、これらのコードを徐々に分析します.
@DataBoundConstructor   
public HelloWorldBuilder(String name) {   
	this.name = name;   
}   

/**  
 * We'll use this from the <tt>config.jelly</tt>.  
 */   
public String getName() {   
	return name;   
}  

このコードはこのBuliderを構築し、対応するconfigから使用する.Jellyで対応するパラメータを取得します.Hudsonはstructured form submissionという技術を用いて,この方法で対応するパラメータをアクティブにすることができるようにした. 
public boolean perform(Build 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(DESCRIPTOR.useFrench())   
		 listener.getLogger().println("Bonjour, "+name+"!");   
	 else   
		 listener.getLogger().println("Hello, "+name+"!");   
	return true;   
}   

メソッドperform()は重要なメソッドであり、プラグインが実行されるとこのメソッドが呼び出されます.対応するビジネスロジックもここで実現できます.たとえばこのperform()メソッドでは「Hello」という言葉が実現します.次に、HelloBuilderというクラスにDescriptor Implという内部クラスがあり、Descriptorを継承しています.Hudsonの公式説明ドキュメントでは、Descriptorには構成インスタンスのメタデータが含まれていると述べています.たとえば、プラグインをエンジニアリング構成で構成しました.これは、プラグインの構成データを格納するクラスを作成することに相当します.このクラスがDescriptorです. 
 
public String getDisplayName() {   
	return "Say hello world";   
}   

上のコードのように、Descriptorのこの方法の下でプラグインを設定して工事の配置のページの下で現実的な名前を設定することができます
public boolean configure(StaplerRequest req, JSONObject o) throws FormException {   
	 // to persist global configuration information,   
	 // set that to properties and call save().   
	 useFrench = o.getBoolean("useFrench");   
	 save();   
	 return super.configure(req);   
}  

このメソッドは、コメントが属するように、グローバル構成をプロジェクトに格納するために使用されます.
 
注意点:
HUDSON_HOME:
Hudsonは、構築のたびに関連する構成情報を保持し、テストの結果を保存する場所を必要とする.これは、Hudson環境を導入すると、現在のユーザーの下に自動的に新しいシステムが作成されることである.hudson、linuxの下で:~/.hudson、このパスを変えるには3つの方法があります.
1.servletコンテナを起動する前に、「HUDSON_HOME」環境変数を設定し、設定するディレクトリを指します.
2.servletコンテナでシステム属性を設定する
3.JNDIの環境エンティティ[HUDSON_HOME]を設定し、設定するディレクトリを指定します.
現在glassfishでjvm-optionを設定する方法は2つ目です.
この変数を設定してからディレクトリを変更したいのですが、以前の構成をなくしたくない場合はどうすればいいのでしょうか.簡単です.Hudsonを閉じて、ディレクトリAの内容をコピーしたディレクトリBに行って、「HUDSON_HOME」の値を再設定してから、再起動すると、前に行ったすべての構成が完全に残っていることがわかります.
1.Hudson-homeのディレクトリ構造:
HUDSON_HOME
 +- config.xml(hudsonの基本プロファイル、例えばjdkのインストールパス)
 +- *.xml(他のシステム関連プロファイル、例えばワンワンプラグインのグローバル構成情報)
+-fingerprints(ファイルバージョン追跡記録情報を格納)
+-plugins(プラグインを保存)
 +- jobs
+-[JOBNAME](タスク名、対応ページのproject name)
         +- config.xml(CCのconfig.xmlの各項目の構成と同様のタスクプロファイル)
+-workspace(SCMで使用されるディレクトリ、hudsonでダウンロードされたコードはデフォルトでこのディレクトリに保存されます)
         +- builds
+-[BUILD_ID](構築ごとのシーケンス番号)
                 +- build.xml(構築結果要約)
+-log(実行ログ)
                 +- changelog.xml(SCM変更ログ)
ヒント:e-mailを使用してテストメッセージを受信し、hudsonの移行が異なるipアドレスマシンへの移行を設計している場合は、Hudsonのプライマリ構成でHudsonのアクセスアドレスを変更する必要がある場合があります.
workspace:
先ほどhudson-homeのディレクトリ構造でworkspceが見られましたが、現在のhudson-homeが/home/hudson-homeであると仮定すると、hudson上でプロジェクトdemoを構成すると、新しいディレクトリ/home/hudson-home/demoが作成され、最初の実行前にjobsの下にdemoというフォルダはありません.最初の実行後にjobsディレクトリの下にdemoディレクトリが作成されるのは、コードがsvnから順調にダウンロードされるとworkspaceフォルダが作成され、svnからダウンロードされたコードはすべてこのディレクトリに保存されます.
1、相対パス:
プロジェクト構成では、Hudsonは相対パスを使用します.Hudsonでは、demoなどのプロジェクトを新規作成した後、workspaceのディレクトリ構造を次のように仮定します.
workspace
 +- demo
     +- pom.xml
     +- src
テストレポートのパスはdemo/target/surefire-reports/*です.xml、システムは自動的に現在のプロジェクトのworkspaceに行ってこの経路を探します
 
mvn package--コード開発が完了するとpom.xmlの構成情報はhpi形式のプラグインファイルにパッケージされます.これは最終的にhudsonプラットフォームにアップロードできるものです.
mvn hpi:run--ローカルJettyでhudsonプラグインを実行し、デバッグ専用です.もちろんDebugモードを使用して、実行後、ローカルでアクセスできます.http://localhost:8080/すぐに表示されます(8080ポートは使用しないでください)
mvnDebug hup:run,debugデバッグモード
以下に、プロジェクト構築用に自分で書いたHudsonプラグインのソースコードを自動的にコンパイルします.
HelloWorldBuilder.java
package zygroup;
import hudson.FilePath;
import hudson.Launcher;
import hudson.Extension;
import hudson.Proc;
import hudson.util.FormValidation;
import hudson.model.AbstractBuild;
import hudson.model.BuildListener;
import hudson.model.AbstractProject;
import hudson.remoting.Channel;
import hudson.tasks.Builder;
import hudson.tasks.BuildStepDescriptor;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.QueryParameter;

import javax.servlet.ServletException;
import java.io.IOException;


public class HelloWorldBuilder extends Builder {

    private final String locate;
    private final String cmd;

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

    /**
     * We'll use this from the <tt>config.jelly</tt>.
     */
    public String getLocate() {
        return locate;
    }

    public String getCmd() {
        return cmd;
    }
    
    @Override
    public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) {
        listener.getLogger().println("The SLiM build home is "+locate+".");
        listener.getLogger().println("The SLiM build command is "+cmd+".");
         
        try {
        	FilePath path = new FilePath(Channel.current(),locate);
            Proc proc = launcher.launch(cmd, build.getEnvVars(), listener.getLogger(),path);
            int exitCode = proc.join();
            if (exitCode != 0) return false;

            return true;
          } catch (IOException e) {
            e.printStackTrace();
            listener.getLogger().println("IOException !");
            return false;
          } catch (InterruptedException e) {
            e.printStackTrace();
            listener.getLogger().println("InterruptedException!");
            return false;
          }
    }

    @Override
    public DescriptorImpl getDescriptor() {
        return (DescriptorImpl)super.getDescriptor();
    }

    @Extension // this marker indicates Hudson that this is an implementation of an extension point.
    public static final class DescriptorImpl extends BuildStepDescriptor<Builder> {

        public FormValidation doCheckName(@QueryParameter String value) throws IOException, ServletException {
            if(value.length()==0)
                return FormValidation.error("Please set a name");
            if(value.length()<4)
                return FormValidation.warning("Isn't the name too short?");
            return FormValidation.ok();
        }

        public boolean isApplicable(Class<? extends AbstractProject> aClass) {
            // indicates that this builder can be used with all kinds of project types 
            return true;
        }

        public String getDisplayName() {
            return "SLiM build";
        }

        @Override
        public boolean configure(StaplerRequest req, JSONObject formData) throws FormException {
            save();
            return super.configure(req,formData);
        }


    }
}
プラグイン関連のユーザ入力ページのファイルconfigを設定.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">
  <!--
    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="Build Home" help="plugin/zyartifact/WEB-INF/classes/zygroup/HelloWorldBuilder/help-buildhome.html">
    <f:textbox name="locate" type="text" value="${instance.locate}"/>
  </f:entry>
   <f:entry title="Build Command" help="plugin/zyartifact/WEB-INF/classes/zygroup/HelloWorldBuilder/help-cmd.html">
    <f:textbox name="cmd" type="text" value="${instance.cmd}"/>
  </f:entry>
</j:jelly>

のhelpプロパティはhtmlファイルを指し、コードに設定された場所に標準のhtmlタグを書き込むことができ、この入力ボックスの右側にヘルプボタンとヘルプ情報を表示します.
このプラグインの主な入力内容は次のとおりです.
locateとcmdの2つの文字列は、buildプログラムに渡されて使用され、locateとcmdの2つの変数になります.ユーザーがコンストラクションコードを入力するディレクトリと、コンストラクションを開始するコマンド.
たとえば
/opt/CruiseControl/apache-ant-1.7.0/
ant antbuild
buildプログラムは、この2つの変数を取得するとshellを起動し、locateディレクトリの下でcmdコマンドを実行します.この機能はperform関数で実現されます.
    public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) {
	
		// hudson 
        listener.getLogger().println("The SLiM build home is "+locate+".");
        listener.getLogger().println("The SLiM build command is "+cmd+".");
         
        try {
			// locate hudson FilePath 
        	FilePath path = new FilePath(Channel.current(),locate);
			
			// path cmd 
            Proc proc = launcher.launch(cmd, build.getEnvVars(), listener.getLogger(),path);
			
			// shell , 
            int exitCode = proc.join();
            if (exitCode != 0) return false;

			// 
            return true;
          } catch (IOException e) {
				......
          }
    }

Windowsのcmdまたはlinuxのコンソールでプロジェクトディレクトリの下にmvn packageを入力すると、hpiファイルとjarファイルを含むtargetディレクトリの下のファイルが自動的に生成されます.
hpiをhudsonディレクトリのpluginディレクトリにコピーするか、hudsonのページにプラグインをアップロードしてhudsonを再起動すれば使用できます.
このプラグインはbuildタイプのプラグインで、hudsonのjob構成ページに表示され、build stepドロップダウンメニューに表示されます.名前はHelloWorldBuilderです.JAvaの次の関数制御:
        public String getDisplayName() {
            return "SLiM build";
        }

プラグインhudsonがインストールしたプラグインのリストに表示される名前は、mavenプロジェクトのpoe.xml構成:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.jvnet.hudson.plugins</groupId>
        <artifactId>hudson-plugin-parent</artifactId>
        <version>2.1.1</version><!-- which version of Hudson is this plugin built against? -->
    </parent>

    <groupId>zygroup</groupId>
    <artifactId>zyartifact</artifactId>
    <version>1.0-SNAPSHOT</version>

    <packaging>hpi</packaging>

    <name>SLiM build</name>

</project>

このようなプロジェクトの自動構築を実現する簡単なプラグインが使用できます^.^