扩展你的插件
尽管可以使用纯文本编辑器,但使用支持 Maven 的 Java IDE 更容易进行接下来的步骤。 |
到用新功能扩展示例插件的时候了:
我们必须存储运行构建时使用的名称,而不是使用配置中的名称,因为配置随时可能会被更改。
记录名称
首先,在与 HelloWorldBuilder
相同的包中创建一个名为 HelloWorldAction
的类。 该类需要实现 Action。 Actions 是 Jenkins 中可扩展性的基本构建块:它们可以附加到许多模型对象上并被储存起来,并可以添加到它们的 UI 中。
package io.jenkins.plugins.sample;
import hudson.model.Action;
public class HelloWorldAction implements Action {
@Override
public String getIconFileName() {
return null;
}
@Override
public String getDisplayName() {
return null;
}
@Override
public String getUrlName() {
return null;
}
}
由于我们要存储问候中使用的名称,因此我们需要为此类添加一个 getter。我们还将添加一个以 name 为参数的构造方法。
(...)
public class HelloWorldAction implements Action {
private String name;
public HelloWorldAction(String name) {
this.name = name;
}
public String getName() {
return name;
}
(...)
现在,我们实际上需要在构建步骤执行时创建该类的一个实例。 我们需要扩展 HelloWorldBuilder
类中的 perform
方法来把我们创建的动作的一个实例添加到正在运行的构建中:
(...)
@Override
public void perform(Run<?, ?> run, FilePath workspace, Launcher launcher, TaskListener listener) throws InterruptedException, IOException {
run.addAction(new HelloWorldAction(name)); (1)
if (useFrench) {
listener.getLogger().println("Bonjour, " + name + "!");
} else {
listener.getLogger().println("Hello, " + name + "!");
}
}
(...)
1 | 这一行是新增的,其他所有内容都保持不变。 |
保存这些修改,然后用 mvn hpi:run
再次运行插件。
每当修改 Java 源代码或添加删除资源文件时,您都需要重新启动 Jenkins 实例才能使修改生效。 Jenkins 通过 hpi:run 运行时只能编辑一些资源文件。 |
现在,使用此构建步骤运行构建时,该操作将被添加到构建数据中。 我们可以通过查看与 work/jobs/JOBNAME/builds/BUILDNUMBER/
目录中的构建版本相对应的 build.xml
文件来确认。
我们可以看到,这个构建中有两个动作:
<build>
<actions>
<hudson.model.CauseAction> (1)
<causes>
<hudson.model.Cause_-UserIdCause/>
</causes>
</hudson.model.CauseAction>
<io.jenkins.plugins.sample.HelloWorldAction plugin="demo@1.0-SNAPSHOT"> (2)
<name>Jenkins</name> (3)
</io.jenkins.plugins.sample.HelloWorldAction>
</actions>
(...)
</build>
1 | 构建原因(构建如何触发)也存储为 Action。在这种情况下,匿名用户开始了构建。 |
2 | 这是我们创建的 Action。 |
3 | 这是创建构建时使用的名称。 |
添加一个显示名称的视图
接下来,我们需要使我们正在存储的构建可视化。
首先,我们需要回到过去 HelloWorldAction
并定义图标,标题和 URL 名称:
@Override
public String getIconFileName() {
return "document.png"; (1)
}
@Override
public String getDisplayName() {
return "Greeting"; (2)
}
@Override
public String getUrlName() {
return "greeting"; (3)
}
1 | 这是用于侧面板项目的图标。 document.png 是 Jenkins 绑定的预定义图标之一。 |
2 | 这是用于侧面板项目的标签。 |
3 | 这是用于此操作的 URL 片段。 |
通过这些修改,操作将显示在构建的侧面板中,并链接到 URL http://JENKINS/job/JOBNAME/BUILDNUMBER/greeting/
。
接下来,需要定义出现在该 URL 上的页面。为了在 Jenkins 创建这样的 视图 , 通常使用 Apache Commons Jelly。Jelly 允许用 XML 定义 XML 和 XHTML 输出。它有许多有用的功能:它
在 src/main/resources/io/jenkins/plugins/sample/
中, 我们需要创建一个新的名为 HelloWorldAction/
的目录。该目录与 HelloWorldAction
类对应并包含相关资源。
它是 src/main/resources 中的一个目录, 而不是 src/main/java 。 |
我们可以看到与构建步骤 HelloWorldBuilder 相关的资源,它被存储在 src/main/resources/io/jenkins/plugins/sample/HelloWorldBuilder/ 目录。config.jelly 是构建步骤配置表单,包含构建步骤配置的本地化的各种 config.properties 文件和为配置提供了本地化的内联帮助 help .html 文件。 |
在 src/main/resources/io/jenkins/plugins/sample/HelloWorldAction/
目录创建名为 index.jelly
的文件。这将会显示在 http://JENKINS/job/JOBNAME/BUILDNUMBER/greeting/
URL 上。添加以下内容:
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout" xmlns:st="jelly:stapler">
<l:layout title="Greeting"> (1)
<l:main-panel> (2)
<h1> (3)
Name: ${it.name} (4)
</h1>
</l:main-panel>
</l:layout>
</j:jelly>
1 | layout 是 Jenkins 核心中定义的可重用 tag,它提供了页眉,侧面板,主要内容区域和页脚的基本页面布局。 |
2 | 为了使名称显示在主内容区域(而不是侧面板),我们需要将输出包裹在 main-panel 标签中。 |
3 | 我们可以使用任何 HTML 标签,并将它们用于输出。 |
4 | 这是一个 JEXL 表达式。 it 引用视图所属的Java对象 (类似于Java中的 this ),在本例中为 HelloWorldAction 实例。 it.name 等同于调用 getName() 。 |
结果页面看起来像这样:
将构建的侧面板添加到视图
在上面的输出中没有侧面板,由于此视图与特定版本相关,因此应该显示该版本的侧面板。 为此,我们首先需要获取对我们动作中相应构建的引用,然后在动作视图中包含构建的侧面视图 fragment。
为了获得 HelloWorldAction
所属的构建(或者更一般地说, Run
)的引用,我们需要改变现有的类以使其实现 RunAction2
。
该接口添加了两个方法,当运行首次连接到构建时 onAttached(Run)
,以及从磁盘 onLoad(Run)
加载操作和运行时分别调用该方法。
(...)
import hudson.model.Run;
import jenkins.model.RunAction2;
public class HelloWorldAction implements RunAction2 { (1)
private transient Run run; (2)
@Override
public void onAttached(Run<?, ?> run) {
this.run = run; (3)
}
@Override
public void onLoad(Run<?, ?> run) {
this.run = run; (4)
}
public Run getRun() { (5)
return run;
}
(...)
1 | 实现 RunAction2 接口,以便添加到 Run 的 Action 能够恰当地引用 Run。 |
2 | Run 存储在一个瞬态动作中,所以这个字段不会被序列化到磁盘上。 |
3 | 首次将此操作附加到 Run 时设置该字段。 |
4 | 从磁盘加载此操作时设置该字段。 |
5 | 这将使 Run 可用于 Jelly 视图 — 它不能访问私有字段。 |
这些一旦完成之后,我们需要将扩展这个视图来将 Run
的侧面板视图片段 包含 进来:
(...)
<l:layout title="Greeting">
<l:side-panel> (1)
<st:include page="sidepanel.jelly" it="${it.run}" optional="true" /> (2)
</l:side-panel>
<l:main-panel>
(...)
</l:main-panel>
</l:layout>
(...)
1 | 与 main-panel 类似,我们希望内容仅在侧面板中显示,因此我们需要将它们包裹在此元素中。 |
2 | 在这个位置 includes 另一个对象 Run 的视图片段 sidepanel.jelly 。 我们把它标记为可选的,所以如果这个视图片断不存在,就不会显示错误,因为抽象类 Run 没有定义这样的视图,只有它的子类 AbstractBuild 。 |
有了这些修改,我们创建的视图与 Jenkins UI 正确集成,与构建版本相关的内置页面没有任何区别:
恭喜,您已成功创建并大幅扩展了 Jenkins 插件!
故障排除
没有适合你的东西? 在 IRC或 jenkinsci-dev 邮件列表中寻求帮助。 |