1. 基础概念与客户端初始化
核心类:JenkinsClient
使用建造者模式构建,支持:
endPoint:Jenkins 地址,默认http://127.0.0.1:8080credentials:格式username:apiToken(推荐使用 ApiToken,不要直接填密码)jcloudsProperties:底层 jclouds 超时、代理等配置build():生成客户端实例,线程安全,可以全局单例
⚠️ Jenkins 开启 CSRF(crumb)时,库内部自动获取并携带 crumb,业务代码无需手动处理。
import io.github.cdancy.jenkins.rest.JenkinsClient;
import java.util.Properties;
public class JenkinsInit {
public static JenkinsClient getClient() {
Properties props = new Properties();
// 设置连接超时、读取超时(毫秒)
props.setProperty("jclouds.connect-timeout", "10000");
props.setProperty("jclouds.so-timeout", "30000");
JenkinsClient client = JenkinsClient.builder()
.endPoint("http://127.0.0.1:8080")
.credentials("admin:1142ea7ea6919fadad76b2e0057ec01e17")
.jcloudsProperties(props)
.build();
return client;
}
}获取各个 API 入口:
JenkinsClient client = JenkinsInit.getClient();
client.api().systemApi();
client.api().jobsApi();
client.api().buildApi();
client.api().queueApi();
client.api().pluginApi();
client.api().crumbApi();
client.api().folderApi();2. SystemApi 系统信息 API
包:
io.github.cdancy.jenkins.rest.api.SystemApi作用:获取 Jenkins 服务基础信息、版本号
表格
| 方法 | 说明 |
|---|---|
systemInfo() | 获取 Jenkins 系统信息对象 SystemInfo,包含 jenkinsVersion、mode、url 等 |
import io.github.cdancy.jenkins.rest.domain.system.SystemInfo;
import io.github.cdancy.jenkins.rest.api.SystemApi;
import io.github.cdancy.jenkins.rest.JenkinsClient;
public class SystemApiDemo {
public static void main(String[] args) {
JenkinsClient client = JenkinsInit.getClient();
SystemApi systemApi = client.api().systemApi();
SystemInfo systemInfo = systemApi.systemInfo();
System.out.println("Jenkins版本:" + systemInfo.jenkinsVersion());
System.out.println("Jenkins Url:" + systemInfo.url());
System.out.println("系统模式:" + systemInfo.mode());
}
}返回字段解读:
jenkinsVersion:Jenkins 核心版本号url:Jenkins 根地址mode:运行模式slaveAgentPort:代理端口
3. JobsApi 任务 Job 核心 API
包:
io.github.cdancy.jenkins.rest.api.JobsApi最常用 API,管理 Job:创建、删除、启用禁用、获取配置、触发构建、查询 Job 信息
方法清单
表格
| 方法 | 说明 |
|---|---|
jobInfo(jobName) | 获取 Job 基础信息(是否启用、最后构建号、颜色状态等) |
create(jobName, configXml) | 创建 Job,入参是 Jenkins 标准 config.xml 字符串 |
config(jobName) | 获取 Job 的 config.xml 配置文本 |
updateConfig(jobName, configXml) | 更新 Job 配置 |
delete(jobName) | 删除 Job |
enable(jobName) | 启用 Job |
disable(jobName) | 禁用 Job |
build(jobName) | 触发无参构建,返回 Response<Integer>,value 为 queueId |
buildWithParameters(jobName, Map<String, String> params) | 带参数触发构建 |
lastBuildNumber(jobName) | 获取最后一次构建编号 |
lastBuildTimestamp(jobName) | 获取最后构建时间戳 |
progressiveText(jobName, buildNumber, start) | 获取构建控制台增量日志(流式输出) |
3.1 查询 Job 信息 jobInfo
import io.github.cdancy.jenkins.rest.domain.job.JobInfo;
import io.github.cdancy.jenkins.rest.api.JobsApi;
public class JobInfoDemo {
public static void main(String[] args) {
JenkinsClient client = JenkinsInit.getClient();
JobsApi jobsApi = client.api().jobsApi();
JobInfo jobInfo = jobsApi.jobInfo("demo-job");
System.out.println("Job名称:" + jobInfo.name());
System.out.println("是否禁用:" + jobInfo.disabled());
System.out.println("最后构建号:" + jobInfo.lastBuildNumber());
System.out.println("Job颜色状态:" + jobInfo.color());
// color:blue成功 / red失败 / yellow不稳定 / grey未构建 / disabled禁用
}
}3.2 创建 Job create
Jenkins Job 本质是 xml 配置,你可以先在页面建好 Job,复制
config.xml使用
public class JobCreateDemo {
public static void main(String[] args) {
JenkinsClient client = JenkinsInit.getClient();
JobsApi jobsApi = client.api().jobsApi();
String configXml = """
<?xml version='1.1' encoding='UTF-8'?>
<project>
<actions/>
<description>java jenkins-rest创建的任务</description>
<keepDependencies>false</keepDependencies>
<properties/>
<scm class="hudson.scm.NullSCM"/>
<canRoam>true</canRoam>
<disabled>false</disabled>
<blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
<blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
<triggers/>
<concurrentBuild>false</concurrentBuild>
<builders/>
<publishers/>
<buildWrappers/>
</project>
""";
var resp = jobsApi.create("new-demo-job", configXml);
if (!resp.errors().isEmpty()) {
resp.errors().forEach(e -> System.err.println(e.message()));
} else {
System.out.println("创建成功");
}
}
}3.3 获取 & 更新 Job 配置
// 获取配置
String xml = jobsApi.config("demo-job");
System.out.println(xml);
// 更新配置
String newXml = xml.replace("<description>old</description>","<description>new desc</description>");
var updateResp = jobsApi.updateConfig("demo-job", newXml);3.4 启用 / 禁用 / 删除 Job
jobsApi.disable("demo-job");
jobsApi.enable("demo-job");
var delResp = jobsApi.delete("demo-job");3.5 触发构建(无参 / 带参)
返回 Response<Integer>,value 是队列 ID(queueId),交给 QueueApi 轮询
// 无参构建
var buildResp = jobsApi.build("demo-job");
Integer queueId = buildResp.value();
System.out.println("队列ID:" + queueId);
// 带参数构建
Map<String, String> params = new HashMap<>();
params.put("env", "test");
params.put("version", "1.0.0");
var paramBuildResp = jobsApi.buildWithParameters("demo-job", params);
Integer paramQueueId = paramBuildResp.value();3.6 获取构建控制台日志 progressiveText
// start=0 从头获取
String log = jobsApi.progressiveText("demo-job", 1, 0);
System.out.println(log);4. BuildApi 构建信息 API
包:
io.github.cdancy.jenkins.rest.api.BuildApi根据构建编号,查询构建详情、构建结果、构建日志
表格
| 方法 | 说明 |
|---|---|
buildInfo(jobName, buildNumber) | 获取构建详情 BuildInfo,结果、耗时、执行人等 |
import io.github.cdancy.jenkins.rest.domain.build.BuildInfo;
import io.github.cdancy.jenkins.rest.api.BuildApi;
public class BuildApiDemo {
public static void main(String[] args) {
JenkinsClient client = JenkinsInit.getClient();
BuildApi buildApi = client.api().buildApi();
BuildInfo buildInfo = buildApi.buildInfo("demo-job", 1);
System.out.println("构建号:" + buildInfo.number());
System.out.println("构建结果:" + buildInfo.result());
// 结果枚举:SUCCESS、FAILURE、UNSTABLE、ABORTED、NOT_BUILT
System.out.println("开始时间:" + buildInfo.timestamp());
System.out.println("耗时ms:" + buildInfo.duration());
System.out.println("是否正在构建:" + buildInfo.building());
}
}5. QueueApi 构建队列 API
包:
io.github.cdancy.jenkins.rest.api.QueueApi核心场景:触发构建拿到 queueId 后,轮询队列,拿到真实 buildNumber
表格
| 方法 | 说明 |
|---|---|
queueItem(queueId) | 查询队列项,返回 QueueItem,判断是否取消、是否已经开始构建 |
cancel(queueId) | 取消排队中的任务 |
list() | 获取当前 Jenkins 全部排队任务列表 |
QueueItem 关键字段:
cancelled():是否取消executable():如果已经开始执行,executable 里面包含number(buildNumber)why():排队原因(资源不足、并发限制等)
import io.github.cdancy.jenkins.rest.domain.queue.QueueItem;
import io.github.cdancy.jenkins.rest.api.QueueApi;
public class QueueApiDemo {
public static void main(String[] args) throws InterruptedException {
JenkinsClient client = JenkinsInit.getClient();
QueueApi queueApi = client.api().queueApi();
Integer queueId = 123; // 来自jobsApi.build返回的queueId
while (true) {
QueueItem queueItem = queueApi.queueItem(queueId);
if (queueItem.cancelled()) {
System.out.println("任务被取消");
break;
}
if (queueItem.executable() != null) {
// 已经调度成功,拿到buildNumber
Integer buildNumber = queueItem.executable().number();
System.out.println("构建编号:" + buildNumber);
break;
}
System.out.println("排队中,原因:" + queueItem.why());
Thread.sleep(1000);
}
// 取消队列任务
// queueApi.cancel(queueId);
}
}6. PluginApi 插件管理 API
包:
io.github.cdancy.jenkins.rest.api.PluginApi
表格
| 方法 | 说明 |
|---|---|
plugins() | 获取已安装插件列表 Plugins,包含插件 id、版本、是否启用 |
installNecessaryPlugins(String pluginList) | 批量安装插件,格式 pluginId@version,pluginId@version |
import io.github.cdancy.jenkins.rest.domain.plugin.Plugins;
import io.github.cdancy.jenkins.rest.api.PluginApi;
public class PluginApiDemo {
public static void main(String[] args) {
JenkinsClient client = JenkinsInit.getClient();
PluginApi pluginApi = client.api().pluginApi();
Plugins plugins = pluginApi.plugins();
plugins.plugins().forEach(p -> {
System.out.println("插件:" + p.shortName() + " 版本:" + p.version() + " 启用:" + p.enabled());
});
// 安装插件:maven-plugin@3.20
var resp = pluginApi.installNecessaryPlugins("maven-plugin@3.20");
if (!resp.errors().isEmpty()) {
resp.errors().forEach(e->System.err.println(e.message()));
}
}
}⚠️ 安装插件后通常需要重启 Jenkins 才能生效。
7. CrumbApi 防跨域令牌 API
包:
io.github.cdancy.jenkins.rest.api.CrumbApi库内部默认自动调用该接口获取 crumb,业务一般不需要手动调用; 仅当你需要自己拼接原生 http 请求时使用。
表格
| 方法 | 说明 |
|---|---|
crumb() | 获取 Crumb 对象,包含 crumbValue 和 crumbRequestField |
import io.github.cdancy.jenkins.rest.domain.crumb.Crumb;
import io.github.cdancy.jenkins.rest.api.CrumbApi;
public class CrumbApiDemo {
public static void main(String[] args) {
JenkinsClient client = JenkinsInit.getClient();
CrumbApi crumbApi = client.api().crumbApi();
Crumb crumb = crumbApi.crumb();
System.out.println("crumb header key:" + crumb.crumbRequestField());
System.out.println("crumb value:" + crumb.crumbValue());
}
}8. FolderApi 文件夹 Job API
包:
io.github.cdancy.jenkins.rest.api.FolderApiJenkins Folder 插件支持文件夹分类 Job,jobName 使用/分隔路径,例如folderA/jobB
表格
| 方法 | 说明 |
|---|---|
create(folderName, configXml) | 创建文件夹 |
config(folderName) | 获取文件夹 xml 配置 |
updateConfig(folderName, configXml) | 更新文件夹配置 |
public class FolderApiDemo {
public static void main(String[] args) {
JenkinsClient client = JenkinsInit.getClient();
var folderApi = client.api().folderApi();
String folderXml = """
<?xml version='1.1' encoding='UTF-8'?>
<com.cloudbees.hudson.plugins.folder.Folder>
<actions/>
<description>测试文件夹</description>
<properties/>
<displayName>测试目录</displayName>
</com.cloudbees.hudson.plugins.folder.Folder>
""";
var resp = folderApi.create("test-folder", folderXml);
}
}文件夹内 Job 操作:JobsApi 的 jobName 参数传
test-folder/sub-job
9. 通用返回对象(Response、Errors)解读
所有写操作(create/build/delete 等)返回 Response<T>
public interface Response<T> {
T value();
List<Error> errors();
}value():成功时返回主体对象;失败可能为 nullerrors():错误列表,不为空代表请求异常
for (Error error : resp.errors()) {
System.err.println("错误信息:" + error.message());
System.err.println("错误上下文:" + error.context());
}✅ 判断标准:
resp.errors().isEmpty()代表请求成功,不要只判断 value!=null
10. 常见坑与最佳实践
- 优先使用 ApiToken,不要使用账号密码:用户设置 → API Token 生成
- Jenkins 开启 CSRF:库自动处理 crumb,无需手动编码
- Job 名称包含中文、空格:Jenkins 原生支持,但 url 编码由底层库自动处理
- 并发构建:Job 配置里勾选 “并发构建”,否则排队
- 文件夹 Job 依赖:必须安装
CloudBees Folder插件 - 超时:生产环境务必配置 jclouds connect-timeout /so-timeout
- 权限:账号至少需要 Job Read、Build、Create、Delete 权限
- pipeline Job 和普通 Job 操作无区别,只是 config.xml 结构不同
progressiveText增量日志:start 参数传上次返回的末尾位置,适合实时日志
11. 完整可运行 Demo 类
import io.github.cdancy.jenkins.rest.JenkinsClient;
import io.github.cdancy.jenkins.rest.api.BuildApi;
import io.github.cdancy.jenkins.rest.api.JobsApi;
import io.github.cdancy.jenkins.rest.api.QueueApi;
import io.github.cdancy.jenkins.rest.domain.build.BuildInfo;
import io.github.cdancy.jenkins.rest.domain.job.JobInfo;
import io.github.cdancy.jenkins.rest.domain.queue.QueueItem;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public class JenkinsRestFullDemo {
private static JenkinsClient client;
static {
Properties props = new Properties();
props.setProperty("jclouds.connect-timeout", "10000");
props.setProperty("jclouds.so-timeout", "30000");
client = JenkinsClient.builder()
.endPoint("http://127.0.0.1:8080")
.credentials("admin:你的apitoken")
.jcloudsProperties(props)
.build();
}
public static void main(String[] args) throws InterruptedException {
JobsApi jobsApi = client.api().jobsApi();
QueueApi queueApi = client.api().queueApi();
BuildApi buildApi = client.api().buildApi();
String jobName = "demo-job";
// 1. 查询job信息
JobInfo jobInfo = jobsApi.jobInfo(jobName);
System.out.println("Job状态:" + jobInfo.color());
// 2. 触发带参构建
Map<String, String> params = new HashMap<>();
params.put("env", "test");
var buildResp = jobsApi.buildWithParameters(jobName, params);
if (!buildResp.errors().isEmpty()) {
buildResp.errors().forEach(e -> System.err.println(e.message()));
return;
}
Integer queueId = buildResp.value();
System.out.println("队列ID:" + queueId);
// 3. 轮询队列拿到buildNumber
Integer buildNumber = null;
while (true) {
QueueItem queueItem = queueApi.queueItem(queueId);
if (queueItem.cancelled()) {
System.out.println("任务取消");
return;
}
if (queueItem.executable() != null) {
buildNumber = queueItem.executable().number();
System.out.println("构建编号:" + buildNumber);
break;
}
System.out.println("排队原因:" + queueItem.why());
Thread.sleep(1000);
}
// 4. 查询构建结果
BuildInfo buildInfo = buildApi.buildInfo(jobName, buildNumber);
System.out.println("构建结果:" + buildInfo.result());
System.out.println("耗时:" + buildInfo.duration() + "ms");
}
}