From e6d59df705dd2578cb89b3cf02116aedb805b9ff Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Sat, 30 Aug 2014 14:15:29 -0700 Subject: [PATCH 01/15] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1947c37495..e252104a93 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ github-api - 1.58 + 1.59-SNAPSHOT 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.58 + HEAD From 9eda2d3f773c5e71ff82d563059a92e3b404279e Mon Sep 17 00:00:00 2001 From: Thomas Bruyelle Date: Wed, 3 Sep 2014 22:01:43 +0200 Subject: [PATCH 02/15] Add missing field browser_download_url in GHAsset --- src/main/java/org/kohsuke/github/GHAsset.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/org/kohsuke/github/GHAsset.java b/src/main/java/org/kohsuke/github/GHAsset.java index b26c72000e..902356eebf 100644 --- a/src/main/java/org/kohsuke/github/GHAsset.java +++ b/src/main/java/org/kohsuke/github/GHAsset.java @@ -21,6 +21,7 @@ public class GHAsset { private long download_count; private Date created_at; private Date updated_at; + private String browser_download_url; public String getContentType() { return content_type; @@ -80,6 +81,10 @@ public String getUrl() { return url; } + public String getBrowserDownloadUrl() { + return browser_download_url; + } + private void edit(String key, Object value) throws IOException { new Requester(root)._with(key, value).method("PATCH").to(getApiRoute()); } From 1a9b8bd1da7a7c1ff62e437b9167d69269355a8d Mon Sep 17 00:00:00 2001 From: Roberto Tyley Date: Wed, 3 Sep 2014 23:18:22 +0100 Subject: [PATCH 03/15] Separate out clean-up of Pull requests created during test If the test assertion fails, we want the pull-request to be closed anyway. You can't have more than one pull-request merging a given branch into another, so leaving the PR hanging will cause subsequent test runs to fail in setup. --- src/test/java/org/kohsuke/github/PullRequestTest.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/kohsuke/github/PullRequestTest.java b/src/test/java/org/kohsuke/github/PullRequestTest.java index 18f4a160f7..c60d6a9724 100644 --- a/src/test/java/org/kohsuke/github/PullRequestTest.java +++ b/src/test/java/org/kohsuke/github/PullRequestTest.java @@ -1,5 +1,6 @@ package org.kohsuke.github; +import org.junit.After; import org.junit.Test; /** @@ -13,6 +14,12 @@ public void createPullRequest() throws Exception { GHPullRequest p = j.createPullRequest(name, "stable", "master", "## test"); System.out.println(p.getUrl()); assertEquals(name, p.getTitle()); - p.close(); + } + + @After + public void cleanUp() throws Exception { + for (GHPullRequest pr : getRepository().getPullRequests(GHIssueState.OPEN)) { + pr.close(); + } } } From e2220bb3b386c1ce9166b4c7e6c5ec55f3cb66cd Mon Sep 17 00:00:00 2001 From: Roberto Tyley Date: Wed, 3 Sep 2014 23:34:58 +0100 Subject: [PATCH 04/15] Fix setting labels and assignee on PullRequests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Setting labels and assignee on Pull requests failed silently, because the API endpoint being hit contained '/pulls/' rather than '/issues/'. "Every pull request is an issue, but not every issue is a pull request. For this reason, “shared” actions for both features, like manipulating assignees, labels and milestones, are provided within the Issues API." https://developer.github.com/v3/pulls/#labels-assignees-and-milestones --- src/main/java/org/kohsuke/github/GHIssue.java | 10 +++++-- .../org/kohsuke/github/PullRequestTest.java | 30 +++++++++++++++++-- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/kohsuke/github/GHIssue.java b/src/main/java/org/kohsuke/github/GHIssue.java index e83176ace5..74a09e88ff 100644 --- a/src/main/java/org/kohsuke/github/GHIssue.java +++ b/src/main/java/org/kohsuke/github/GHIssue.java @@ -163,6 +163,10 @@ private void edit(String key, Object value) throws IOException { new Requester(root)._with(key, value).method("PATCH").to(getApiRoute()); } + private void editIssue(String key, Object value) throws IOException { + new Requester(root)._with(key, value).method("PATCH").to(getIssuesApiRoute()); + } + /** * Closes this issue. */ @@ -186,11 +190,11 @@ public void setBody(String body) throws IOException { } public void assignTo(GHUser user) throws IOException { - edit("assignee",user.getLogin()); + editIssue("assignee",user.getLogin()); } public void setLabels(String... labels) throws IOException { - edit("labels",labels); + editIssue("labels",labels); } /** @@ -222,7 +226,7 @@ protected String getApiRoute() { return getIssuesApiRoute(); } - private String getIssuesApiRoute() { + protected String getIssuesApiRoute() { return "/repos/"+owner.getOwnerName()+"/"+owner.getName()+"/issues/"+number; } diff --git a/src/test/java/org/kohsuke/github/PullRequestTest.java b/src/test/java/org/kohsuke/github/PullRequestTest.java index c60d6a9724..174d38b2c0 100644 --- a/src/test/java/org/kohsuke/github/PullRequestTest.java +++ b/src/test/java/org/kohsuke/github/PullRequestTest.java @@ -3,23 +3,49 @@ import org.junit.After; import org.junit.Test; +import java.io.IOException; +import java.util.Collection; + /** * @author Kohsuke Kawaguchi */ public class PullRequestTest extends AbstractGitHubApiTestBase { @Test public void createPullRequest() throws Exception { - GHRepository j = gitHub.getOrganization("github-api-test-org").getRepository("jenkins"); String name = rnd.next(); - GHPullRequest p = j.createPullRequest(name, "stable", "master", "## test"); + GHPullRequest p = getRepository().createPullRequest(name, "stable", "master", "## test"); System.out.println(p.getUrl()); assertEquals(name, p.getTitle()); } + @Test // Requires push access to the test repo to pass + public void setLabels() throws Exception { + GHPullRequest p = getRepository().createPullRequest(rnd.next(), "stable", "master", "## test"); + String label = rnd.next(); + p.setLabels(label); + + Collection labels = getRepository().getPullRequest(p.getNumber()).getLabels(); + assertEquals(1, labels.size()); + assertEquals(label, labels.iterator().next().getName()); + } + + @Test // Requires push access to the test repo to pass + public void setAssignee() throws Exception { + GHPullRequest p = getRepository().createPullRequest(rnd.next(), "stable", "master", "## test"); + GHMyself user = gitHub.getMyself(); + p.assignTo(user); + + assertEquals(user, getRepository().getPullRequest(p.getNumber()).getAssignee()); + } + @After public void cleanUp() throws Exception { for (GHPullRequest pr : getRepository().getPullRequests(GHIssueState.OPEN)) { pr.close(); } } + + private GHRepository getRepository() throws IOException { + return gitHub.getOrganization("github-api-test-org").getRepository("jenkins"); + } } From 86d75fd767f0c9b95f1bfe506ce1817a5a5dd3a2 Mon Sep 17 00:00:00 2001 From: OHTAKE Tomohiro Date: Thu, 4 Sep 2014 11:40:19 +0900 Subject: [PATCH 05/15] Introduce GitHubBuilder for flexible GitHub instance creation --- src/main/java/org/kohsuke/github/GitHub.java | 41 ++++-------- .../org/kohsuke/github/GitHubBuilder.java | 63 +++++++++++++++++++ 2 files changed, 74 insertions(+), 30 deletions(-) create mode 100644 src/main/java/org/kohsuke/github/GitHubBuilder.java diff --git a/src/main/java/org/kohsuke/github/GitHub.java b/src/main/java/org/kohsuke/github/GitHub.java index 5404b1f309..44c2ed7cf2 100644 --- a/src/main/java/org/kohsuke/github/GitHub.java +++ b/src/main/java/org/kohsuke/github/GitHub.java @@ -26,8 +26,6 @@ import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY; import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NONE; -import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.Reader; @@ -42,12 +40,10 @@ import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Properties; import java.util.Set; import java.util.TimeZone; import org.apache.commons.codec.binary.Base64; -import org.apache.commons.io.IOUtils; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; @@ -74,13 +70,6 @@ public class GitHub { private HttpConnector connector = HttpConnector.DEFAULT; - /** - * Connects to GitHub.com - */ - private GitHub(String login, String oauthAccessToken, String password) throws IOException { - this (GITHUB_URL, login, oauthAccessToken, password); - } - /** * Creates a client API root object. * @@ -115,7 +104,7 @@ private GitHub(String login, String oauthAccessToken, String password) throws IO * @param password * User's password. Always used in conjunction with the {@code login} parameter */ - private GitHub(String apiUrl, String login, String oauthAccessToken, String password) throws IOException { + /* package */ GitHub(String apiUrl, String login, String oauthAccessToken, String password) throws IOException { if (apiUrl.endsWith("/")) apiUrl = apiUrl.substring(0, apiUrl.length()-1); // normalize this.apiUrl = apiUrl; @@ -139,15 +128,7 @@ private GitHub(String apiUrl, String login, String oauthAccessToken, String pass * Obtains the credential from "~/.github" */ public static GitHub connect() throws IOException { - Properties props = new Properties(); - File homeDir = new File(System.getProperty("user.home")); - FileInputStream in = new FileInputStream(new File(homeDir, ".github")); - try { - props.load(in); - } finally { - IOUtils.closeQuietly(in); - } - return new GitHub(GITHUB_URL,props.getProperty("login"), props.getProperty("oauth"),props.getProperty("password")); + return GitHubBuilder.fromPropertyFile().build(); } /** @@ -159,15 +140,15 @@ public static GitHub connect() throws IOException { * For historical reasons, this parameter still accepts the bare domain name, but that's considered deprecated. */ public static GitHub connectToEnterprise(String apiUrl, String oauthAccessToken) throws IOException { - return connectUsingOAuth(apiUrl, oauthAccessToken); + return new GitHubBuilder().withEndpoint(apiUrl).withOAuthToken(oauthAccessToken).build(); } public static GitHub connectToEnterprise(String apiUrl, String login, String password) throws IOException { - return new GitHub(apiUrl, login, null, password); + return new GitHubBuilder().withEndpoint(apiUrl).withPassword(login, password).build(); } public static GitHub connect(String login, String oauthAccessToken) throws IOException { - return new GitHub(login,oauthAccessToken,null); + return new GitHubBuilder().withOAuthToken(oauthAccessToken, login).build(); } /** @@ -176,19 +157,19 @@ public static GitHub connect(String login, String oauthAccessToken) throws IOExc * Use {@link #connectUsingPassword(String, String)} or {@link #connectUsingOAuth(String)}. */ public static GitHub connect(String login, String oauthAccessToken, String password) throws IOException { - return new GitHub(login,oauthAccessToken,password); + return new GitHubBuilder().withOAuthToken(oauthAccessToken, login).withPassword(login, password).build(); } public static GitHub connectUsingPassword(String login, String password) throws IOException { - return new GitHub(login,null,password); + return new GitHubBuilder().withPassword(login, password).build(); } public static GitHub connectUsingOAuth(String oauthAccessToken) throws IOException { - return new GitHub(null, oauthAccessToken, null); + return new GitHubBuilder().withOAuthToken(oauthAccessToken).build(); } public static GitHub connectUsingOAuth(String githubServer, String oauthAccessToken) throws IOException { - return new GitHub(githubServer,null, oauthAccessToken,null); + return new GitHubBuilder().withEndpoint(githubServer).withOAuthToken(oauthAccessToken).build(); } /** * Connects to GitHub anonymously. @@ -196,7 +177,7 @@ public static GitHub connectUsingOAuth(String githubServer, String oauthAccessTo * All operations that requires authentication will fail. */ public static GitHub connectAnonymously() throws IOException { - return new GitHub(null,null,null); + return new GitHubBuilder().build(); } /** @@ -475,5 +456,5 @@ public boolean isCredentialValid() throws IOException { MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); } - private static final String GITHUB_URL = "https://api.github.com"; + /* package */ static final String GITHUB_URL = "https://api.github.com"; } diff --git a/src/main/java/org/kohsuke/github/GitHubBuilder.java b/src/main/java/org/kohsuke/github/GitHubBuilder.java new file mode 100644 index 0000000000..3959e400de --- /dev/null +++ b/src/main/java/org/kohsuke/github/GitHubBuilder.java @@ -0,0 +1,63 @@ +package org.kohsuke.github; + +import org.apache.commons.io.IOUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Properties; + +/** + * @since 1.59 + */ +public class GitHubBuilder { + private String endpoint = GitHub.GITHUB_URL; + private String user; + private String password; + private String oauthToken; + + public GitHubBuilder() { + } + + public static GitHubBuilder fromPropertyFile() throws IOException { + File homeDir = new File(System.getProperty("user.home")); + File propertyFile = new File(homeDir, ".github"); + return fromPropertyFile(propertyFile.getPath()); + } + public static GitHubBuilder fromPropertyFile(String propertyFileName) throws IOException { + Properties props = new Properties(); + FileInputStream in = null; + try { + in = new FileInputStream(propertyFileName); + props.load(in); + } finally { + IOUtils.closeQuietly(in); + } + GitHubBuilder self = new GitHubBuilder(); + self.withOAuthToken(props.getProperty("oauth"), props.getProperty("login")); + self.withPassword(props.getProperty("login"), props.getProperty("password")); + return self; + } + + public GitHubBuilder withEndpoint(String endpoint) { + this.endpoint = endpoint; + return this; + } + public GitHubBuilder withPassword(String user, String password) { + this.user = user; + this.password = password; + return this; + } + public GitHubBuilder withOAuthToken(String oauthToken) { + return withOAuthToken(oauthToken, null); + } + public GitHubBuilder withOAuthToken(String oauthToken, String user) { + this.oauthToken = oauthToken; + this.user = user; + return this; + } + + public GitHub build() throws IOException { + return new GitHub(endpoint, user, oauthToken, password); + } +} From d6627b1e34da3cfcf28db1030d0ace10d969eabd Mon Sep 17 00:00:00 2001 From: OHTAKE Tomohiro Date: Thu, 4 Sep 2014 11:45:40 +0900 Subject: [PATCH 06/15] Use custom HttpConnector when only OAuth token is given --- src/main/java/org/kohsuke/github/GitHub.java | 5 ++++- src/main/java/org/kohsuke/github/GitHubBuilder.java | 7 ++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/kohsuke/github/GitHub.java b/src/main/java/org/kohsuke/github/GitHub.java index 44c2ed7cf2..9037572c7c 100644 --- a/src/main/java/org/kohsuke/github/GitHub.java +++ b/src/main/java/org/kohsuke/github/GitHub.java @@ -103,10 +103,13 @@ public class GitHub { * Secret OAuth token. * @param password * User's password. Always used in conjunction with the {@code login} parameter + * @param connector + * HttpConnector to use. Pass null to use default connector. */ - /* package */ GitHub(String apiUrl, String login, String oauthAccessToken, String password) throws IOException { + /* package */ GitHub(String apiUrl, String login, String oauthAccessToken, String password, HttpConnector connector) throws IOException { if (apiUrl.endsWith("/")) apiUrl = apiUrl.substring(0, apiUrl.length()-1); // normalize this.apiUrl = apiUrl; + if (null != connector) this.connector = connector; if (oauthAccessToken!=null) { encodedAuthorization = "token "+oauthAccessToken; diff --git a/src/main/java/org/kohsuke/github/GitHubBuilder.java b/src/main/java/org/kohsuke/github/GitHubBuilder.java index 3959e400de..baf26a6415 100644 --- a/src/main/java/org/kohsuke/github/GitHubBuilder.java +++ b/src/main/java/org/kohsuke/github/GitHubBuilder.java @@ -15,6 +15,7 @@ public class GitHubBuilder { private String user; private String password; private String oauthToken; + private HttpConnector connector; public GitHubBuilder() { } @@ -56,8 +57,12 @@ public GitHubBuilder withOAuthToken(String oauthToken, String user) { this.user = user; return this; } + public GitHubBuilder withConnector(HttpConnector connector) { + this.connector = connector; + return this; + } public GitHub build() throws IOException { - return new GitHub(endpoint, user, oauthToken, password); + return new GitHub(endpoint, user, oauthToken, password, connector); } } From 7b58182683583ed79006a4381ef7aa8b5780dd5b Mon Sep 17 00:00:00 2001 From: Nicolas De loof Date: Fri, 12 Sep 2014 23:16:43 +0200 Subject: [PATCH 07/15] Update github scopes according to https://developer.github.com/v3/oauth/#scopes --- src/main/java/org/kohsuke/github/GHAuthorization.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/org/kohsuke/github/GHAuthorization.java b/src/main/java/org/kohsuke/github/GHAuthorization.java index dae2c74115..7fe8fdaf94 100644 --- a/src/main/java/org/kohsuke/github/GHAuthorization.java +++ b/src/main/java/org/kohsuke/github/GHAuthorization.java @@ -22,6 +22,15 @@ public class GHAuthorization { public static final String DELETE_REPO = "delete_repo"; public static final String NOTIFICATIONS = "notifications"; public static final String GIST = "gist"; + public static final String READ_HOOK = "read:repo_hook"; + public static final String WRITE_HOOK = "write:repo_hook"; + public static final String AMIN_HOOK = "admin:repo_hook"; + public static final String READ_ORG = "read:org"; + public static final String WRITE_ORG = "write:org"; + public static final String ADMIN_ORG = "admin:org"; + public static final String READ_KEY = "read:public_key"; + public static final String WRITE_KEY = "write:public_key"; + public static final String ADMIN_KEY = "admin:public_key"; private GitHub root; private int id; From 8eb9fba0510a689bd9f28f975da162853c474d0c Mon Sep 17 00:00:00 2001 From: Michael O'Cleirigh Date: Mon, 22 Sep 2014 10:43:18 -0400 Subject: [PATCH 08/15] Allow pullRequest.getHead().getRepository().getCommit(headSha1) to work The wrong .wrap method was used for pull requests initialized by state (GHRepository.getPullReqests). The wrong wrap call was introduced in 9fd34aec7fe9fa1359418c92b0a14526f837a4a4 This commit sets it back to the .wrapUp method which makes sure the pull request substructure has the repo object set properly. Without this change a NullPointerException is thrown on the last line of this code because the repo object inside of the remoteRepository object is null: GHRepository repo = github.getRepository(targetRepository); List openPullRequests = repo.getPullRequests(GHIssueState.OPEN); for (GHPullRequest pullRequest : openPullRequests) { GHCommitPointer head = pullRequest.getHead(); GHRepository remoteRepository = head.getRepository(); String commitId = head.getSha(); GHCommit headCommit = remoteRepository.getCommit(commitId); --- src/main/java/org/kohsuke/github/GHRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/kohsuke/github/GHRepository.java b/src/main/java/org/kohsuke/github/GHRepository.java index 57b4a2043c..4e3441166c 100644 --- a/src/main/java/org/kohsuke/github/GHRepository.java +++ b/src/main/java/org/kohsuke/github/GHRepository.java @@ -514,7 +514,7 @@ public PagedIterator iterator() { @Override protected void wrapUp(GHPullRequest[] page) { for (GHPullRequest pr : page) - pr.wrap(GHRepository.this); + pr.wrapUp(GHRepository.this); } }; } From 4d6c5c14f1a44639b786aa217077cf10db909b5a Mon Sep 17 00:00:00 2001 From: Michael O'Cleirigh Date: Mon, 29 Sep 2014 11:41:14 -0400 Subject: [PATCH 09/15] Modify GitHubBuilder to resolve user credentials from the system environment Using the Jenkins EnvInject or Credentials Binding Plugins its possible to pass credentials as Environment Variables. Its useful for Github.connect() to be able to directly read the values of the 'login', 'password' and 'oauth' properties directly from the environment. This commit modifies the base Github.connect() method to resolve credentials in two steps: 1. ~/.github credentials file if it exists. 2. login, password or oauth variables from the environment A further fromEnvironment() method is provided to support loading from non-standard variable names. The old Github.connect() method would throw an IOException if the ~/.github file did not exist. Now it will fail silently instead dropping back to the anonymous users access level. Added new unit tests into GitHubTest. --- src/main/java/org/kohsuke/github/GitHub.java | 4 +- .../org/kohsuke/github/GitHubBuilder.java | 83 +++++++++++++- .../java/org/kohsuke/github/GitHubTest.java | 102 ++++++++++++++++++ 3 files changed, 184 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/kohsuke/github/GitHub.java b/src/main/java/org/kohsuke/github/GitHub.java index 9037572c7c..66b6ffe1df 100644 --- a/src/main/java/org/kohsuke/github/GitHub.java +++ b/src/main/java/org/kohsuke/github/GitHub.java @@ -128,10 +128,10 @@ public class GitHub { } /** - * Obtains the credential from "~/.github" + * Obtains the credential from "~/.github" or from the System Environment Properties. */ public static GitHub connect() throws IOException { - return GitHubBuilder.fromPropertyFile().build(); + return GitHubBuilder.fromCredentials().build(); } /** diff --git a/src/main/java/org/kohsuke/github/GitHubBuilder.java b/src/main/java/org/kohsuke/github/GitHubBuilder.java index baf26a6415..e08a8dd2a2 100644 --- a/src/main/java/org/kohsuke/github/GitHubBuilder.java +++ b/src/main/java/org/kohsuke/github/GitHubBuilder.java @@ -4,7 +4,9 @@ import java.io.File; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.IOException; +import java.util.Map; import java.util.Properties; /** @@ -12,19 +14,89 @@ */ public class GitHubBuilder { private String endpoint = GitHub.GITHUB_URL; - private String user; - private String password; - private String oauthToken; + + // default scoped so unit tests can read them. + /* private */ String user; + /* private */ String password; + /* private */ String oauthToken; + private HttpConnector connector; public GitHubBuilder() { } + /** + * First check if the credentials are configured using the ~/.github properties file. + * + * If no user is specified it means there is no configuration present so check the environment instead. + * + * the system properties for the login, password or oauth environment variables. + * + * @return the configured Builder from credentials defined on the system or in the environment. + * + * @throws IOException + */ + public static GitHubBuilder fromCredentials() throws IOException { + + GitHubBuilder builder; + try { + builder = fromPropertyFile(); + + if (builder.user != null) + return builder; + else + return fromEnvironment(); + + } catch (FileNotFoundException e) { + return fromEnvironment(); + } + + } + + public static GitHubBuilder fromEnvironment(String loginVariableName, String passwordVariableName, String oauthVariableName) throws IOException { + + + Properties env = new Properties(); + + Object loginValue = System.getenv(loginVariableName); + + if (loginValue != null) + env.put("login", loginValue); + + Object passwordValue = System.getenv(passwordVariableName); + + if (passwordValue != null) + env.put("password", passwordValue); + + Object oauthValue = System.getenv(oauthVariableName); + + if (oauthValue != null) + env.put("oauth", oauthValue); + + return fromProperties(env); + + } + + public static GitHubBuilder fromEnvironment() throws IOException { + + Properties props = new Properties(); + + Map env = System.getenv(); + + for (Map.Entry element : env.entrySet()) { + + props.put(element.getKey(), element.getValue()); + } + + return fromProperties(props); + } + public static GitHubBuilder fromPropertyFile() throws IOException { File homeDir = new File(System.getProperty("user.home")); File propertyFile = new File(homeDir, ".github"); return fromPropertyFile(propertyFile.getPath()); } + public static GitHubBuilder fromPropertyFile(String propertyFileName) throws IOException { Properties props = new Properties(); FileInputStream in = null; @@ -34,6 +106,11 @@ public static GitHubBuilder fromPropertyFile(String propertyFileName) throws IOE } finally { IOUtils.closeQuietly(in); } + + return fromProperties(props); + } + + public static GitHubBuilder fromProperties(Properties props) { GitHubBuilder self = new GitHubBuilder(); self.withOAuthToken(props.getProperty("oauth"), props.getProperty("login")); self.withPassword(props.getProperty("login"), props.getProperty("password")); diff --git a/src/test/java/org/kohsuke/github/GitHubTest.java b/src/test/java/org/kohsuke/github/GitHubTest.java index 6ff39feec9..6bcdb99e4b 100644 --- a/src/test/java/org/kohsuke/github/GitHubTest.java +++ b/src/test/java/org/kohsuke/github/GitHubTest.java @@ -1,5 +1,12 @@ package org.kohsuke.github; +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + import junit.framework.TestCase; /** @@ -21,4 +28,99 @@ public void testGitHubServerWithoutServer() throws Exception { GitHub hub = GitHub.connectUsingPassword("kohsuke", "bogus"); assertEquals("https://api.github.com/test", hub.getApiURL("/test").toString()); } + + public void testGitHubFromEnvironment() throws IOException { + + Mapprops = new HashMap(); + + props.put("login", "bogus"); + + setupEnvironment(props); + + GitHub hub = GitHub.connect(); + + assertEquals("bogus", hub.login); + + } + + public void testGitHubBuilderFromEnvironment() throws IOException { + + Mapprops = new HashMap(); + + props.put("login", "bogus"); + props.put("oauth", "bogus"); + props.put("password", "bogus"); + + setupEnvironment(props); + + GitHubBuilder builder = GitHubBuilder.fromEnvironment(); + + assertEquals("bogus", builder.user); + assertEquals("bogus", builder.oauthToken); + assertEquals("bogus", builder.password); + + } + + /* + * Copied from StackOverflow: http://stackoverflow.com/a/7201825/2336755 + * + * This allows changing the in memory process environment. + * + * Its used to wire in values for the github credentials to test that the GitHubBuilder works properly to resolve them. + */ + private void setupEnvironment(Map newenv) { + try + { + Class processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment"); + Field theEnvironmentField = processEnvironmentClass.getDeclaredField("theEnvironment"); + theEnvironmentField.setAccessible(true); + Map env = (Map) theEnvironmentField.get(null); + env.putAll(newenv); + Field theCaseInsensitiveEnvironmentField = processEnvironmentClass.getDeclaredField("theCaseInsensitiveEnvironment"); + theCaseInsensitiveEnvironmentField.setAccessible(true); + Map cienv = (Map) theCaseInsensitiveEnvironmentField.get(null); + cienv.putAll(newenv); + } + catch (NoSuchFieldException e) + { + try { + Class[] classes = Collections.class.getDeclaredClasses(); + Map env = System.getenv(); + for(Class cl : classes) { + if("java.util.Collections$UnmodifiableMap".equals(cl.getName())) { + Field field = cl.getDeclaredField("m"); + field.setAccessible(true); + Object obj = field.get(env); + Map map = (Map) obj; + map.clear(); + map.putAll(newenv); + } + } + } catch (Exception e2) { + e2.printStackTrace(); + } + } catch (Exception e1) { + e1.printStackTrace(); + } + + } + + public void testGitHubBuilderFromCustomEnvironment() throws IOException { + + Mapprops = new HashMap(); + + props.put("customLogin", "bogusLogin"); + props.put("customOauth", "bogusOauth"); + props.put("customPassword", "bogusPassword"); + + setupEnvironment(props); + + GitHubBuilder builder = GitHubBuilder.fromEnvironment("customLogin", "customPassword", "customOauth"); + + assertEquals("bogusLogin", builder.user); + assertEquals("bogusOauth", builder.oauthToken); + assertEquals("bogusPassword", builder.password); + + } + } From 9802132b6f257b10bca48aaad8fe2f98cc8459aa Mon Sep 17 00:00:00 2001 From: Michael O'Cleirigh Date: Tue, 30 Sep 2014 11:51:57 -0400 Subject: [PATCH 10/15] Add GHCompare.getFiles() method to be able to see the precise files changed. There is a file field inside of GHCompare but no getter to extract the values for analysis. There are contents in that field so I've added a new get method so that they can be extracted. --- src/main/java/org/kohsuke/github/GHCompare.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/kohsuke/github/GHCompare.java b/src/main/java/org/kohsuke/github/GHCompare.java index 0435116995..d61991fa13 100644 --- a/src/main/java/org/kohsuke/github/GHCompare.java +++ b/src/main/java/org/kohsuke/github/GHCompare.java @@ -68,9 +68,12 @@ public Commit getMergeBaseCommit() { public Commit[] getCommits() { return commits; } + + public GHCommit.File[] getFiles() { + return files; + } - - public GHCompare wrap(GHRepository owner) { + public GHCompare wrap(GHRepository owner) { this.owner = owner; for (Commit commit : commits) { commit.wrapUp(owner); From 0f64994537223ddab0afb3c0bf3f95a5af3ee2bb Mon Sep 17 00:00:00 2001 From: Michael O'Cleirigh Date: Tue, 30 Sep 2014 13:58:06 -0400 Subject: [PATCH 11/15] Make Github.connect() fail if no credentials are setup With the previous change if no credentials were defined Github.connect() would fall back on an anonymous connection. This commit changes the behaviour back to what it was before so that if there are no credentials defined in the ~/.github file and no credentials defined in the environment an IOException is thrown to alert the method caller. The caller can call Github.connectAnonymously() if that scenario is allowed. This should handle most cases unless callers are depending on the FileNotFoundException being specifically thrown instead of an IOException. --- .../org/kohsuke/github/GitHubBuilder.java | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/kohsuke/github/GitHubBuilder.java b/src/main/java/org/kohsuke/github/GitHubBuilder.java index e08a8dd2a2..0aa9532506 100644 --- a/src/main/java/org/kohsuke/github/GitHubBuilder.java +++ b/src/main/java/org/kohsuke/github/GitHubBuilder.java @@ -30,11 +30,11 @@ public GitHubBuilder() { * * If no user is specified it means there is no configuration present so check the environment instead. * - * the system properties for the login, password or oauth environment variables. + * If there is still no user it means there are no credentials defined and throw an IOException. * * @return the configured Builder from credentials defined on the system or in the environment. * - * @throws IOException + * @throws IOException If there are no credentials defined in the ~/.github properties file or the process environment. */ public static GitHubBuilder fromCredentials() throws IOException { @@ -44,11 +44,25 @@ public static GitHubBuilder fromCredentials() throws IOException { if (builder.user != null) return builder; - else - return fromEnvironment(); + else { + + // this is the case where the ~/.github file exists but has no content. + + builder = fromEnvironment(); + + if (builder.user != null) + return builder; + else + throw new IOException("Failed to resolve credentials from ~/.github or the environment."); + } } catch (FileNotFoundException e) { - return fromEnvironment(); + builder = fromEnvironment(); + + if (builder.user != null) + return builder; + else + throw new IOException("Failed to resolve credentials from ~/.github or the environment.", e); } } From ebf39eaea1330a3e3fbb35e3c35f557dfd218958 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Wed, 8 Oct 2014 11:28:02 -0700 Subject: [PATCH 12/15] [INFRA-142] Reworked version of https://github.com/kohsuke/github-api/pull/133/files --- src/main/java/org/kohsuke/github/GHTeam.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/kohsuke/github/GHTeam.java b/src/main/java/org/kohsuke/github/GHTeam.java index 54b416edd8..56dafe3978 100644 --- a/src/main/java/org/kohsuke/github/GHTeam.java +++ b/src/main/java/org/kohsuke/github/GHTeam.java @@ -78,9 +78,13 @@ public Map getRepositories() throws IOException { /** * Adds a member to the team. + * + * The user will be invited to the organization if required. + * + * @since 1.59 */ public void add(GHUser u) throws IOException { - org.root.retrieve().method("PUT").to(api("/members/" + u.getLogin()), null); + org.root.retrieve().method("PUT").to(api("/memberships/" + u.getLogin()), null); } /** From 372d5ff7581b9cdca6510c47a579433b291d814b Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Wed, 8 Oct 2014 12:07:09 -0700 Subject: [PATCH 13/15] Fixed a test regression Presumably due to the behaviour change on GitHub API? --- src/main/java/org/kohsuke/github/GHIssue.java | 2 +- .../java/org/kohsuke/github/GHPullRequest.java | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/kohsuke/github/GHIssue.java b/src/main/java/org/kohsuke/github/GHIssue.java index 74a09e88ff..bed8ba30f0 100644 --- a/src/main/java/org/kohsuke/github/GHIssue.java +++ b/src/main/java/org/kohsuke/github/GHIssue.java @@ -129,7 +129,7 @@ public GHIssueState getState() { return Enum.valueOf(GHIssueState.class, state.toUpperCase(Locale.ENGLISH)); } - public Collection