Skip to content

1. 基础概念与客户端初始化 ​

核心类:JenkinsClient ​

使用建造者模式构建,支持:

  • endPoint:Jenkins 地址,默认 http://127.0.0.1:8080
  • credentials:格式 username:apiToken(推荐使用 ApiToken,不要直接填密码)
  • jcloudsProperties:底层 jclouds 超时、代理等配置
  • build():生成客户端实例,线程安全,可以全局单例

⚠️ Jenkins 开启 CSRF(crumb)时,库内部自动获取并携带 crumb,业务代码无需手动处理。

java
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 入口:

java
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 等
java
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 ​

java
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 使用

java
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 配置 ​

java
// 获取配置
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 ​

java
jobsApi.disable("demo-job");
jobsApi.enable("demo-job");
var delResp = jobsApi.delete("demo-job");

3.5 触发构建(无参 / 带参) ​

返回 Response<Integer>,value 是队列 ID(queueId),交给 QueueApi 轮询

java
// 无参构建
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,结果、耗时、执行人等
java
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():排队原因(资源不足、并发限制等)
java
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
java
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
java
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.FolderApi Jenkins Folder 插件支持文件夹分类 Job,jobName 使用 / 分隔路径,例如 folderA/jobB

表格

方法说明
create(folderName, configXml)创建文件夹
config(folderName)获取文件夹 xml 配置
updateConfig(folderName, configXml)更新文件夹配置
java
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>

java
public interface Response<T> {
    T value();
    List<Error> errors();
}
  • value():成功时返回主体对象;失败可能为 null
  • errors():错误列表,不为空代表请求异常
java
for (Error error : resp.errors()) {
    System.err.println("错误信息:" + error.message());
    System.err.println("错误上下文:" + error.context());
}

✅ 判断标准:resp.errors().isEmpty() 代表请求成功,不要只判断 value!=null


10. 常见坑与最佳实践 ​

  1. 优先使用 ApiToken,不要使用账号密码:用户设置 → API Token 生成
  2. Jenkins 开启 CSRF:库自动处理 crumb,无需手动编码
  3. Job 名称包含中文、空格:Jenkins 原生支持,但 url 编码由底层库自动处理
  4. 并发构建:Job 配置里勾选 “并发构建”,否则排队
  5. 文件夹 Job 依赖:必须安装 CloudBees Folder 插件
  6. 超时:生产环境务必配置 jclouds connect-timeout /so-timeout
  7. 权限:账号至少需要 Job Read、Build、Create、Delete 权限
  8. pipeline Job 和普通 Job 操作无区别,只是 config.xml 结构不同
  9. progressiveText 增量日志:start 参数传上次返回的末尾位置,适合实时日志

11. 完整可运行 Demo 类 ​

java
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");
    }
}