diff --git a/README.md b/README.md
index 30cbb60..ada9b9f 100644
--- a/README.md
+++ b/README.md
@@ -12,8 +12,9 @@ Build a default client:
Build a customized client:
```
DiggerClient client = DiggerClient.builder()
- .createJobService(new CreateJobService())
- .triggerBuildService(new TriggerBuildService(10000, 100))
+ .jobService(new JobService())
+ .triggerBuildService(new BuildService(10000, 100))
+ .artifactsService(artifactsService)
.withAuth("https://digger.com", "admin", "password")
.build();
```
diff --git a/pom.xml b/pom.xml
index 0d09fa4..e30ff9d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,11 +4,21 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
- groupId
+ org.jboss.aerogear
jenkins-client
1.0.0
jar
+
+ 4.11
+ 1.9.5
+ 0.3.7-SNAPSHOT
+ 5.65
+ 1.7.21
+ 1.7.21
+ 3.6.1
+
+
Apache License, Version 2.0
@@ -22,42 +32,41 @@
com.offbytwo.jenkins
jenkins-client
- 0.3.7-SNAPSHOT
+ ${jenkins.client.version}
org.jtwig
jtwig-core
- 5.65
+ ${jtwig.templates.version}
org.slf4j
slf4j-api
- 1.7.21
+ ${slf4j.api.version}
org.slf4j
slf4j-log4j12
- 1.7.21
+ ${slf4j-log4j12.version}
junit
junit
- 4.12
+ ${junit.version}
test
-
org.mockito
mockito-core
- 1.10.19
+ ${mockito-core.version}
test
org.assertj
assertj-core
- 3.6.1
+ ${assertj-core.version}
test
diff --git a/src/main/java/com/redhat/digkins/DiggerClient.java b/src/main/java/com/redhat/digkins/DiggerClient.java
index f4edc49..abaf8ba 100644
--- a/src/main/java/com/redhat/digkins/DiggerClient.java
+++ b/src/main/java/com/redhat/digkins/DiggerClient.java
@@ -2,14 +2,17 @@
import com.offbytwo.jenkins.JenkinsServer;
import com.redhat.digkins.model.BuildStatus;
-import com.redhat.digkins.services.CreateJobService;
+import com.redhat.digkins.services.ArtifactsService;
+import com.redhat.digkins.services.JobService;
import com.redhat.digkins.services.TriggerBuildService;
import com.redhat.digkins.util.DiggerClientException;
import com.redhat.digkins.util.JenkinsAuth;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
@@ -24,8 +27,9 @@ public class DiggerClient {
private JenkinsServer jenkinsServer;
- private CreateJobService createJobService;
+ private JobService jobService;
private TriggerBuildService triggerBuildService;
+ private ArtifactsService artifactsService;
private DiggerClient() {
}
@@ -42,9 +46,13 @@ private DiggerClient() {
* @throws DiggerClientException if something goes wrong
*/
public static DiggerClient createDefaultWithAuth(String url, String user, String password) throws DiggerClientException {
+ TriggerBuildService triggerBuildService = new TriggerBuildService(TriggerBuildService.DEFAULT_FIRST_CHECK_DELAY, TriggerBuildService.DEFAULT_POLL_PERIOD);
+ JobService jobService = new JobService();
+ ArtifactsService artifactsService = new ArtifactsService();
return DiggerClient.builder()
- .createJobService(new CreateJobService())
- .triggerBuildService(new TriggerBuildService(TriggerBuildService.DEFAULT_FIRST_CHECK_DELAY, TriggerBuildService.DEFAULT_POLL_PERIOD))
+ .createJobService(jobService)
+ .triggerBuildService(triggerBuildService)
+ .artifactsService(artifactsService)
.withAuth(url, user, password)
.build();
}
@@ -52,19 +60,19 @@ public static DiggerClient createDefaultWithAuth(String url, String user, String
public static DiggerClientBuilder builder() {
return new DiggerClientBuilder();
}
-
public static class DiggerClientBuilder {
private JenkinsAuth auth;
- private CreateJobService createJobService;
+ private JobService jobService;
private TriggerBuildService triggerBuildService;
+ private ArtifactsService artifactsService;
public DiggerClientBuilder withAuth(String url, String user, String password) {
this.auth = new JenkinsAuth(url, user, password);
return this;
}
- public DiggerClientBuilder createJobService(CreateJobService createJobService) {
- this.createJobService = createJobService;
+ public DiggerClientBuilder createJobService(JobService jobService) {
+ this.jobService = jobService;
return this;
}
@@ -73,12 +81,18 @@ public DiggerClientBuilder triggerBuildService(TriggerBuildService triggerBuildS
return this;
}
+ public DiggerClientBuilder artifactsService(ArtifactsService artifactsService) {
+ this.artifactsService = artifactsService;
+ return this;
+ }
+
public DiggerClient build() throws DiggerClientException {
final DiggerClient client = new DiggerClient();
try {
client.jenkinsServer = new JenkinsServer(new URI(auth.getUrl()), auth.getUser(), auth.getPassword());
- client.createJobService = this.createJobService;
+ client.jobService = this.jobService;
client.triggerBuildService = this.triggerBuildService;
+ client.artifactsService = this.artifactsService;
return client;
} catch (URISyntaxException e) {
throw new DiggerClientException("Invalid jenkins url format.");
@@ -97,7 +111,7 @@ public DiggerClient build() throws DiggerClientException {
*/
public void createJob(String name, String gitRepo, String gitBranch) throws DiggerClientException {
try {
- createJobService.create(this.jenkinsServer, name, gitRepo, gitBranch);
+ jobService.create(this.jenkinsServer, name, gitRepo, gitBranch);
} catch (Throwable e) {
throw new DiggerClientException(e);
}
@@ -153,4 +167,32 @@ public BuildStatus build(String jobName) throws DiggerClientException {
return this.build(jobName, DEFAULT_BUILD_TIMEOUT);
}
+ /**
+ * Fetch artifacts urls for specific job and build number
+ *
+ * @param jobName name of the job
+ * @param buildNumber job build number
+ * @param artifactName - name of the artifact to fetch - can be regexp
+ * @return InputStream with file contents
+ * @throws DiggerClientException - when problem with fetching artifacts from jenkins
+ */
+ public InputStream fetchArtifact(String jobName, int buildNumber, String artifactName) throws DiggerClientException {
+ return artifactsService.streamArtifact(jenkinsServer,jobName, buildNumber, artifactName);
+ }
+
+ /**
+ * Save artifact for specified location for specific job, build number and artifact name.
+ * If name would be an regular expression method would return stream for the first match.
+ *
+ * @param jobName name of the job
+ * @param buildNumber job build number
+ * @param artifactName name of the artifact to fetch - can be regexp for example *.apk
+ * @param outputFile file (location) used to save artifact
+ *
+ * @throws DiggerClientException when problem with fetching artifacts from jenkins
+ * @throws IOException when one of the files cannot be saved
+ */
+ public void saveArtifact(String jobName, int buildNumber, String artifactName, File outputFile) throws DiggerClientException, IOException {
+ artifactsService.saveArtifact(jenkinsServer,jobName, buildNumber, artifactName,outputFile);
+ }
}
diff --git a/src/main/java/com/redhat/digkins/Main.java b/src/main/java/com/redhat/digkins/Main.java
new file mode 100644
index 0000000..7800df4
--- /dev/null
+++ b/src/main/java/com/redhat/digkins/Main.java
@@ -0,0 +1,36 @@
+package com.redhat.digkins;
+
+import com.redhat.digkins.services.JobService;
+import com.redhat.digkins.services.TriggerBuildService;
+import com.redhat.digkins.util.DiggerClientException;
+import org.apache.commons.io.IOUtils;
+
+import java.io.*;
+
+/**
+ */
+public class Main {
+
+ public static void main(String[] args) throws DiggerClientException, IOException {
+ DiggerClient client = DiggerClient.builder()
+ .createJobService(new JobService())
+ .triggerBuildService(new TriggerBuildService(10000, 100))
+ .withAuth("https://jenkins-digger2.osm3.feedhenry.net", "admin", "Vu8ysYH5f2dJiLgL")
+ .build();
+ //client.createJob("wtr-java-tests2", "https://github.com/wtrocki/helloworld-android-gradle", "master");
+ //client.build("wtr-java-tests2");
+ InputStream inputStream = client.fetchArtifact("wtr-java-tests2", 1, "app-debug.apk");
+ if(inputStream != null){
+ File targetFile = new File("artifact.tmp");
+ OutputStream outStream = new FileOutputStream(targetFile);
+
+ byte[] buffer = new byte[8 * 1024];
+ int bytesRead;
+ while ((bytesRead = inputStream.read(buffer)) != -1) {
+ outStream.write(buffer, 0, bytesRead);
+ }
+ IOUtils.closeQuietly(inputStream);
+ IOUtils.closeQuietly(outStream);
+ }
+ }
+}
diff --git a/src/main/java/com/redhat/digkins/services/ArtifactsService.java b/src/main/java/com/redhat/digkins/services/ArtifactsService.java
new file mode 100644
index 0000000..68efca8
--- /dev/null
+++ b/src/main/java/com/redhat/digkins/services/ArtifactsService.java
@@ -0,0 +1,94 @@
+package com.redhat.digkins.services;
+
+import com.offbytwo.jenkins.JenkinsServer;
+import com.offbytwo.jenkins.model.Artifact;
+import com.offbytwo.jenkins.model.Build;
+import com.offbytwo.jenkins.model.BuildWithDetails;
+import com.offbytwo.jenkins.model.JobWithDetails;
+import com.redhat.digkins.util.DiggerClientException;
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.*;
+import java.net.URISyntaxException;
+import java.util.List;
+
+/**
+ * Service used to retrieve artifacts
+ */
+public class ArtifactsService {
+
+ /**
+ */
+ public ArtifactsService() {
+ }
+
+ private static final Logger LOG = LoggerFactory.getLogger(ArtifactsService.class);
+ private static int DEFAULT_BUFFER = 8 * 1024;
+
+ /**
+ * Save artifact for specified location for specific job, build number and artifact name.
+ * If name would be an regular expression method would return stream for the first match.
+ *
+ * @param jobName name of the job
+ * @param buildNumber job build number
+ * @param artifactName name of the artifact to fetch - can be regexp for example *.apk
+ * @param outputFile file (location) used to save artifact
+ * @throws DiggerClientException when problem with fetching artifacts from jenkins
+ * @throws IOException when one of the files cannot be saved
+ */
+ public void saveArtifact(JenkinsServer jenkins, String jobName, int buildNumber, String artifactName, File outputFile) throws DiggerClientException, IOException {
+ InputStream inputStream = streamArtifact(jenkins, jobName, buildNumber, artifactName);
+ if (inputStream != null) {
+ OutputStream outStream = new FileOutputStream(outputFile);
+ byte[] buffer = new byte[DEFAULT_BUFFER];
+ int bytesRead;
+ while ((bytesRead = inputStream.read(buffer)) != -1) {
+ outStream.write(buffer, 0, bytesRead);
+ }
+ IOUtils.closeQuietly(inputStream);
+ IOUtils.closeQuietly(outStream);
+ } else {
+ throw new DiggerClientException("Cannot fetch artifacts from jenkins");
+ }
+ }
+
+ /**
+ * Get artifact inputstream for specific job, build number and artifact name.
+ * If name would be an regular expression method would return stream for the first match.
+ *
+ * @param jobName name of the job
+ * @param buildNumber job build number
+ * @param artifactName name of the artifact to fetch - can be regexp for example *.apk
+ * @return InputStream with file contents that can be saved or piped to socket
+ * @throws DiggerClientException when problem with fetching artifacts from jenkins
+ */
+ public InputStream streamArtifact(JenkinsServer jenkins, String jobName, int buildNumber, String artifactName) throws DiggerClientException {
+ try {
+ JobWithDetails job = jenkins.getJob(jobName);
+ if (job == null) {
+ LOG.error("Cannot fetch job from jenkins {0}", jobName);
+ throw new DiggerClientException("Cannot fetch job from jenkins");
+ }
+ Build build = job.getBuildByNumber(buildNumber);
+ BuildWithDetails buildWithDetails = build.details();
+ List artifacts = buildWithDetails.getArtifacts();
+ for (Artifact artifact : artifacts) {
+ if (artifact.getFileName().matches(artifactName)) {
+ LOG.debug("Streaming artifact {0}", artifactName);
+ return buildWithDetails.downloadArtifact(artifact);
+ }
+ }
+ } catch (URISyntaxException e) {
+ LOG.error("Invalid job name {0}", jobName, e);
+ throw new DiggerClientException(e);
+ } catch (IOException e) {
+ LOG.error("Problem when fetching artifacts for {0} {1} {2}", jobName, buildNumber, artifactName, e);
+ throw new DiggerClientException(e);
+ }
+ LOG.debug("Cannot find build for ", jobName, buildNumber, artifactName);
+ return null;
+ }
+
+}
diff --git a/src/main/java/com/redhat/digkins/services/CreateJobService.java b/src/main/java/com/redhat/digkins/services/JobService.java
similarity index 97%
rename from src/main/java/com/redhat/digkins/services/CreateJobService.java
rename to src/main/java/com/redhat/digkins/services/JobService.java
index 1d658bd..521fc1d 100644
--- a/src/main/java/com/redhat/digkins/services/CreateJobService.java
+++ b/src/main/java/com/redhat/digkins/services/JobService.java
@@ -9,11 +9,10 @@
/**
* Create digger job on jenkins platform
*/
-public class CreateJobService {
+public class JobService {
private final static String GIT_REPO_URL = "GIT_REPO_URL";
private final static String GIT_REPO_BRANCH = "GIT_REPO_BRANCH";
-
private static final String JOB_TEMPLATE_PATH = "templates/job.xml";
/**
@@ -29,5 +28,4 @@ public void create(JenkinsServer jenkinsServer, String name, String gitRepo, Str
JtwigModel model = JtwigModel.newModel().with(GIT_REPO_URL, gitRepo).with(GIT_REPO_BRANCH, gitBranch);
jenkinsServer.createJob(name, template.render(model));
}
-
}
diff --git a/src/test/java/com/redhat/digkins/services/ArtifactsServiceTests.java b/src/test/java/com/redhat/digkins/services/ArtifactsServiceTests.java
new file mode 100644
index 0000000..59d4fc2
--- /dev/null
+++ b/src/test/java/com/redhat/digkins/services/ArtifactsServiceTests.java
@@ -0,0 +1,87 @@
+package com.redhat.digkins.services;
+
+import com.offbytwo.jenkins.JenkinsServer;
+import com.offbytwo.jenkins.model.*;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.Arrays;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ArtifactsServiceTests {
+
+ @Mock
+ private JenkinsServer server;
+ private ArtifactsService artifactsService;
+
+ @Before
+ public void beforeTests() {
+ artifactsService = new ArtifactsService();
+ }
+
+ @Test
+ public void shouldFetchArtifactsWithNoResults() throws Exception {
+ BuildWithDetails build = mock(BuildWithDetails.class);
+ JobWithDetails job = mock(JobWithDetails.class);
+ when(server.getJob(anyString())).thenReturn(job);
+ when(job.getBuildByNumber(anyInt())).thenReturn(build);
+ when(build.details()).thenReturn(build);
+ InputStream artifact = artifactsService.streamArtifact(server, "artifact", 1, "test");
+
+ assertNull(artifact);
+ }
+
+ @Test
+ public void shouldFetchArtifactsWithResults() throws Exception {
+ Artifact artifact = mock(Artifact.class);
+ JobWithDetails job = mock(JobWithDetails.class);
+ BuildWithDetails build = mock(BuildWithDetails.class);
+ FileInputStream fs = mock(FileInputStream.class);
+ when(server.getJob(anyString())).thenReturn(job);
+ when(job.getBuildByNumber(anyInt())).thenReturn(build);
+ when(build.details()).thenReturn(build);
+ when(build.getArtifacts()).thenReturn(Arrays.asList(artifact));
+ when(build.downloadArtifact(artifact)).thenReturn(fs);
+ String artifactName = "test";
+ when(artifact.getFileName()).thenReturn(artifactName);
+ InputStream artifactStream = artifactsService.streamArtifact(server, "artifact", 1, artifactName);
+
+ assertThat(artifactStream).isNotNull();
+ }
+
+
+ @Test
+ public void shouldSaveFileArtifactWithResults() throws Exception {
+ Artifact artifact = mock(Artifact.class);
+ FileInputStream fs = mock(FileInputStream.class);
+ JobWithDetails job = mock(JobWithDetails.class);
+ BuildWithDetails build = mock(BuildWithDetails.class);
+ String artifactName = "test";
+ when(server.getJob(anyString())).thenReturn(job);
+ when(job.getBuildByNumber(anyInt())).thenReturn(build);
+ when(build.details()).thenReturn(build);
+ when(build.getArtifacts()).thenReturn(Arrays.asList(artifact));
+ when(artifact.getFileName()).thenReturn(artifactName);
+ when(build.downloadArtifact(artifact)).thenReturn(fs);
+ when(fs.read(Matchers.anyObject())).thenReturn(-1);
+ when(build.details()).thenReturn(build);
+ File outputFile = new File("test.out");
+ artifactsService.saveArtifact(server, "artifact", 1, artifactName, outputFile);
+
+ assertThat(outputFile.exists()).isTrue();
+ outputFile.delete();
+ }
+}
diff --git a/src/test/java/com/redhat/digkins/services/JobServiceTests.java b/src/test/java/com/redhat/digkins/services/JobServiceTests.java
new file mode 100644
index 0000000..96bc0cc
--- /dev/null
+++ b/src/test/java/com/redhat/digkins/services/JobServiceTests.java
@@ -0,0 +1,39 @@
+package com.redhat.digkins.services;
+
+import com.offbytwo.jenkins.JenkinsServer;
+import com.offbytwo.jenkins.model.BuildWithDetails;
+import com.offbytwo.jenkins.model.JobWithDetails;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.*;
+
+@RunWith(MockitoJUnitRunner.class)
+public class JobServiceTests {
+
+ @Mock
+ private JenkinsServer server;
+ private JobService jobService;
+
+ @Before
+ public void beforeTests() {
+ jobService = new JobService();
+ }
+
+ @Test
+ public void shouldCreateJob() throws Exception {
+ JobWithDetails job = mock(JobWithDetails.class);
+ BuildWithDetails build = mock(BuildWithDetails.class);
+ when(job.getBuildByNumber(anyInt())).thenReturn(build);
+ when(build.details()).thenReturn(build);
+ jobService.create(server, "name", "repo", "branch");
+ verify(server, times(1)).createJob(anyString(), anyString());
+ }
+
+}