自定义插件运行参数
jenkins的插件常规的运行方式是使用mvn hpi:run
方法运行的。其有以下主要参数
-Djetty.port
: 插件运行的端口号
-DJENKINS_HOME
: jenkins的数据目录
自定义插件修改jenkins的ContextPath
如果是通过以下方式进行插件开发(以org.jenkins-ci.plugin plugin
作为父的maven)
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>3.4</version>
<relativePath />
</parent>
可通过修改pom.xml
文件来达到自定义jenkins的ContextPath。
<build>
<plugins>
<plugin>
<groupId>org.jenkins-ci.tools</groupId>
<artifactId>maven-hpi-plugin</artifactId>
<extensions>true</extensions>
<configuration combine.self="override">
<showDeprecation>true</showDeprecation>
<contextPath>/</contextPath>
<!-- TODO specify ${java.level} when JENKINS-20679 implemented -->
<systemProperties>
<hudson.Main.development>${hudson.Main.development}</hudson.Main.development>
</systemProperties>
</configuration>
</plugin>
</plugins>
</build>
快速定位插件
方法一、通过前端元素来定位属于哪个插件
如需要知道以下功能是哪个插件提供的。可通过浏览器的开发者模式(f12)查看红圈的元素,即可知道该功能是由哪个插件提供的。
只要前端页面带小问号图标的功能都可以进行查看
自定义插件引入jar包(非maven的)
jenkins插件如果通过常规的方式引入jar包(非maven)是行不通的。需要通过maven的plugin来实现jar包的拷贝。
如需引入存放在项目lib目录下的kcbpop_1.0.3-R.jar
可通过修改pom.xml
<dependencies>
<dependency>
<groupId>com.szkingdom.kcbp</groupId>
<artifactId>kcbpop</artifactId>
<version>1.0.3-R</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/kcbpop_1.0.3-R.jar</systemPath>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
<executions>
<execution>
<id>copy-resources</id>
<!-- here the phase you need -->
<phase>validate</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/${project.build.finalName}/WEB-INF/lib</outputDirectory>
<resources>
<resource>
<directory>lib</directory>
<filtering>false</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
扩展点
添加定时执行任务
可通过继承PeriodicWork
来实现定时任务,其通过getRecurrencePeriod
方法返回两次开始执行最小间隔时间、getInitialDelay
方法返回初始化延时、doRun
为执行的函数。例如
@Extension
@Symbol("PushFailBuildLogWorker")
public static class PushFailBuildLogWorker extends PeriodicWork{
public long getRecurrencePeriod() {
return DAY;
}
protected void doRun() {
doPushBuildLogs();
}
}
job运行监听
通过继承RunListener
来实现监听,job开始运行的时候会调用onStarted
方法,结束的时候会调用onCompleted
方法。例如
@Extension
public class StatsJobRunListener extends RunListener<Run< ?, ?>>{
private static final Logger LOGGER = Logger.getLogger(StatsJobRunListener.class.getName());
//开始执行流水线的时候
public void onStarted(Run run, TaskListener listener){ }
//流水线结束执行的时候
@Override
public void onCompleted(Run run, TaskListener listener){
super.onCompleted(run, listener);
}
}
workflow的步骤
可通过继承Step
类来实现,如以下代码实现echo
的功能
package com.szkingdom.kt.wEcho.step;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.Extension;
import hudson.model.TaskListener;
import java.io.PrintStream;
import java.util.Collections;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.jenkinsci.plugins.workflow.actions.LabelAction;
import org.jenkinsci.plugins.workflow.graph.FlowNode;
import org.jenkinsci.plugins.workflow.steps.Step;
import org.jenkinsci.plugins.workflow.steps.StepContext;
import org.jenkinsci.plugins.workflow.steps.StepDescriptor;
import org.jenkinsci.plugins.workflow.steps.StepExecution;
import org.jenkinsci.plugins.workflow.steps.SynchronousStepExecution;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
public class wEchoStep
extends Step
{
private static final int MAX_LABEL_LENGTH = 100;
private final String message;
private String label;
@DataBoundConstructor
public wEchoStep(String message)
{
this.message = message;
}
@DataBoundSetter
public void setLabel(String label)
{
this.label = label;
}
public String getLabel()
{
return this.label;
}
public String getMessage()
{
return this.message;
}
public StepExecution start(StepContext context)
throws Exception
{
if ((this.label != null) && (!this.label.isEmpty())) {
((FlowNode)context.get(FlowNode.class)).addAction(new LabelAction(StringUtils.left(this.label, 100)));
}
return new Execution(this.message, context);
}
@Extension
public static class DescriptorImpl
extends StepDescriptor
{
public String getFunctionName()
{
return "wEcho";
}
public String getDisplayName()
{
return "输出";
}
public Set<? extends Class<?>> getRequiredContext()
{
return Collections.singleton(TaskListener.class);
}
}
public static class Execution
extends SynchronousStepExecution<Void>
{
@SuppressFBWarnings(value={"SE_TRANSIENT_FIELD_NOT_RESTORED"}, justification="Only used when starting.")
private final transient String message;
private static final long serialVersionUID = 1L;
Execution(String message, StepContext context)
{
super();
this.message = message;
}
protected Void run()
throws Exception
{
((TaskListener)getContext().get(TaskListener.class)).getLogger().println(this.message);
return null;
}
}
}
主要知识点:
StepDescriptor
的继承类:表示该步骤相关的信息,其getFunctionName
方法设置使用该步骤的名称,getDisplayName
方法返回该步骤显示的名称、getRequiredContext
方法可设置执行该步骤所需要的内容。
SynchronousStepExecution
的继承类: 为该步骤执行的类,run
方法为执行到该步骤实现的内容。
**Step
的继承类:**为步骤的主类,其需要设置该步骤运行需要的参数(可通过在构造方法上添加@DataBoundConstructor
或在某字段的set方法上添加@DataBoundSetter
方法)。还需要重写start
方法返回对应的执行类(即SynchronousStepExecution
的继承类)
设置插件不可使用
jenkins数据目录下的plugins
文件夹为插件目录。
如果想不使用某个插件可以通过在pulgins
目录下添加插件完整名称.disabled
文件。
例如禁用插件filesystem_scm.hpi
。可以通过添加空的filesystem_scm.hpi.disabled
文件。
对应的jenkins核心代码如下
@Override public PluginWrapper createPluginWrapper(File archive) throws IOException {
...
File disableFile = new File(archive.getPath() + ".disabled");
if (disableFile.exists()) {
LOGGER.info("Plugin " + archive.getName() + " is disabled");
}
...
return new PluginWrapper(pluginManager, archive, manifest, baseResourceURL,
createClassLoader(paths, dependencyLoader, atts), disableFile, dependencies, optionalDependencies);
}
/**
* @param archive
* A .jpi archive file jar file, or a .jpl linked plugin.
* @param manifest
* The manifest for the plugin
* @param baseResourceURL
* A URL pointing to the resources for this plugin
* @param classLoader
* a classloader that loads classes from this plugin and its dependencies
* @param disableFile
* if this file exists on startup, the plugin will not be activated
* @param dependencies a list of mandatory dependencies
* @param optionalDependencies a list of optional dependencies
*/
public PluginWrapper(PluginManager parent, File archive, Manifest manifest, URL baseResourceURL,
ClassLoader classLoader, File disableFile,
List<Dependency> dependencies, List<Dependency> optionalDependencies) {
this.parent = parent;
this.manifest = manifest;
this.shortName = Util.intern(computeShortName(manifest, archive.getName()));
this.baseResourceURL = baseResourceURL;
this.classLoader = classLoader;
this.disableFile = disableFile;
this.active = !disableFile.exists();
this.dependencies = dependencies;
this.optionalDependencies = optionalDependencies;
for (Dependency d : optionalDependencies) {
assert d.optional : d + " included among optionalDependencies of " + shortName + " but was not marked optional";
}
this.archive = archive;
}