Skip to content

Commit

Permalink
Merge pull request #124 from stephenc/jenkins-36121
Browse files Browse the repository at this point in the history
[JENKINS-36121] Do not trip the rate limit
  • Loading branch information
stephenc committed Mar 2, 2017
2 parents 1dbf190 + b59c83d commit 2b7a7f9
Show file tree
Hide file tree
Showing 10 changed files with 1,057 additions and 538 deletions.
4 changes: 2 additions & 2 deletions pom.xml
Expand Up @@ -35,7 +35,7 @@
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>scm-api</artifactId>
<version>2.0.4</version>
<version>2.0.7</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
Expand All @@ -52,7 +52,7 @@
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>github-api</artifactId>
<version>1.84</version>
<version>1.85</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
Expand Down
307 changes: 266 additions & 41 deletions src/main/java/org/jenkinsci/plugins/github_branch_source/Connector.java

Large diffs are not rendered by default.

Large diffs are not rendered by default.

@@ -0,0 +1,35 @@
/*
* The MIT License
*
* Copyright (c) 2017 CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package org.jenkinsci.plugins.github_branch_source;

import java.io.Closeable;

/**
* Package private interface to allow {@link GitHubSCMFile} and {@link GitHubSCMFileSystem} to be aware of the state
* of their connection.
*/
interface GitHubClosable extends Closeable {
boolean isOpen();
}
@@ -0,0 +1,77 @@
/*
* The MIT License
*
* Copyright 2017 CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package org.jenkinsci.plugins.github_branch_source;

import hudson.Extension;
import hudson.MarkupText;
import hudson.console.ConsoleAnnotationDescriptor;
import hudson.console.ConsoleAnnotator;
import hudson.console.ConsoleNote;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;

/**
* A timestamped console note from the GitHub branch source.
*/
@Restricted(NoExternalUse.class)
public class GitHubConsoleNote extends ConsoleNote {

private static final Logger LOGGER = Logger.getLogger(GitHubConsoleNote.class.getName());

private static final long serialVersionUID = 1L;

private final long timestamp;

public GitHubConsoleNote(long timestamp) {
this.timestamp = timestamp;
}

@Override
public ConsoleAnnotator annotate(Object context, MarkupText text, int charPos) {
text.addMarkup(0, text.length(), String.format("<span class='greyed'><small>%tT</small> ", timestamp), "</span>");
return null;
}

public static String create(long timestamp, String text) {
try {
return new GitHubConsoleNote(timestamp).encode() + text;
} catch (IOException e) {
// impossible, but don't make this a fatal problem
LOGGER.log(Level.WARNING, "Failed to serialize " + GitHubConsoleNote.class, e);
return text;
}
}

@Extension
public static final class DescriptorImpl extends ConsoleAnnotationDescriptor {
public String getDisplayName() {
return "GitHub API Usage";
}
}

}
Expand Up @@ -33,20 +33,21 @@
import java.util.ArrayList;
import java.util.List;
import jenkins.scm.api.SCMFile;
import org.apache.commons.lang.StringUtils;
import org.kohsuke.github.GHContent;
import org.kohsuke.github.GHRepository;

class GitHubSCMFile extends SCMFile {

private TypeInfo info;
private final GitHubClosable closable;
private final GHRepository repo;
private final String ref;
private transient Object metadata;
private transient boolean resolved;

GitHubSCMFile(GHRepository repo, String ref) {
GitHubSCMFile(GitHubClosable closable, GHRepository repo, String ref) {
super();
this.closable = closable;
type(Type.DIRECTORY);
info = TypeInfo.DIRECTORY_ASSUMED; // we have not resolved the metadata yet
this.repo = repo;
Expand All @@ -55,13 +56,15 @@ class GitHubSCMFile extends SCMFile {

private GitHubSCMFile(@NonNull GitHubSCMFile parent, String name, TypeInfo info) {
super(parent, name);
this.closable = parent.closable;
this.info = info;
this.repo = parent.repo;
this.ref = parent.ref;
}

private GitHubSCMFile(@NonNull GitHubSCMFile parent, String name, GHContent metadata) {
super(parent, name);
this.closable = parent.closable;
this.repo = parent.repo;
this.ref = parent.ref;
if (metadata.isDirectory()) {
Expand All @@ -74,6 +77,12 @@ private GitHubSCMFile(@NonNull GitHubSCMFile parent, String name, GHContent meta
}
}

private void checkOpen() throws IOException {
if (!closable.isOpen()) {
throw new IOException("Closed");
}
}

private Object metadata() throws IOException {
if (metadata == null && !resolved) {
try {
Expand All @@ -92,6 +101,7 @@ private Object metadata() throws IOException {
resolved = true;
break;
case UNRESOLVED:
checkOpen();
try {
metadata = repo.getFileContent(getPath(), ref);
info = TypeInfo.NON_DIRECTORY_CONFIRMED;
Expand Down Expand Up @@ -125,6 +135,7 @@ protected SCMFile newChild(String name, boolean assumeIsDirectory) {
@NonNull
@Override
public Iterable<SCMFile> children() throws IOException {
checkOpen();
List<GHContent> content = repo.getDirectoryContent(getPath(), ref);
List<SCMFile> result = new ArrayList<>(content.size());
for (GHContent c : content) {
Expand Down
Expand Up @@ -38,17 +38,20 @@
import jenkins.scm.api.SCMHead;
import jenkins.scm.api.SCMRevision;
import jenkins.scm.api.SCMSource;
import org.apache.commons.lang.StringUtils;
import org.kohsuke.github.GHRepository;
import org.kohsuke.github.GitHub;
import org.kohsuke.github.HttpException;

public class GitHubSCMFileSystem extends SCMFileSystem {
public class GitHubSCMFileSystem extends SCMFileSystem implements GitHubClosable {
private final GitHub gitHub;
private final GHRepository repo;
private final String ref;
private boolean open;

protected GitHubSCMFileSystem(GHRepository repo, String ref, @CheckForNull SCMRevision rev) throws IOException {
protected GitHubSCMFileSystem(GitHub gitHub, GHRepository repo, String ref, @CheckForNull SCMRevision rev) throws IOException {
super(rev);
this.gitHub = gitHub;
this.open = true;
this.repo = repo;
if (rev != null) {
if (rev.getHead() instanceof PullRequestSCMHead) {
Expand All @@ -65,6 +68,22 @@ protected GitHubSCMFileSystem(GHRepository repo, String ref, @CheckForNull SCMRe
}
}

@Override
public void close() throws IOException {
synchronized (this) {
if (!open) {
return;
}
open = false;
}
Connector.release(gitHub);
}

@Override
public synchronized boolean isOpen() {
return open;
}

@Override
public long lastModified() throws IOException {
// TODO figure out how to implement this
Expand All @@ -74,7 +93,7 @@ public long lastModified() throws IOException {
@NonNull
@Override
public SCMFile getRoot() {
return new GitHubSCMFile(repo, ref);
return new GitHubSCMFile(this, repo, ref);
}

@Extension
Expand Down Expand Up @@ -107,33 +126,42 @@ public SCMFileSystem build(@NonNull SCMSource source, @NonNull SCMHead head, @Ch
// Github client and validation
GitHub github = Connector.connect(apiUri, credentials);
try {
github.checkApiUrlValidity();
} catch (HttpException e) {
String message = String.format("It seems %s is unreachable",
apiUri == null ? GitHubSCMSource.GITHUB_URL : apiUri);
throw new IOException(message);
}
String ref;
if (head instanceof BranchSCMHead) {
ref = head.getName();
} else if (head instanceof PullRequestSCMHead) {
PullRequestSCMHead pr = (PullRequestSCMHead) head;
if (!pr.isMerge() && pr.getSourceRepo() != null) {
return new GitHubSCMFileSystem(
github.getUser(pr.getSourceOwner()).getRepository(pr.getSourceRepo()),
pr.getSourceBranch(),
rev);
try {
github.checkApiUrlValidity();
} catch (HttpException e) {
String message = String.format("It seems %s is unreachable",
apiUri == null ? GitHubSCMSource.GITHUB_URL : apiUri);
throw new IOException(message);
}
String ref;
if (head instanceof BranchSCMHead) {
ref = head.getName();
} else if (head instanceof PullRequestSCMHead) {
PullRequestSCMHead pr = (PullRequestSCMHead) head;
if (!pr.isMerge() && pr.getSourceRepo() != null) {
return new GitHubSCMFileSystem(
github, github.getUser(pr.getSourceOwner()).getRepository(pr.getSourceRepo()),
pr.getSourceBranch(),
rev);
}
// we need to release here as we are not throwing an exception or transferring responsibility to FS
Connector.release(github);
return null; // TODO support merge revisions somehow
} else {
// we need to release here as we are not throwing an exception or transferring responsibility to FS
Connector.release(github);
return null;
}
return null; // TODO support merge revisions somehow
} else {
return null;
}

GHRepository repo = github.getUser(src.getRepoOwner()).getRepository(src.getRepository());
if (rev == null) {
rev = new AbstractGitSCMSource.SCMRevisionImpl((BranchSCMHead) head, repo.getBranch(ref).getSHA1());
GHRepository repo = github.getUser(src.getRepoOwner()).getRepository(src.getRepository());
if (rev == null) {
rev = new AbstractGitSCMSource.SCMRevisionImpl((BranchSCMHead) head, repo.getBranch(ref).getSHA1());
}
return new GitHubSCMFileSystem(github, repo, ref, rev);
} catch (IOException | RuntimeException e) {
Connector.release(github);
throw e;
}
return new GitHubSCMFileSystem(repo, ref, rev);
}
}
}

0 comments on commit 2b7a7f9

Please sign in to comment.