Skip to content

Commit

Permalink
Merge pull request #1075 from gsmet/more-workflows
Browse files Browse the repository at this point in the history
Add support for artifacts and logs of workflow runs
  • Loading branch information
bitwiseman authored Apr 2, 2021
2 parents feba6ed + 9abfdc8 commit 78ffe5a
Show file tree
Hide file tree
Showing 51 changed files with 9,261 additions and 16 deletions.
106 changes: 106 additions & 0 deletions src/main/java/org/kohsuke/github/GHArtifact.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package org.kohsuke.github;

import com.fasterxml.jackson.annotation.JsonIgnore;
import org.apache.commons.lang3.StringUtils;
import org.kohsuke.github.function.InputStreamFunction;

import java.io.IOException;
import java.net.URL;
import java.util.Date;
import java.util.Objects;

import static java.util.Objects.requireNonNull;

/**
* An artifact from a workflow run.
*
* @author Guillaume Smet
*/
public class GHArtifact extends GHObject {

// Not provided by the API.
@JsonIgnore
private GHRepository owner;

private String name;
private long sizeInBytes;
private String archiveDownloadUrl;
private boolean expired;
private String expiresAt;

public String getName() {
return name;
}

public long getSizeInBytes() {
return sizeInBytes;
}

public URL getArchiveDownloadUrl() {
return GitHubClient.parseURL(archiveDownloadUrl);
}

public boolean isExpired() {
return expired;
}

public Date getExpiresAt() {
return GitHubClient.parseDate(expiresAt);
}

/**
* @deprecated This object has no HTML URL.
*/
@Override
public URL getHtmlUrl() throws IOException {
return null;
}

/**
* Deletes the artifact.
*
* @throws IOException
* the io exception
*/
public void delete() throws IOException {
root.createRequest().method("DELETE").withUrlPath(getApiRoute()).fetchHttpStatusCode();
}

/**
* Downloads the artifact.
*
* @param <T>
* the type of result
* @param streamFunction
* The {@link InputStreamFunction} that will process the stream
* @throws IOException
* The IO exception.
* @return the result of reading the stream.
*/
public <T> T download(InputStreamFunction<T> streamFunction) throws IOException {
requireNonNull(streamFunction, "Stream function must not be null");

return root.createRequest().method("GET").withUrlPath(getApiRoute(), "zip").fetchStream(streamFunction);
}

private String getApiRoute() {
if (owner == null) {
// Workflow runs returned from search to do not have an owner. Attempt to use url.
final URL url = Objects.requireNonNull(getUrl(), "Missing instance URL!");
return StringUtils.prependIfMissing(url.toString().replace(root.getApiUrl(), ""), "/");
}
return "/repos/" + owner.getOwnerName() + "/" + owner.getName() + "/actions/artifacts/" + getId();
}

GHArtifact wrapUp(GHRepository owner) {
this.owner = owner;
return wrapUp(owner.root);
}

GHArtifact wrapUp(GitHub root) {
this.root = root;
if (owner != null)
owner.wrap(root);
return this;
}
}
49 changes: 49 additions & 0 deletions src/main/java/org/kohsuke/github/GHArtifactsIterable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.kohsuke.github;

import java.net.MalformedURLException;
import java.util.Iterator;

import javax.annotation.Nonnull;

/**
* Iterable for artifacts listing.
*/
class GHArtifactsIterable extends PagedIterable<GHArtifact> {
private final transient GHRepository owner;
private final GitHubRequest request;

private GHArtifactsPage result;

public GHArtifactsIterable(GHRepository owner, GitHubRequest.Builder<?> requestBuilder) {
this.owner = owner;
try {
this.request = requestBuilder.build();
} catch (MalformedURLException e) {
throw new GHException("Malformed URL", e);
}
}

@Nonnull
@Override
public PagedIterator<GHArtifact> _iterator(int pageSize) {
return new PagedIterator<>(
adapt(GitHubPageIterator.create(owner.getRoot().getClient(), GHArtifactsPage.class, request, pageSize)),
null);
}

protected Iterator<GHArtifact[]> adapt(final Iterator<GHArtifactsPage> base) {
return new Iterator<GHArtifact[]>() {
public boolean hasNext() {
return base.hasNext();
}

public GHArtifact[] next() {
GHArtifactsPage v = base.next();
if (result == null) {
result = v;
}
return v.getArtifacts(owner);
}
};
}
}
20 changes: 20 additions & 0 deletions src/main/java/org/kohsuke/github/GHArtifactsPage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.kohsuke.github;

/**
* Represents the one page of artifacts result when listing artifacts.
*/
class GHArtifactsPage {
private int total_count;
private GHArtifact[] artifacts;

public int getTotalCount() {
return total_count;
}

GHArtifact[] getArtifacts(GHRepository owner) {
for (GHArtifact artifact : artifacts) {
artifact.wrapUp(owner);
}
return artifacts;
}
}
10 changes: 4 additions & 6 deletions src/main/java/org/kohsuke/github/GHEventPayload.java
Original file line number Diff line number Diff line change
Expand Up @@ -1400,13 +1400,11 @@ void wrapUp(GitHub root) {
"Expected workflow and workflow_run payload, but got something else. Maybe we've got another type of event?");
}
GHRepository repository = getRepository();
if (repository != null) {
workflowRun.wrapUp(repository);
workflow.wrapUp(repository);
} else {
workflowRun.wrapUp(root);
workflow.wrapUp(root);
if (repository == null) {
throw new IllegalStateException("Repository must not be null");
}
workflowRun.wrapUp(repository);
workflow.wrapUp(repository);
}
}
}
35 changes: 33 additions & 2 deletions src/main/java/org/kohsuke/github/GHRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,15 @@

import javax.annotation.Nonnull;

import static java.util.Arrays.*;
import static java.util.Arrays.asList;
import static java.util.Objects.requireNonNull;
import static org.kohsuke.github.internal.Previews.*;
import static org.kohsuke.github.internal.Previews.ANTIOPE;
import static org.kohsuke.github.internal.Previews.ANT_MAN;
import static org.kohsuke.github.internal.Previews.BAPTISTE;
import static org.kohsuke.github.internal.Previews.FLASH;
import static org.kohsuke.github.internal.Previews.INERTIA;
import static org.kohsuke.github.internal.Previews.MERCY;
import static org.kohsuke.github.internal.Previews.SHADOW_CAT;

/**
* A repository on GitHub.
Expand Down Expand Up @@ -2961,6 +2967,31 @@ public GHWorkflowRun getWorkflowRun(long id) throws IOException {
.wrapUp(this);
}

/**
* Lists all the artifacts of this repository.
*
* @return the paged iterable
*/
public PagedIterable<GHArtifact> listArtifacts() {
return new GHArtifactsIterable(this, root.createRequest().withUrlPath(getApiTailUrl("actions/artifacts")));
}

/**
* Gets an artifact by id.
*
* @param id
* the id of the artifact
* @return the artifact
* @throws IOException
* the io exception
*/
public GHArtifact getArtifact(long id) throws IOException {
return root.createRequest()
.withUrlPath(getApiTailUrl("actions/artifacts"), String.valueOf(id))
.fetch(GHArtifact.class)
.wrapUp(this);
}

// Only used within listTopics().
private static class Topics {
public List<String> names;
Expand Down
42 changes: 42 additions & 0 deletions src/main/java/org/kohsuke/github/GHWorkflowRun.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.commons.lang3.StringUtils;
import org.kohsuke.github.function.InputStreamFunction;
import org.kohsuke.github.internal.EnumUtils;

import java.io.IOException;
Expand All @@ -13,6 +14,8 @@
import java.util.Locale;
import java.util.Objects;

import static java.util.Objects.requireNonNull;

/**
* A workflow run.
*
Expand Down Expand Up @@ -261,6 +264,45 @@ public void rerun() throws IOException {
root.createRequest().method("POST").withUrlPath(getApiRoute(), "rerun").fetchHttpStatusCode();
}

/**
* Lists the artifacts attached to this workflow run.
*
* @return the paged iterable
*/
public PagedIterable<GHArtifact> listArtifacts() {
return new GHArtifactsIterable(owner, root.createRequest().withUrlPath(getApiRoute(), "artifacts"));
}

/**
* Downloads the logs.
* <p>
* The logs are in the form of a zip archive. The full log file is at the root and called {@code 1_build.txt}. Split
* log files are also available in the {@code build} directory.
*
* @param <T>
* the type of result
* @param streamFunction
* The {@link InputStreamFunction} that will process the stream
* @throws IOException
* The IO exception.
* @return the result of reading the stream.
*/
public <T> T downloadLogs(InputStreamFunction<T> streamFunction) throws IOException {
requireNonNull(streamFunction, "Stream function must not be null");

return root.createRequest().method("GET").withUrlPath(getApiRoute(), "logs").fetchStream(streamFunction);
}

/**
* Delete the logs.
*
* @throws IOException
* the io exception
*/
public void deleteLogs() throws IOException {
root.createRequest().method("DELETE").withUrlPath(getApiRoute(), "logs").fetchHttpStatusCode();
}

private String getApiRoute() {
if (owner == null) {
// Workflow runs returned from search to do not have an owner. Attempt to use url.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public GHWorkflowRunQueryBuilder status(Status status) {
@Override
public PagedIterable<GHWorkflowRun> list() {
try {
return new GHWorkflowRunsIterable(root, req.withUrlPath(repo.getApiTailUrl("actions/runs")).build());
return new GHWorkflowRunsIterable(repo, req.withUrlPath(repo.getApiTailUrl("actions/runs")).build());
} catch (MalformedURLException e) {
throw new GHException(e.getMessage(), e);
}
Expand Down
11 changes: 6 additions & 5 deletions src/main/java/org/kohsuke/github/GHWorkflowRunsIterable.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,22 @@
* Iterable for workflow runs listing.
*/
class GHWorkflowRunsIterable extends PagedIterable<GHWorkflowRun> {
private final transient GitHub root;
private final GHRepository owner;
private final GitHubRequest request;

private GHWorkflowRunsPage result;

public GHWorkflowRunsIterable(GitHub root, GitHubRequest request) {
this.root = root;
public GHWorkflowRunsIterable(GHRepository owner, GitHubRequest request) {
this.owner = owner;
this.request = request;
}

@Nonnull
@Override
public PagedIterator<GHWorkflowRun> _iterator(int pageSize) {
return new PagedIterator<>(
adapt(GitHubPageIterator.create(root.getClient(), GHWorkflowRunsPage.class, request, pageSize)),
adapt(GitHubPageIterator
.create(owner.getRoot().getClient(), GHWorkflowRunsPage.class, request, pageSize)),
null);
}

Expand All @@ -37,7 +38,7 @@ public GHWorkflowRun[] next() {
if (result == null) {
result = v;
}
return v.getWorkflowRuns(root);
return v.getWorkflowRuns(owner);
}
};
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/org/kohsuke/github/GHWorkflowRunsPage.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ public int getTotalCount() {
return totalCount;
}

GHWorkflowRun[] getWorkflowRuns(GitHub root) {
GHWorkflowRun[] getWorkflowRuns(GHRepository owner) {
for (GHWorkflowRun workflowRun : workflowRuns) {
workflowRun.wrapUp(root);
workflowRun.wrapUp(owner);
}
return workflowRuns;
}
Expand Down
Loading

0 comments on commit 78ffe5a

Please sign in to comment.