Skip to content

Commit

Permalink
[JENKINS-31155] Implement new retrieve(String, TaskListener) overload.
Browse files Browse the repository at this point in the history
  • Loading branch information
jglick committed Aug 17, 2016
1 parent 3515970 commit 2b201d8
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 94 deletions.
4 changes: 2 additions & 2 deletions pom.xml
Expand Up @@ -3,7 +3,7 @@
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>2.12</version>
<version>2.14-SNAPSHOT</version> <!-- https://github.com/jenkinsci/plugin-pom/pull/33 -->
</parent>

<licenses>
Expand Down Expand Up @@ -173,7 +173,7 @@
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>scm-api</artifactId>
<version>1.0</version>
<version>1.3-SNAPSHOT</version> <!-- TODO https://github.com/jenkinsci/scm-api-plugin/pull/12 -->
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
Expand Down
205 changes: 113 additions & 92 deletions src/main/java/jenkins/plugins/git/AbstractGitSCMSource.java
Expand Up @@ -162,9 +162,10 @@ protected GitTool resolveGitTool() {
return tool;
}

@CheckForNull
@Override
protected SCMRevision retrieve(@NonNull SCMHead head, @NonNull TaskListener listener)
private interface Retriever<T> {
T run(GitClient client, String remoteName) throws IOException, InterruptedException;
}
private <T> T doRetrieve(Retriever<T> retriever, @NonNull TaskListener listener)
throws IOException, InterruptedException {
String cacheEntry = getCacheEntry();
Lock cacheLock = getCacheLock(cacheEntry);
Expand All @@ -185,19 +186,31 @@ protected SCMRevision retrieve(@NonNull SCMHead head, @NonNull TaskListener list
listener.getLogger().println("Fetching " + remoteName + "...");
List<RefSpec> refSpecs = getRefSpecs();
client.fetch(remoteName, refSpecs.toArray(new RefSpec[refSpecs.size()]));
// we don't prune remotes here, as we just want one head's revision
for (Branch b : client.getRemoteBranches()) {
String branchName = StringUtils.removeStart(b.getName(), remoteName + "/");
if (branchName.equals(head.getName())) {
return new SCMRevisionImpl(head, b.getSHA1String());
}
}
return null;
return retriever.run(client, remoteName);
} finally {
cacheLock.unlock();
}
}

@CheckForNull
@Override
protected SCMRevision retrieve(@NonNull final SCMHead head, @NonNull TaskListener listener)
throws IOException, InterruptedException {
return doRetrieve(new Retriever<SCMRevision>() {
@Override
public SCMRevision run(GitClient client, String remoteName) throws IOException, InterruptedException {
// we don't prune remotes here, as we just want one head's revision
for (Branch b : client.getRemoteBranches()) {
String branchName = StringUtils.removeStart(b.getName(), remoteName + "/");
if (branchName.equals(head.getName())) {
return new SCMRevisionImpl(head, b.getSHA1String());
}
}
return null;
}
}, listener);
}

private static void _close(@NonNull Object walk) {
java.lang.reflect.Method closeMethod;
try {
Expand Down Expand Up @@ -259,97 +272,105 @@ private void _release(RevWalk walk) {
@NonNull
@Override
protected void retrieve(@NonNull final SCMHeadObserver observer,
@NonNull TaskListener listener)
@NonNull final TaskListener listener)
throws IOException, InterruptedException {
String cacheEntry = getCacheEntry();
Lock cacheLock = getCacheLock(cacheEntry);
cacheLock.lock();
try {
File cacheDir = getCacheDir(cacheEntry);
GitTool tool = resolveGitTool();
Git git = Git.with(listener, new EnvVars(EnvVars.masterEnvVars)).using(tool.getGitExe()).in(cacheDir);
GitClient client = git.getClient();
client.addDefaultCredentials(getCredentials());
if (!client.hasGitRepo()) {
listener.getLogger().println("Creating git repository in " + cacheDir);
client.init();
}
String remoteName = getRemoteName();
listener.getLogger().println("Setting " + remoteName + " to " + getRemote());
client.setRemoteUrl(remoteName, getRemote());
listener.getLogger().println("Fetching " + remoteName + "...");
List<RefSpec> refSpecs = getRefSpecs();
client.fetch(remoteName, refSpecs.toArray(new RefSpec[refSpecs.size()]));
listener.getLogger().println("Pruning stale remotes...");
final Repository repository = client.getRepository();
try {
client.prune(new RemoteConfig(repository.getConfig(), remoteName));
} catch (UnsupportedOperationException e) {
e.printStackTrace(listener.error("Could not prune stale remotes"));
} catch (URISyntaxException e) {
e.printStackTrace(listener.error("Could not prune stale remotes"));
}
listener.getLogger().println("Getting remote branches...");
SCMSourceCriteria branchCriteria = getCriteria();
RevWalk walk = new RevWalk(repository);
try {
walk.setRetainBody(false);
for (Branch b : client.getRemoteBranches()) {
if (!b.getName().startsWith(remoteName + "/")) {
continue;
}
final String branchName = StringUtils.removeStart(b.getName(), remoteName + "/");
listener.getLogger().println("Checking branch " + branchName);
if (isExcluded(branchName)){
continue;
}
if (branchCriteria != null) {
RevCommit commit = walk.parseCommit(b.getSHA1());
final long lastModified = TimeUnit.SECONDS.toMillis(commit.getCommitTime());
final RevTree tree = commit.getTree();
SCMSourceCriteria.Probe probe = new SCMSourceCriteria.Probe() {
@Override
public String name() {
return branchName;
}
doRetrieve(new Retriever<Void>() {
@Override
public Void run(GitClient client, String remoteName) throws IOException, InterruptedException {
listener.getLogger().println("Pruning stale remotes...");
final Repository repository = client.getRepository();
try {
client.prune(new RemoteConfig(repository.getConfig(), remoteName));
} catch (UnsupportedOperationException e) {
e.printStackTrace(listener.error("Could not prune stale remotes"));
} catch (URISyntaxException e) {
e.printStackTrace(listener.error("Could not prune stale remotes"));
}
listener.getLogger().println("Getting remote branches...");
SCMSourceCriteria branchCriteria = getCriteria();
RevWalk walk = new RevWalk(repository);
try {
walk.setRetainBody(false);
for (Branch b : client.getRemoteBranches()) {
if (!b.getName().startsWith(remoteName + "/")) {
continue;
}
final String branchName = StringUtils.removeStart(b.getName(), remoteName + "/");
listener.getLogger().println("Checking branch " + branchName);
if (isExcluded(branchName)) {
continue;
}
if (branchCriteria != null) {
RevCommit commit = walk.parseCommit(b.getSHA1());
final long lastModified = TimeUnit.SECONDS.toMillis(commit.getCommitTime());
final RevTree tree = commit.getTree();
SCMSourceCriteria.Probe probe = new SCMSourceCriteria.Probe() {
@Override
public String name() {
return branchName;
}

@Override
public long lastModified() {
return lastModified;
}
@Override
public long lastModified() {
return lastModified;
}

@Override
public boolean exists(@NonNull String path) throws IOException {
TreeWalk tw = TreeWalk.forPath(repository, path, tree);
try {
return tw != null;
} finally {
_release(tw);
@Override
public boolean exists(@NonNull String path) throws IOException {
TreeWalk tw = TreeWalk.forPath(repository, path, tree);
try {
return tw != null;
} finally {
_release(tw);
}
}
};
if (branchCriteria.isHead(probe, listener)) {
listener.getLogger().println("Met criteria");
} else {
listener.getLogger().println("Does not meet criteria");
continue;
}
};
if (branchCriteria.isHead(probe, listener)) {
listener.getLogger().println("Met criteria");
} else {
listener.getLogger().println("Does not meet criteria");
continue;
}
SCMHead head = new SCMHead(branchName);
SCMRevision hash = new SCMRevisionImpl(head, b.getSHA1String());
observer.observe(head, hash);
if (!observer.isObserving()) {
return null;
}
}
SCMHead head = new SCMHead(branchName);
SCMRevision hash = new SCMRevisionImpl(head, b.getSHA1String());
observer.observe(head, hash);
if (!observer.isObserving()) {
return;
}
} finally {
_release(walk);
}
} finally {
_release(walk);

listener.getLogger().println("Done.");
return null;
}
}, listener);
}

listener.getLogger().println("Done.");
} finally {
cacheLock.unlock();
}
@CheckForNull
@Override
protected SCMRevision retrieve(@NonNull final String revision, @NonNull final TaskListener listener) throws IOException, InterruptedException {
return doRetrieve(new Retriever<SCMRevision>() {
@Override
public SCMRevision run(GitClient client, String remoteName) throws IOException, InterruptedException {
String hash;
try {
hash = client.revParse(revision).name();
} catch (GitException x) {
// Try prepending origin/ in case it was a branch.
try {
hash = client.revParse("origin/" + revision).name();
} catch (GitException x2) {
listener.getLogger().println(x.getMessage());
listener.getLogger().println(x2.getMessage());
return null;
}
}
return new SCMRevisionImpl(new SCMHead(revision), hash);
}
}, listener);
}

protected String getCacheEntry() {
Expand Down
45 changes: 45 additions & 0 deletions src/test/java/jenkins/plugins/git/AbstractGitSCMSourceTest.java
@@ -1,7 +1,12 @@
package jenkins.plugins.git;

import hudson.FilePath;
import hudson.Launcher;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.scm.SCMRevisionState;
import hudson.util.StreamTaskListener;
import jenkins.scm.api.SCMRevision;
import jenkins.scm.api.SCMSource;
import org.junit.Rule;
import org.junit.Test;
Expand Down Expand Up @@ -77,4 +82,44 @@ public void retrieveHeads() throws Exception {
assertEquals("[SCMHead{'dev'}, SCMHead{'dev2'}, SCMHead{'master'}]", source.fetch(listener).toString());
}

@Issue("JENKINS-31155")
@Test
public void retrieveRevision() throws Exception {
sampleRepo.init();
sampleRepo.write("file", "v1");
sampleRepo.git("commit", "--all", "--message=v1");
sampleRepo.git("tag", "v1");
String v1 = sampleRepo.head();
sampleRepo.write("file", "v2");
sampleRepo.git("commit", "--all", "--message=v2"); // master
sampleRepo.git("checkout", "-b", "dev");
sampleRepo.write("file", "v3");
sampleRepo.git("commit", "--all", "--message=v3"); // dev
// SCM.checkout does not permit a null build argument, unfortunately.
Run<?,?> run = r.buildAndAssertSuccess(r.createFreeStyleProject());
// Test retrieval of branches:
assertEquals("v2", fileAt("master", run));
assertEquals("v3", fileAt("dev", run));
// Tags:
assertEquals("v1", fileAt("v1", run));
// And commit hashes:
assertEquals("v1", fileAt(v1, run));
assertEquals("v1", fileAt(v1.substring(0, 7), run));
// Nonexistent stuff:
assertNull(fileAt("nonexistent", run));
assertNull(fileAt("1234567", run));
}
private String fileAt(String revision, Run<?,?> run) throws Exception {
SCMSource source = new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true);
TaskListener listener = StreamTaskListener.fromStderr();
SCMRevision rev = source.fetch(revision, listener);
if (rev == null) {
return null;
} else {
FilePath ws = new FilePath(run.getRootDir()).child("tmp-" + revision);
source.build(rev.getHead(), rev).checkout(run, new Launcher.LocalLauncher(listener), ws, listener, null, SCMRevisionState.NONE);
return ws.child("file").readToString();
}
}

}
7 changes: 7 additions & 0 deletions src/test/java/jenkins/plugins/git/GitSampleRepoRule.java
Expand Up @@ -26,6 +26,8 @@

import com.gargoylesoftware.htmlunit.WebResponse;
import com.gargoylesoftware.htmlunit.util.NameValuePair;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.RepositoryBuilder;
import org.jenkinsci.plugins.workflow.steps.scm.AbstractSampleDVCSRepoRule;
import org.jvnet.hudson.test.JenkinsRule;

Expand Down Expand Up @@ -59,4 +61,9 @@ public void notifyCommit(JenkinsRule r) throws Exception {
r.waitUntilNoActivity();
}

/** Returns the (full) commit hash of the current {@link Constants#HEAD} of the repository. */
public String head() throws Exception {
return new RepositoryBuilder().setWorkTree(sampleRepo).build().resolve(Constants.HEAD).name();
}

}

0 comments on commit 2b201d8

Please sign in to comment.