diff --git a/pom.xml b/pom.xml index 5b11e2f88c..a0428a8a09 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ github-api - 1.93 + 1.94 GitHub API for Java http://github-api.kohsuke.org/ GitHub API for Java @@ -16,7 +16,7 @@ scm:git:git@github.com/kohsuke/${project.artifactId}.git scm:git:ssh://git@github.com/kohsuke/${project.artifactId}.git http://${project.artifactId}.kohsuke.org/ - github-api-1.93 + github-api-1.94 diff --git a/src/main/java/org/kohsuke/github/GHBranch.java b/src/main/java/org/kohsuke/github/GHBranch.java index 1ce9c29ac4..e457fc243d 100644 --- a/src/main/java/org/kohsuke/github/GHBranch.java +++ b/src/main/java/org/kohsuke/github/GHBranch.java @@ -65,9 +65,8 @@ public URL getProtectionUrl() { return GitHub.parseURL(protection_url); } - @Preview @Deprecated public GHBranchProtection getProtection() throws IOException { - return root.retrieve().withPreview(LOKI).to(protection_url, GHBranchProtection.class); + return root.retrieve().to(protection_url, GHBranchProtection.class).wrap(this); } /** @@ -80,9 +79,8 @@ public String getSHA1() { /** * Disables branch protection and allows anyone with push access to push changes. */ - @Preview @Deprecated public void disableProtection() throws IOException { - new Requester(root).method("DELETE").withPreview(LOKI).to(protection_url); + new Requester(root).method("DELETE").to(protection_url); } /** diff --git a/src/main/java/org/kohsuke/github/GHBranchProtection.java b/src/main/java/org/kohsuke/github/GHBranchProtection.java index 7e43bd69cb..8420a531ad 100644 --- a/src/main/java/org/kohsuke/github/GHBranchProtection.java +++ b/src/main/java/org/kohsuke/github/GHBranchProtection.java @@ -3,27 +3,46 @@ import com.fasterxml.jackson.annotation.JsonProperty; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import static org.kohsuke.github.Previews.ZZZAX; + +import java.io.IOException; import java.util.Collection; -@SuppressFBWarnings(value = { "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD", - "URF_UNREAD_FIELD" }, justification = "JSON API") +@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD", + "URF_UNREAD_FIELD"}, justification = "JSON API") public class GHBranchProtection { - @JsonProperty("enforce_admins") - private EnforceAdmins enforceAdmins; + private static final String REQUIRE_SIGNATURES_URI = "/required_signatures"; + + @JsonProperty("enforce_admins") + private EnforceAdmins enforceAdmins; + + private GitHub root; + + @JsonProperty("required_pull_request_reviews") + private RequiredReviews requiredReviews; - @JsonProperty("required_pull_request_reviews") - private RequiredReviews requiredReviews; + @JsonProperty("required_status_checks") + private RequiredStatusChecks requiredStatusChecks; - @JsonProperty("required_status_checks") - private RequiredStatusChecks requiredStatusChecks; + @JsonProperty + private Restrictions restrictions; - @JsonProperty - private Restrictions restrictions; + @JsonProperty + private String url; + + @Preview @Deprecated + public void enabledSignedCommits() throws IOException { + requester().method("POST") + .to(url + REQUIRE_SIGNATURES_URI, RequiredSignatures.class); + } + + @Preview @Deprecated + public void disableSignedCommits() throws IOException { + requester().method("DELETE") + .to(url + REQUIRE_SIGNATURES_URI); + } - @JsonProperty - private String url; - - public EnforceAdmins getEnforceAdmins() { + public EnforceAdmins getEnforceAdmins() { return enforceAdmins; } @@ -31,6 +50,12 @@ public RequiredReviews getRequiredReviews() { return requiredReviews; } + @Preview @Deprecated + public boolean getRequiredSignatures() throws IOException { + return requester().method("GET") + .to(url + REQUIRE_SIGNATURES_URI, RequiredSignatures.class).enabled; + } + public RequiredStatusChecks getRequiredStatusChecks() { return requiredStatusChecks; } @@ -43,12 +68,21 @@ public String getUrl() { return url; } + GHBranchProtection wrap(GHBranch branch) { + this.root = branch.getRoot(); + return this; + } + + private Requester requester() { + return new Requester(root).withPreview(ZZZAX); + } + public static class EnforceAdmins { - @JsonProperty - private boolean enabled; + @JsonProperty + private boolean enabled; - @JsonProperty - private String url; + @JsonProperty + private String url; public String getUrl() { return url; @@ -57,20 +91,23 @@ public String getUrl() { public boolean isEnabled() { return enabled; } - } + } + + public static class RequiredReviews { + @JsonProperty("dismissal_restrictions") + private Restrictions dismissalRestriction; - public static class RequiredReviews { - @JsonProperty("dismissal_restrictions") - private Restrictions dismissalRestriction; + @JsonProperty("dismiss_stale_reviews") + private boolean dismissStaleReviews; - @JsonProperty("dismiss_stale_reviews") - private boolean dismissStaleReviews; + @JsonProperty("require_code_owner_reviews") + private boolean requireCodeOwnerReviews; - @JsonProperty("require_code_owner_reviews") - private boolean requireCodeOwnerReviews; + @JsonProperty("required_approving_review_count") + private int requiredReviewers; - @JsonProperty - private String url; + @JsonProperty + private String url; public Restrictions getDismissalRestrictions() { return dismissalRestriction; @@ -87,17 +124,37 @@ public boolean isDismissStaleReviews() { public boolean isRequireCodeOwnerReviews() { return requireCodeOwnerReviews; } - } - public static class RequiredStatusChecks { - @JsonProperty - private Collection contexts; + public int getRequiredReviewers() { + return requiredReviewers; + } + } + + private static class RequiredSignatures { + @JsonProperty + private boolean enabled; + + @JsonProperty + private String url; + + public String getUrl() { + return url; + } - @JsonProperty - private boolean strict; + public boolean isEnabled() { + return enabled; + } + } - @JsonProperty - private String url; + public static class RequiredStatusChecks { + @JsonProperty + private Collection contexts; + + @JsonProperty + private boolean strict; + + @JsonProperty + private String url; public Collection getContexts() { return contexts; @@ -110,23 +167,23 @@ public String getUrl() { public boolean isRequiresBranchUpToDate() { return strict; } - } + } - public static class Restrictions { - @JsonProperty - private Collection teams; + public static class Restrictions { + @JsonProperty + private Collection teams; - @JsonProperty("teams_url") - private String teamsUrl; + @JsonProperty("teams_url") + private String teamsUrl; - @JsonProperty - private String url; + @JsonProperty + private String url; - @JsonProperty - private Collection users; + @JsonProperty + private Collection users; - @JsonProperty("users_url") - private String usersUrl; + @JsonProperty("users_url") + private String usersUrl; public Collection getTeams() { return teams; @@ -147,5 +204,5 @@ public Collection getUsers() { public String getUsersUrl() { return usersUrl; } - } + } } diff --git a/src/main/java/org/kohsuke/github/GHBranchProtectionBuilder.java b/src/main/java/org/kohsuke/github/GHBranchProtectionBuilder.java index dc7dbd8845..822541a14d 100644 --- a/src/main/java/org/kohsuke/github/GHBranchProtectionBuilder.java +++ b/src/main/java/org/kohsuke/github/GHBranchProtectionBuilder.java @@ -44,7 +44,11 @@ public GHBranchProtectionBuilder addRequiredChecks(String... checks) { } public GHBranchProtectionBuilder dismissStaleReviews() { - getPrReviews().put("dismiss_stale_reviews", true); + return dismissStaleReviews(true); + } + + public GHBranchProtectionBuilder dismissStaleReviews(boolean v) { + getPrReviews().put("dismiss_stale_reviews", v); return this; } @@ -54,7 +58,8 @@ public GHBranchProtection enable() throws IOException { .withNullable("required_pull_request_reviews", prReviews) .withNullable("restrictions", restrictions) .withNullable("enforce_admins", enforceAdmins) - .to(branch.getProtectionUrl().toString(), GHBranchProtection.class); + .to(branch.getProtectionUrl().toString(), GHBranchProtection.class) + .wrap(branch); } public GHBranchProtectionBuilder includeAdmins() { @@ -66,6 +71,11 @@ public GHBranchProtectionBuilder includeAdmins(boolean v) { return this; } + public GHBranchProtectionBuilder requiredReviewers(int v) { + getPrReviews().put("required_approving_review_count", v); + return this; + } + public GHBranchProtectionBuilder requireBranchIsUpToDate() { return requireBranchIsUpToDate(true); } @@ -89,6 +99,16 @@ public GHBranchProtectionBuilder requireReviews() { return this; } + public GHBranchProtectionBuilder restrictReviewDismissals() { + getPrReviews(); + + if (!prReviews.containsKey("dismissal_restrictions")) { + prReviews.put("dismissal_restrictions", new Restrictions()); + } + + return this; + } + public GHBranchProtectionBuilder restrictPushAccess() { getRestrictions(); return this; @@ -151,12 +171,7 @@ public GHBranchProtectionBuilder userReviewDismissals(GHUser... users) { } private void addReviewRestriction(String restriction, boolean isTeam) { - getPrReviews(); - - if (!prReviews.containsKey("dismissal_restrictions")) { - prReviews.put("dismissal_restrictions", new Restrictions()); - } - + restrictReviewDismissals(); Restrictions restrictions = (Restrictions) prReviews.get("dismissal_restrictions"); if (isTeam) { @@ -188,7 +203,7 @@ private StatusChecks getStatusChecks() { } private Requester requester() { - return new Requester(branch.getRoot()).withPreview(LOKI); + return new Requester(branch.getRoot()).withPreview(LUKE_CAGE); } private static class Restrictions { diff --git a/src/main/java/org/kohsuke/github/GHContentBuilder.java b/src/main/java/org/kohsuke/github/GHContentBuilder.java new file mode 100644 index 0000000000..cdc019f6d1 --- /dev/null +++ b/src/main/java/org/kohsuke/github/GHContentBuilder.java @@ -0,0 +1,76 @@ +package org.kohsuke.github; + +import org.apache.commons.codec.binary.Base64; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; + +/** + * Used to create/update content. + * + *

+ * Call various methods to build up parameters, then call {@link #commit()} to make the change effective. + * + * @author Kohsuke Kawaguchi + * @see GHRepository#createContent() + */ +public final class GHContentBuilder { + private final GHRepository repo; + private final Requester req; + private String path; + + GHContentBuilder(GHRepository repo) { + this.repo = repo; + this.req = new Requester(repo.root).method("PUT"); + } + + public GHContentBuilder path(String path) { + this.path = path; + req.with("path",path); + return this; + } + + public GHContentBuilder branch(String branch) { + req.with("branch", branch); + return this; + } + + /** + * Used when updating (but not creating a new content) to specify + * Thetblob SHA of the file being replaced. + */ + public GHContentBuilder sha(String sha) { + req.with("sha", sha); + return this; + } + + public GHContentBuilder content(byte[] content) { + req.with("content", Base64.encodeBase64String(content)); + return this; + } + + public GHContentBuilder content(String content) { + try { + return content(content.getBytes("UTF-8")); + } catch (UnsupportedEncodingException x) { + throw new AssertionError(); + } + } + + public GHContentBuilder message(String commitMessage) { + req.with("message", commitMessage); + return this; + } + + /** + * Commits a new content. + */ + public GHContentUpdateResponse commit() throws IOException { + GHContentUpdateResponse response = req.to(repo.getApiTailUrl("contents/" + path), GHContentUpdateResponse.class); + + response.getContent().wrap(repo); + response.getCommit().wrapUp(repo); + + return response; + } +} diff --git a/src/main/java/org/kohsuke/github/GHEventPayload.java b/src/main/java/org/kohsuke/github/GHEventPayload.java index b01b32e432..acdf5c74e9 100644 --- a/src/main/java/org/kohsuke/github/GHEventPayload.java +++ b/src/main/java/org/kohsuke/github/GHEventPayload.java @@ -174,6 +174,51 @@ void wrapUp(GitHub root) { } } + /** + * A Issue has been assigned, unassigned, labeled, unlabeled, opened, edited, milestoned, demilestoned, closed, or reopened. + * + * @see authoritative source + */ + @SuppressFBWarnings(value = {"UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR", "NP_UNWRITTEN_FIELD" }, + justification = "Constructed by JSON deserialization") + public static class Issue extends GHEventPayload { + private String action; + private GHIssue issue; + private GHRepository repository; + + @SuppressFBWarnings(value = "UWF_UNWRITTEN_FIELD", justification = "Comes from JSON deserialization") + public String getAction() { + return action; + } + + public GHIssue getIssue() { + return issue; + } + + public void setIssue(GHIssue issue) { + this.issue = issue; + } + + public GHRepository getRepository() { + return repository; + } + + public void setRepository(GHRepository repository) { + this.repository = repository; + } + + @Override + void wrapUp(GitHub root) { + super.wrapUp(root); + if (repository != null) { + repository.wrap(root); + issue.wrap(repository); + } else { + issue.wrap(root); + } + } + } + /** * A comment was added to an issue * @@ -716,6 +761,48 @@ public List getModified() { } } + /** + * A release was added to the repo + * + * @see authoritative source + */ + @SuppressFBWarnings(value = {"UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR", "NP_UNWRITTEN_FIELD" }, + justification = "Constructed by JSON deserialization") + public static class Release extends GHEventPayload { + private String action; + private GHRelease release; + private GHRepository repository; + + @SuppressFBWarnings(value = "UWF_UNWRITTEN_FIELD", justification = "Comes from JSON deserialization") + public String getAction() { + return action; + } + + public GHRelease getRelease() { + return release; + } + + public void setRelease(GHRelease release) { + this.release = release; + } + + public GHRepository getRepository() { + return repository; + } + + public void setRepository(GHRepository repository) { + this.repository = repository; + } + + @Override + void wrapUp(GitHub root) { + super.wrapUp(root); + if (repository != null) { + repository.wrap(root); + } + } + } + /** * A repository was created, deleted, made public, or made private. * diff --git a/src/main/java/org/kohsuke/github/GHInvitation.java b/src/main/java/org/kohsuke/github/GHInvitation.java new file mode 100644 index 0000000000..74619ad6e0 --- /dev/null +++ b/src/main/java/org/kohsuke/github/GHInvitation.java @@ -0,0 +1,46 @@ +package org.kohsuke.github; + +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + +import java.io.IOException; +import java.net.URL; + +/** + * @see GitHub#getMyInvitations() + * @see GHRepository#listInvitations() + */ +@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD", + "NP_UNWRITTEN_FIELD", "UUF_UNUSED_FIELD"}, justification = "JSON API") +public class GHInvitation extends GHObject { + /*package almost final*/ GitHub root; + + private int id; + private GHRepository repository; + private GHUser invitee, inviter; + private String permissions; + private String html_url; + + /*package*/ GHInvitation wrapUp(GitHub root) { + this.root = root; + return this; + } + + /** + * Accept a repository invitation. + */ + public void accept() throws IOException { + root.retrieve().method("PATCH").to("/user/repository_invitations/" + id); + } + + /** + * Decline a repository invitation. + */ + public void decline() throws IOException { + root.retrieve().method("DELETE").to("/user/repository_invitations/" + id); + } + + @Override + public URL getHtmlUrl() { + return GitHub.parseURL(html_url); + } +} diff --git a/src/main/java/org/kohsuke/github/GHOrganization.java b/src/main/java/org/kohsuke/github/GHOrganization.java index f174decf2f..e5ce68e26b 100644 --- a/src/main/java/org/kohsuke/github/GHOrganization.java +++ b/src/main/java/org/kohsuke/github/GHOrganization.java @@ -50,7 +50,7 @@ public GHRepository createRepository(String name, String description, String hom * You use the returned builder to set various properties, then call {@link GHCreateRepositoryBuilder#create()} * to finally createa repository. */ - public GHCreateRepositoryBuilder createRepository(String name) throws IOException { + public GHCreateRepositoryBuilder createRepository(String name) { return new GHCreateRepositoryBuilder(root,"/orgs/"+login+"/repos",name); } @@ -269,7 +269,7 @@ protected void wrapUp(GHEventInfo[] page) { public PagedIterable listRepositories(final int pageSize) { return new PagedIterable() { public PagedIterator _iterator(int pageSize) { - return new PagedIterator(root.retrieve().asIterator("/orgs/" + login + "/repos?per_page=" + pageSize, GHRepository[].class, pageSize)) { + return new PagedIterator(root.retrieve().asIterator("/orgs/" + login + "/repos", GHRepository[].class, pageSize)) { @Override protected void wrapUp(GHRepository[] page) { for (GHRepository c : page) @@ -277,7 +277,7 @@ protected void wrapUp(GHRepository[] page) { } }; } - }; + }.withPageSize(pageSize); } /** diff --git a/src/main/java/org/kohsuke/github/GHPerson.java b/src/main/java/org/kohsuke/github/GHPerson.java index 87cb8efaa7..b1be225965 100644 --- a/src/main/java/org/kohsuke/github/GHPerson.java +++ b/src/main/java/org/kohsuke/github/GHPerson.java @@ -81,7 +81,7 @@ public PagedIterable listRepositories() { public PagedIterable listRepositories(final int pageSize) { return new PagedIterable() { public PagedIterator _iterator(int pageSize) { - return new PagedIterator(root.retrieve().asIterator("/users/" + login + "/repos?per_page=" + pageSize, GHRepository[].class, pageSize)) { + return new PagedIterator(root.retrieve().asIterator("/users/" + login + "/repos", GHRepository[].class, pageSize)) { @Override protected void wrapUp(GHRepository[] page) { for (GHRepository c : page) @@ -89,7 +89,7 @@ protected void wrapUp(GHRepository[] page) { } }; } - }; + }.withPageSize(pageSize); } /** @@ -108,7 +108,7 @@ protected void wrapUp(GHRepository[] page) { public synchronized Iterable> iterateRepositories(final int pageSize) { return new Iterable>() { public Iterator> iterator() { - final Iterator pager = root.retrieve().asIterator("/users/" + login + "/repos?per_page="+pageSize,GHRepository[].class, pageSize); + final Iterator pager = root.retrieve().asIterator("/users/" + login + "/repos",GHRepository[].class, pageSize); return new Iterator>() { public boolean hasNext() { diff --git a/src/main/java/org/kohsuke/github/GHRelease.java b/src/main/java/org/kohsuke/github/GHRelease.java index a299add2c5..0df6b415b0 100644 --- a/src/main/java/org/kohsuke/github/GHRelease.java +++ b/src/main/java/org/kohsuke/github/GHRelease.java @@ -3,6 +3,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; import java.net.URL; import java.util.Arrays; import java.util.Date; @@ -125,12 +126,21 @@ static GHRelease[] wrap(GHRelease[] releases, GHRepository owner) { * handling of the HTTP requests to github's API. */ public GHAsset uploadAsset(File file, String contentType) throws IOException { + FileInputStream s = new FileInputStream(file); + try { + return uploadAsset(file.getName(), s, contentType); + } finally { + s.close(); + } + } + + public GHAsset uploadAsset(String filename, InputStream stream, String contentType) throws IOException { Requester builder = new Requester(owner.root); String url = format("https://uploads.github.com%s/releases/%d/assets?name=%s", - owner.getApiTailUrl(""), getId(), file.getName()); + owner.getApiTailUrl(""), getId(), filename); return builder.contentType(contentType) - .with(new FileInputStream(file)) + .with(stream) .to(url, GHAsset.class).wrap(this); } diff --git a/src/main/java/org/kohsuke/github/GHRepository.java b/src/main/java/org/kohsuke/github/GHRepository.java index 3c80c499ff..2acc864123 100644 --- a/src/main/java/org/kohsuke/github/GHRepository.java +++ b/src/main/java/org/kohsuke/github/GHRepository.java @@ -26,7 +26,6 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.infradna.tool.bridge_method_injector.WithBridgeMethods; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang3.StringUtils; import java.io.FileNotFoundException; @@ -35,7 +34,6 @@ import java.io.InputStreamReader; import java.io.InterruptedIOException; import java.io.Reader; -import java.io.UnsupportedEncodingException; import java.net.URL; import java.util.AbstractSet; import java.util.ArrayList; @@ -310,6 +308,22 @@ public GHRef createRef(String name, String sha) throws IOException { public List getReleases() throws IOException { return listReleases().asList(); } + + public GHRelease getRelease(long id) throws IOException { + try { + return root.retrieve().to(getApiTailUrl("releases/" + id), GHRelease.class).wrap(this); + } catch (FileNotFoundException e) { + return null; // no release for this id + } + } + + public GHRelease getReleaseByTagName(String tag) throws IOException { + try { + return root.retrieve().to(getApiTailUrl("releases/tags/" + tag), GHRelease.class).wrap(this); + } catch (FileNotFoundException e) { + return null; // no release for this tag + } + } public GHRelease getLatestRelease() throws IOException { try { @@ -815,7 +829,9 @@ public PagedIterable listRefs() throws IOException { public PagedIterator _iterator(int pageSize) { return new PagedIterator(root.retrieve().asIterator(url, GHRef[].class, pageSize)) { protected void wrapUp(GHRef[] page) { - // no-op + for(GHRef p: page) { + p.wrap(root); + } } }; } @@ -1137,6 +1153,22 @@ public GHLabel createLabel(String name, String color) throws IOException { .to(getApiTailUrl("labels"), GHLabel.class).wrapUp(this); } + /** + * Lists all the invitations. + */ + public PagedIterable listInvitations() { + return new PagedIterable() { + public PagedIterator _iterator(int pageSize) { + return new PagedIterator(root.retrieve().asIterator(String.format("/repos/%s/%s/invitations", getOwnerName(), name), GHInvitation[].class, pageSize)) { + protected void wrapUp(GHInvitation[] page) { + for (GHInvitation c : page) + c.wrapUp(root); + } + }; + } + }; + } + /** * Lists all the subscribers (aka watchers.) * @@ -1306,7 +1338,7 @@ public boolean remove(Object url) { */ public Map getBranches() throws IOException { Map r = new TreeMap(); - for (GHBranch p : root.retrieve().withPreview(LOKI).to(getApiTailUrl("branches"), GHBranch[].class)) { + for (GHBranch p : root.retrieve().to(getApiTailUrl("branches"), GHBranch[].class)) { p.wrap(this); r.put(p.getName(),p); } @@ -1314,7 +1346,7 @@ public Map getBranches() throws IOException { } public GHBranch getBranch(String name) throws IOException { - return root.retrieve().withPreview(LOKI).to(getApiTailUrl("branches/"+name),GHBranch.class).wrap(this); + return root.retrieve().to(getApiTailUrl("branches/"+name),GHBranch.class).wrap(this); } /** @@ -1394,41 +1426,43 @@ public GHContent getReadme() throws IOException { return requester.to(getApiTailUrl("readme"), GHContent.class).wrap(this); } + /** + * Creates a new content, or update an existing content. + */ + public GHContentBuilder createContent() { + return new GHContentBuilder(this); + } + + /** + * Use {@link #createContent()}. + */ + @Deprecated public GHContentUpdateResponse createContent(String content, String commitMessage, String path) throws IOException { - return createContent(content, commitMessage, path, null); + return createContent().content(content).message(commitMessage).path(path).commit(); } + /** + * Use {@link #createContent()}. + */ + @Deprecated public GHContentUpdateResponse createContent(String content, String commitMessage, String path, String branch) throws IOException { - final byte[] payload; - try { - payload = content.getBytes("UTF-8"); - } catch (UnsupportedEncodingException ex) { - throw (IOException) new IOException("UTF-8 encoding is not supported").initCause(ex); - } - return createContent(payload, commitMessage, path, branch); + return createContent().content(content).message(commitMessage).path(path).branch(branch).commit(); } + /** + * Use {@link #createContent()}. + */ + @Deprecated public GHContentUpdateResponse createContent(byte[] contentBytes, String commitMessage, String path) throws IOException { - return createContent(contentBytes, commitMessage, path, null); + return createContent().content(contentBytes).message(commitMessage).path(path).commit(); } + /** + * Use {@link #createContent()}. + */ + @Deprecated public GHContentUpdateResponse createContent(byte[] contentBytes, String commitMessage, String path, String branch) throws IOException { - Requester requester = new Requester(root) - .with("path", path) - .with("message", commitMessage) - .with("content", Base64.encodeBase64String(contentBytes)) - .method("PUT"); - - if (branch != null) { - requester.with("branch", branch); - } - - GHContentUpdateResponse response = requester.to(getApiTailUrl("contents/" + path), GHContentUpdateResponse.class); - - response.getContent().wrap(this); - response.getCommit().wrapUp(this); - - return response; + return createContent().content(contentBytes).message(commitMessage).path(path).branch(branch).commit(); } public GHMilestone createMilestone(String title, String description) throws IOException { diff --git a/src/main/java/org/kohsuke/github/GHRepositorySearchBuilder.java b/src/main/java/org/kohsuke/github/GHRepositorySearchBuilder.java index 642f7f3d95..0ecd77e600 100644 --- a/src/main/java/org/kohsuke/github/GHRepositorySearchBuilder.java +++ b/src/main/java/org/kohsuke/github/GHRepositorySearchBuilder.java @@ -55,6 +55,10 @@ public GHRepositorySearchBuilder stars(String v) { return q("stars:"+v); } + public GHRepositorySearchBuilder topic(String v) { + return q("topic:"+v); + } + public GHRepositorySearchBuilder order(GHDirection v) { req.with("order",v); return this; diff --git a/src/main/java/org/kohsuke/github/GitHub.java b/src/main/java/org/kohsuke/github/GitHub.java index 52a16906da..d5d0a2be33 100644 --- a/src/main/java/org/kohsuke/github/GitHub.java +++ b/src/main/java/org/kohsuke/github/GitHub.java @@ -530,6 +530,15 @@ public GHLicense getLicense(String key) throws IOException { } /** + * Gets complete list of open invitations for current user. + */ + public List getMyInvitations() throws IOException { + GHInvitation[] invitations = retrieve().to("/user/repository_invitations", GHInvitation[].class); + for (GHInvitation i : invitations) { + i.wrapUp(this); + } + return Arrays.asList(invitations); + } /** * This method returns a shallowly populated organizations. diff --git a/src/main/java/org/kohsuke/github/Previews.java b/src/main/java/org/kohsuke/github/Previews.java index 3f98c3d4b9..41ac7e155b 100644 --- a/src/main/java/org/kohsuke/github/Previews.java +++ b/src/main/java/org/kohsuke/github/Previews.java @@ -4,8 +4,9 @@ * @author Kohsuke Kawaguchi */ /*package*/ class Previews { - static final String LOKI = "application/vnd.github.loki-preview+json"; + static final String LUKE_CAGE = "application/vnd.github.luke-cage-preview+json"; static final String DRAX = "application/vnd.github.drax-preview+json"; static final String SQUIRREL_GIRL = "application/vnd.github.squirrel-girl-preview"; static final String CLOAK = "application/vnd.github.cloak-preview"; + static final String ZZZAX = "application/vnd.github.zzzax-preview+json"; } diff --git a/src/test/java/org/kohsuke/github/GHBranchProtectionTest.java b/src/test/java/org/kohsuke/github/GHBranchProtectionTest.java index b7ae713d87..19e42c51ba 100644 --- a/src/test/java/org/kohsuke/github/GHBranchProtectionTest.java +++ b/src/test/java/org/kohsuke/github/GHBranchProtectionTest.java @@ -32,6 +32,12 @@ public void setUp() throws Exception { branch = repo.getBranch(BRANCH); if (branch.isProtected()) { + GHBranchProtection protection = branch.getProtection(); + if (protection.getRequiredSignatures()) { + protection.disableSignedCommits(); + } + + assertFalse(protection.getRequiredSignatures()); branch.disableProtection(); } @@ -47,6 +53,7 @@ public void testEnableBranchProtections() throws Exception { .requireBranchIsUpToDate() .requireCodeOwnReviews() .dismissStaleReviews() + .requiredReviewers(2) .includeAdmins() .enable(); @@ -59,6 +66,7 @@ public void testEnableBranchProtections() throws Exception { assertNotNull(requiredReviews); assertTrue(requiredReviews.isDismissStaleReviews()); assertTrue(requiredReviews.isRequireCodeOwnerReviews()); + assertEquals(2, requiredReviews.getRequiredReviewers()); EnforceAdmins enforceAdmins = protection.getEnforceAdmins(); assertNotNull(enforceAdmins); @@ -79,4 +87,17 @@ public void testEnableRequireReviewsOnly() throws Exception { assertNotNull(protection.getRequiredReviews()); } + + @Test + public void testSignedCommits() throws Exception { + GHBranchProtection protection = branch.enableProtection().enable(); + + assertFalse(protection.getRequiredSignatures()); + + protection.enabledSignedCommits(); + assertTrue(protection.getRequiredSignatures()); + + protection.disableSignedCommits(); + assertFalse(protection.getRequiredSignatures()); + } } diff --git a/src/test/java/org/kohsuke/github/GHContentIntegrationTest.java b/src/test/java/org/kohsuke/github/GHContentIntegrationTest.java index 8e3873708b..4b8bf72d63 100644 --- a/src/test/java/org/kohsuke/github/GHContentIntegrationTest.java +++ b/src/test/java/org/kohsuke/github/GHContentIntegrationTest.java @@ -53,7 +53,8 @@ public void testGetDirectoryContentTrailingSlash() throws Exception { @Test public void testCRUDContent() throws Exception { - GHContentUpdateResponse created = repo.createContent("this is an awesome file I created\n", "Creating a file for integration tests.", createdFilename); + GHContentUpdateResponse created = + repo.createContent("this is an awesome file I created\n", "Creating a file for integration tests.", createdFilename); GHContent createdContent = created.getContent(); assertNotNull(created.getCommit()); diff --git a/src/test/java/org/kohsuke/github/GHEventPayloadTest.java b/src/test/java/org/kohsuke/github/GHEventPayloadTest.java index 71f9fd3e68..b0c2a05408 100644 --- a/src/test/java/org/kohsuke/github/GHEventPayloadTest.java +++ b/src/test/java/org/kohsuke/github/GHEventPayloadTest.java @@ -120,9 +120,19 @@ public void issue_comment() throws Exception { assertThat(event.getSender().getLogin(), is("baxterthehacker")); } -// TODO implement support classes and write test -// @Test -// public void issues() throws Exception {} + @Test + public void issues() throws Exception { + GHEventPayload.Issue event = GitHub.offline().parseEventPayload(payload.asReader(),GHEventPayload.Issue.class); + assertThat(event.getAction(),is("opened")); + assertThat(event.getIssue().getNumber(), is(2)); + assertThat(event.getIssue().getTitle(), is("Spelling error in the README file")); + assertThat(event.getIssue().getState(), is(GHIssueState.OPEN)); + assertThat(event.getIssue().getLabels().size(), is(1)); + assertThat(event.getIssue().getLabels().iterator().next().getName(), is("bug")); + assertThat(event.getRepository().getName(), is("public-repo")); + assertThat(event.getRepository().getOwner().getLogin(), is("baxterthehacker")); + assertThat(event.getSender().getLogin(), is("baxterthehacker")); + } // TODO implement support classes and write test // @Test diff --git a/src/test/java/org/kohsuke/github/PullRequestTest.java b/src/test/java/org/kohsuke/github/PullRequestTest.java index c2a6a2705e..ceb741bd34 100644 --- a/src/test/java/org/kohsuke/github/PullRequestTest.java +++ b/src/test/java/org/kohsuke/github/PullRequestTest.java @@ -105,6 +105,7 @@ public void testSquashMerge() throws Exception { String name = rnd.next(); GHRef masterRef = getRepository().getRef("heads/master"); GHRef branchRef = getRepository().createRef("refs/heads/" + name, masterRef.getObject().getSha()); + getRepository().createContent(name, name, name, name); Thread.sleep(1000); GHPullRequest p = getRepository().createPullRequest(name, name, "master", "## test squash"); @@ -112,6 +113,27 @@ public void testSquashMerge() throws Exception { p.merge("squash merge", null, GHPullRequest.MergeMethod.SQUASH); branchRef.delete(); } + @Test + public void testUpdateContentSquashMerge() throws Exception { + String name = rnd.next(); + GHRef masterRef = getRepository().getRef("heads/master"); + GHRef branchRef = getRepository().createRef("refs/heads/" + name, masterRef.getObject().getSha()); + + GHContentUpdateResponse response = getRepository().createContent(name, name, name, name); + Thread.sleep(1000); + + getRepository().createContent() + .content(name + name) + .path(name) + .branch(name) + .message(name) + .sha(response.getContent().getSha()) + .commit(); + GHPullRequest p = getRepository().createPullRequest(name, name, "master", "## test squash"); + Thread.sleep(1000); + p.merge("squash merge", null, GHPullRequest.MergeMethod.SQUASH); + branchRef.delete(); + } @Test // Requires push access to the test repo to pass diff --git a/src/test/java/org/kohsuke/github/RepositoryTest.java b/src/test/java/org/kohsuke/github/RepositoryTest.java index 728f94a834..66fe689507 100644 --- a/src/test/java/org/kohsuke/github/RepositoryTest.java +++ b/src/test/java/org/kohsuke/github/RepositoryTest.java @@ -94,6 +94,36 @@ public void LatestRepositoryNotExist() { } } + @Test public void listReleases() throws IOException { + PagedIterable releases = gitHub.getOrganization("github").getRepository("hub").listReleases(); + assertTrue(releases.iterator().hasNext()); + } + + @Test + public void getReleaseExists() throws IOException { + GHRelease release = gitHub.getOrganization("github").getRepository("hub").getRelease(6839710); + assertEquals("v2.3.0-pre10", release.getTagName()); + } + + @Test + public void getReleaseDoesNotExist() throws IOException { + GHRelease release = gitHub.getOrganization("github").getRepository("hub").getRelease(Long.MAX_VALUE); + assertNull(release); + } + + @Test + public void getReleaseByTagNameExists() throws IOException { + GHRelease release = gitHub.getOrganization("github").getRepository("hub").getReleaseByTagName("v2.3.0-pre10"); + assertNotNull(release); + assertEquals("v2.3.0-pre10", release.getTagName()); + } + + @Test + public void getReleaseByTagNameDoesNotExist() throws IOException { + GHRelease release = getRepository().getReleaseByTagName("foo-bar-baz"); + assertNull(release); + } + private GHRepository getRepository() throws IOException { return gitHub.getOrganization("github-api-test-org").getRepository("jenkins"); }