第一次做Jenkins插件开发,遂将笔记公开分享

插件名称: gettingCase

插件功能: 获取RallyDev上的某一个Test Case信息

 

0.配置.m2/settings.xml

请查阅本文最后的参考资料

 

1.Maven创建Jenkins插件项目

mvn -U org.jenkins-ci.tools:maven-hpi-plugin:create

第一次执行会比较慢,因为需要下载很多Maven插件.

这个创建项目的过程有2步互动:

第一步需要开发者输入Maven项目的groupId

Enter the groupId of your plugin [org.jenkins-ci.plugins]:

com.technicolor.qcs

第二步需要开发者输入Maven项目的artifactId

Enter the artifactId of your plugin (normally without '-plugin' suffix):

gettingCase

 

2.基于Hello World插件项目,修改以实现自己插件功能

2.1POM

修改pom.xml文件,增加REST访问RallyDev的工具包



创建 Jenkins 插件项目 jenkins插件开发实例_ci

1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
3. <modelVersion>4.0.0</modelVersion>
4. <parent>
5. <groupId>org.jenkins-ci.plugins</groupId>
6. <artifactId>plugin</artifactId>
7. <version>1.509.3</version>
8. <!-- which version of Jenkins is this plugin built against? -->
9. </parent>
10.   
11. <groupId>com.technicolor.qcs</groupId>
12. <artifactId>gettingCase</artifactId>
13. <version>1.0-SNAPSHOT</version>
14. <packaging>hpi</packaging>
15. <description>Gets Rally Test Cases</description>
16.   
17. <developers>
18. <developer>
19. <id>feuyeux</id>
20. <name>eric han</name>
21. <email>feuyeux@gmail.com</email>
22. </developer>
23. </developers>
24. <dependencies>
25. <dependency>
26. <groupId>com.rallydev.rest</groupId>
27. <artifactId>rally-rest-api</artifactId>
28. <version>2.0.4</version>
29. </dependency>
30. <dependency>
31. <groupId>org.apache.httpcomponents</groupId>
32. <artifactId>httpcore</artifactId>
33. <version>4.2.1</version>
34. </dependency>
35. <dependency>
36. <groupId>org.apache.httpcomponents</groupId>
37. <artifactId>httpclient</artifactId>
38. <version>4.2.1</version>
39. </dependency>
40. <dependency>
41. <groupId>org.apache.httpcomponents</groupId>
42. <artifactId>httpclient-cache</artifactId>
43. <version>4.2.1</version>
44. </dependency>
45. <dependency>
46. <groupId>org.apache.httpcomponents</groupId>
47. <artifactId>httpmime</artifactId>
48. <version>4.2.1</version>
49. </dependency>
50. <dependency>
51. <groupId>org.apache.httpcomponents</groupId>
52. <artifactId>fluent-hc</artifactId>
53. <version>4.2.1</version>
54. </dependency>
55. <dependency>
56. <groupId>org.apache.commons</groupId>
57. <artifactId>commons-lang3</artifactId>
58. <version>3.1</version>
59. </dependency>
60. <dependency>
61. <groupId>commons-logging</groupId>
62. <artifactId>commons-logging</artifactId>
63. <version>1.1.1</version>
64. </dependency>
65. <dependency>
66. <groupId>commons-codec</groupId>
67. <artifactId>commons-codec</artifactId>
68. <version>1.6</version>
69. </dependency>
70. <dependency>
71. <groupId>com.google.code.gson</groupId>
72. <artifactId>gson</artifactId>
73. <version>2.1</version>
74. </dependency>
75. </dependencies>
76.   
77. <repositories>
78. <repository>
79. <id>repo.jenkins-ci.org</id>
80. <url>http://repo.jenkins-ci.org/public/</url>
81. </repository>
82. </repositories>
83.   
84. <pluginRepositories>
85. <pluginRepository>
86. <id>repo.jenkins-ci.org</id>
87. <url>http://repo.jenkins-ci.org/public/</url>
88. </pluginRepository>
89. </pluginRepositories>
90. </project>



 2.2 编写全局配置页面

/home/hanl/j-ci/gettingCase/src/main/resources/com/technicolor/qcs/gettingCase/GetCasesBuilder/global.jelly

 



创建 Jenkins 插件项目 jenkins插件开发实例_ci



1. <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">  
2. "Getting Test Cases Builder">  
3. "RallyDev User Name" field="userName">  
4.       <f:textbox />  
5.     </f:entry>  
6. "RallyDev Password" field="password">  
7.       <f:password/>  
8.     </f:entry>  
9.   
10. "HTTP Proxy URL" field="proxyURL">  
11.       <f:textbox />  
12.     </f:entry>  
13. "RallyDev Proxy User Name" field="proxyUser">  
14.       <f:textbox />  
15.     </f:entry>  
16. "RallyDev Proxy Password" field="proxyPassword">  
17.       <f:password />  
18.     </f:entry>  
19.   </f:section>  
20. </j:jelly>

 





 2.3 编写JOB配置页面



创建 Jenkins 插件项目 jenkins插件开发实例_ci



    1. 1
    2. ${app.displayName}. //应用的Display名称
    3.   
    4. 2
    5. ${it.name} 对应于builder的getName()方法  
    6.   
    7. 3




    /home/hanl/j-ci/gettingCase/src/main/resources/com/technicolor/qcs/gettingCase/GetCasesBuilder/config.jelly



    创建 Jenkins 插件项目 jenkins插件开发实例_ci


    1. <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">  
    2. "Test Case No." field="testCaseId">  
    3.     <f:textbox />  
    4.   </f:entry>  
    5. </j:jelly>


     

    2.4 编写扩展点方法

     



    创建 Jenkins 插件项目 jenkins插件开发实例_ci



    1. 一次构建过程通常包括:  
    2. SCM checkout - check out出源码  
    3. Pre-build    - 预编译  
    4. Build wrapper  -准备构建的环境,设置环境变量等  
    5. Builder runs   - 执行构建,比如调用calling Ant, Make  
    6. Recording    - 记录输出,如测试结果  
    7. Notification    - 通知成员  



     

    /home/hanl/j-ci/gettingCase/src/main/java/com/technicolor/qcs/gettingCase/GetCasesBuilder.java



    创建 Jenkins 插件项目 jenkins插件开发实例_ci


    1. package
    2.   
    3. import
    4. import
    5. import
    6. import
    7. import
    8. import
    9. import
    10. import
    11. import
    12. import
    13. import
    14. import
    15.   
    16. import
    17. import
    18. import
    19.   
    20. /**
    21.  * author:feuyeux
    22.  */
    23. public class GetCasesBuilder extends
    24. public static final String RALLY_URL = "https://rally1.rallydev.com";  
    25. private final
    26.   
    27. @DataBoundConstructor
    28. public
    29. this.testCaseId = testCaseId;  
    30.     }  
    31.   
    32. public
    33. return
    34.     }  
    35.   
    36. @Override
    37. public boolean
    38.         PrintStream out = listener.getLogger();  
    39. final
    40. final
    41. final
    42. final
    43. final
    44.   
    45. "RallyDev User Name ="
    46. "HTTP Proxy=" + proxyUser + "@"
    47. "RallyDev Test Case =" + getTestCaseId()+"\n");  
    48. null;  
    49. try
    50. new
    51.             String caseInfo = rallyClient.getTestCases(testCaseId);  
    52.             out.println(caseInfo);  
    53. catch
    54.             out.println(e.getMessage());  
    55. finally
    56. try
    57.                 rallyClient.close();  
    58. catch
    59.                 e.printStackTrace();  
    60.             }  
    61.         }  
    62. return true;  
    63.     }  
    64.   
    65. @Override
    66. public
    67. return (DescriptorImpl) super.getDescriptor();  
    68.     }  
    69.   
    70. @Extension
    71. public static final class DescriptorImpl extends
    72. private
    73. private
    74. private
    75. private
    76. private
    77.   
    78. public FormValidation doCheckName(@QueryParameter
    79. throws
    80. if (value.length() == 0)  
    81. return FormValidation.error("Please set a testCaseId");  
    82. return
    83.         }  
    84.   
    85. public boolean isApplicable(Class<? extends
    86. return true;  
    87.         }  
    88.   
    89. public
    90. return "Getting Test cases from Rally";  
    91.         }  
    92.   
    93. @Override
    94. public boolean configure(StaplerRequest req, JSONObject formData) throws
    95. "userName");  
    96. "password");  
    97. "proxyURL");  
    98. "proxyUser");  
    99. "proxyPassword");  
    100.             save();  
    101. return super.configure(req, formData);  
    102.         }  
    103.   
    104. public
    105. return
    106.         }  
    107.   
    108. public
    109. return
    110.         }  
    111.   
    112. public
    113. return
    114.         }  
    115.   
    116. public
    117. return
    118.         }  
    119.   
    120. public
    121. return
    122.         }  
    123.     }  
    124. }



     2.5 编写RallyDev连接和访问方法

    /home/hanl/j-ci/gettingCase/src/main/java/com/technicolor/qcs/gettingCase/RallyClient.java



    创建 Jenkins 插件项目 jenkins插件开发实例_ci


    1. package
    2.   
    3. import
    4. import
    5. import
    6. import
    7. import
    8.   
    9. import
    10. import
    11. import
    12.   
    13. /**
    14.  * author:feuyeux
    15.  */
    16. public class
    17. /*https://github.com/RallyTools/RallyRestToolkitForJavahttps://github.com/RallyTools/RallyRestToolkitForJava */
    18. private final
    19.   
    20. public RallyClient(String rally_url, String userName, String password, String proxyURL, String proxyUser, String proxyPassword) throws
    21. new RallyRestApi(new
    22. new
    23.     }  
    24.   
    25. public void close() throws
    26.         restApi.close();  
    27.     }  
    28.   
    29. public
    30. /*https://rally1.rallydev.com/slm/doc/webservice/*/
    31. new
    32.   
    33.         String version = restApi.getWsapiVersion();  
    34. "RallyDev Rest Version=").append(version).append("\n");  
    35.   
    36. final String query = "/testcase/"+testCaseId;  
    37. new
    38. null;  
    39. try
    40.             casesResponse = restApi.get(queryRequest);  
    41. catch
    42.             e.printStackTrace();  
    43.         }  
    44.         printWarningsOrErrors(casesResponse, result);  
    45.         JsonObject caseJsonObject = casesResponse.getObject();  
    46.   
    47. "\n").append("Test Case Name: ").append(caseJsonObject.get("Name").getAsString());  
    48. "\n").append("Test Case Type: ").append(caseJsonObject.get("Type").getAsString());  
    49. "\n").append("Test Case URL: ").append(caseJsonObject.get("_ref").getAsString());  
    50. "\n").append("Test Case Creation Time: ").append(caseJsonObject.get("CreationDate").getAsString());  
    51. "\n").append("Test Case LastUpdate Time: ").append(caseJsonObject.get("LastUpdateDate").getAsString());  
    52. "\n").append("Test Case's Project: ").append( caseJsonObject.get("Project").getAsJsonObject().get("_refObjectName")    .getAsString());  
    53. "\n").append("Test Case's Workspace: ").append( caseJsonObject.get("Workspace").getAsJsonObject().get("_refObjectName")    .getAsString());  
    54.   
    55. return
    56.     }  
    57.   
    58. private void
    59. if
    60. "\nSuccess.");  
    61.             String[] warningList;  
    62.             warningList = response.getWarnings();  
    63. for (int i = 0; i < warningList.length; i++) {  
    64. "\twarning:\n"
    65.             }  
    66. else
    67.             String[] errorList;  
    68.             errorList = response.getErrors();  
    69. if (errorList.length > 0) {  
    70. "\nError.");  
    71.   
    72.             }  
    73. for (int i = 0; i < errorList.length; i++) {  
    74. "\terror:\n"
    75.             }  
    76.         }  
    77.     }  
    78. }



     3.调试Plugin程序

    在终端/控制台,首先执行Maven变量配置命令



    创建 Jenkins 插件项目 jenkins插件开发实例_ci



    1. set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8000,suspend=n  



    cd到插件项目目录,执行如下命令



    创建 Jenkins 插件项目 jenkins插件开发实例_ci


    1. hanl@hanl-ubuntu1204:~/j-ci/gettingCase$ mvn clean  
    2. hanl@hanl-ubuntu1204:~/j-ci/gettingCase$ mvnDebug hpi:run


     Maven将对8000端口执行监听,以便在IDE中进行断点调试



    创建 Jenkins 插件项目 jenkins插件开发实例_ci



    1. Preparing to Execute Maven in
    2. Listening for transport dt_socket at address: 8000



     

    进入IDE(本例使用IntelliJ),选择Run菜单的Debug,添加一个8000端口的远程服务器

     

    添加断点,点击左下角运行调试按钮



    创建 Jenkins 插件项目 jenkins插件开发实例_ci


    1. hanl@hanl-ubuntu1204:~/j-ci/gettingCase$ mvnDebug hpi:run  
    2. Preparing to Execute Maven in
    3. Listening for transport dt_socket at address: 8000
    4. [INFO] Scanning for
    5. [INFO]                                                                           
    6. [INFO] ------------------------------------------------------------------------  
    7. [INFO] Building gettingCase 1.0-SNAPSHOT  
    8. [INFO] ------------------------------------------------------------------------  
    9. [INFO]   
    10. [INFO] >>> maven-hpi-plugin:1.95:run (default-cli) @ gettingCase >>>  
    11. [INFO]   
    12. [INFO] --- maven-hpi-plugin:1.95:validate (default-validate) @ gettingCase ---  
    13. [INFO]   
    14. [INFO] --- maven-enforcer-plugin:1.0.1:enforce (enforce-maven) @ gettingCase ---  
    15. [INFO]   
    16. [INFO] --- maven-enforcer-plugin:1.0.1:display-info (display-info) @ gettingCase ---  
    17. [INFO] Maven Version: 3.0.4
    18. [INFO] JDK Version: 1.7.0_25 normalized as: 1.7.0-25
    19. [INFO] OS Info: Arch: amd64 Family: unix Name: linux Version: 3.2.0-54-generic  
    20. [INFO]   
    21. [INFO] --- maven-localizer-plugin:1.14:generate (default) @ gettingCase ---  
    22. [INFO]   
    23. [INFO] --- maven-resources-plugin:2.5:resources (default-resources) @ gettingCase ---  
    24. [debug] execute contextualize  
    25. [INFO] Using 'UTF-8'
    26. [INFO] Copying 5
    27. [INFO]   
    28. [INFO] --- maven-compiler-plugin:2.5:compile (default-compile) @ gettingCase ---  
    29. [INFO] Nothing to compile - all classes are up to date  
    30. [INFO]   
    31. [INFO] <<< maven-hpi-plugin:1.95:run (default-cli) @ gettingCase <<<  
    32. [INFO]   
    33. [INFO] --- maven-hpi-plugin:1.95:run (default-cli) @ gettingCase ---  
    34. [INFO] Generating ./work/plugins/gettingCase.hpl  
    35. [INFO] Copying dependency Jenkins plugin /home/hanl/.m2/repository/org/jenkins-ci/plugins/mailer/1.4/mailer-1.4.jar  
    36. [INFO] Copying dependency Jenkins plugin /home/hanl/.m2/repository/org/jenkins-ci/plugins/ant/1.1/ant-1.1.jar  
    37. [INFO] Copying dependency Jenkins plugin /home/hanl/.m2/repository/org/jenkins-ci/main/maven-plugin/1.509.3/maven-plugin-1.509.3.jar
    38. [INFO] Copying dependency Jenkins plugin /home/hanl/.m2/repository/org/jenkins-ci/plugins/javadoc/1.0/javadoc-1.0.jar  
    39. [INFO] Copying dependency Jenkins plugin /home/hanl/.m2/repository/org/jenkins-ci/plugins/subversion/1.26/subversion-1.26.jar  
    40. [INFO] Copying dependency Jenkins plugin /home/hanl/.m2/repository/org/jenkins-ci/main/ui-samples-plugin/1.509.3/ui-samples-plugin-1.509.3.jar
    41. [INFO] Configuring Jetty for
    42. 2013-10-10 18:10:55.444::INFO:  Logging to STDERR via org.mortbay.log.StdErrLog  
    43. [INFO] Context path = /jenkins  
    44. [INFO] Tmp directory = /home/hanl/j-ci/gettingCase/target/work  
    45. [INFO] Web defaults =  jetty default  
    46. [INFO] Starting jetty 6.1.1
    47. 2013-10-10 18:10:55.587::INFO:  jetty-6.1.1
    48. Jenkins home directory: /home/hanl/j-ci/gettingCase/./work found at: System.getProperty("HUDSON_HOME")  
    49. 2013-10-10 18:10:59.572::INFO:  Started SelectChannelConnector @ 0.0.0.0:8080
    50. [INFO] Started Jetty Server  
    51. [INFO] Console reloading is
    52. Oct 10, 2013 6:10:59 PM jenkins.InitReactorRunner$1
    53. INFO: Started initialization  
    54. Oct 10, 2013 6:11:01 PM jenkins.InitReactorRunner$1
    55. INFO: Listed all plugins  
    56. Oct 10, 2013 6:11:01 PM jenkins.InitReactorRunner$1
    57. INFO: Prepared all plugins  
    58. Oct 10, 2013 6:11:01 PM jenkins.InitReactorRunner$1
    59. INFO: Started all plugins  
    60. Oct 10, 2013 6:11:01 PM jenkins.InitReactorRunner$1
    61. INFO: Augmented all extensions  
    62. Oct 10, 2013 6:11:04 PM jenkins.InitReactorRunner$1
    63. INFO: Loaded all jobs  
    64. Oct 10, 2013 6:11:05
    65. INFO: Started SSHD at port 54676
    66. Oct 10, 2013 6:11:05 PM jenkins.InitReactorRunner$1
    67. INFO: Completed initialization  
    68. Oct 10, 2013 6:11:05
    69. INFO: JNLP slave agent listener started on TCP port 47342
    70. Oct 10, 2013 6:11:05 PM hudson.WebAppMain$2
    71. INFO: Jenkins is fully up and



    4.测试

    在浏览器中录入Jenkins地址,创建Job并按照上述的配置环节,完成配置.



    执行Build Job





    当完成调试后,进入Jenkins构建结果页面,观察构建结果是否符合预期.

     

    到此,获取并显示RallyDev中Test Case的Jenkins插件开发完毕.

     

    IntelliJ IDE 插件:

    https://wiki.jenkins-ci.org/display/JENKINS/IntelliJ+IDEA+plugin+for+Stapler

    Stapler plugin for IntelliJ IDEA

     

    参考资料

    https://wiki.jenkins-ci.org/display/JENKINS/Plugin+tutorial

    https://github.com/jenkinsci/hello-world-plugin

    https://jenkins-ci.org/maven-site/jenkins-core/jelly-taglib-ref.html

    ui-samples: http://localhost:8080/jenkins/ui-samples/

    扩展点:https://wiki.jenkins-ci.org/display/JENKINS/Extension+points

     

     


    创建 Jenkins 插件项目 jenkins插件开发实例_ci


      1. <f:entry title="Test Station">  
      2. <select class="setting-input" name="AndroidBuilder.stationUrl">  
      3. "inst" items="${descriptor.stations}">  
      4. "${inst.url==instance.stationUrl}">${inst.url}</f:option>  
      5.     </j:forEach>  
      6. </select>  
      7. </f:entry>