Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[JENKINS-46207] Initial implementation, if lucky this is complete
- I suspect there are additional changes required before can merge for git
- I suspect there are additional changes required to branch-api before safe to merge for users
  • Loading branch information
stephenc committed Aug 31, 2017
1 parent f85fccb commit 837ae64
Show file tree
Hide file tree
Showing 10 changed files with 544 additions and 13 deletions.
158 changes: 146 additions & 12 deletions src/main/java/jenkins/plugins/git/AbstractGitSCMSource.java
Expand Up @@ -81,6 +81,7 @@
import jenkins.plugins.git.traits.RemoteNameSCMSourceTrait;
import jenkins.scm.api.SCMFile;
import jenkins.scm.api.SCMHead;
import jenkins.scm.api.SCMHeadCategory;
import jenkins.scm.api.SCMHeadEvent;
import jenkins.scm.api.SCMHeadObserver;
import jenkins.scm.api.SCMProbe;
Expand All @@ -94,6 +95,7 @@
import jenkins.scm.api.trait.SCMSourceRequest;
import jenkins.scm.api.trait.SCMSourceTrait;
import jenkins.scm.api.trait.SCMTrait;
import jenkins.scm.impl.TagSCMHeadCategory;
import jenkins.scm.impl.trait.WildcardSCMHeadFilterTrait;
import jenkins.scm.impl.trait.WildcardSCMSourceFilterTrait;
import org.apache.commons.lang.StringUtils;
Expand Down Expand Up @@ -355,19 +357,30 @@ private <T, C extends GitSCMSourceContext<C, R>, R extends GitSCMSourceRequest>
@Override
protected SCMRevision retrieve(@NonNull final SCMHead head, @NonNull TaskListener listener)
throws IOException, InterruptedException {
final GitSCMSourceContext context = new GitSCMSourceContext<>(null, SCMHeadObserver.none()).withTraits(getTraits());
return doRetrieve(new Retriever<SCMRevision>() {
@Override
public SCMRevision run(GitClient client, String remoteName) throws IOException, InterruptedException {
for (Branch b : client.getRemoteBranches()) {
String branchName = StringUtils.removeStart(b.getName(), remoteName + "/");
if (branchName.equals(head.getName())) {
return new SCMRevisionImpl(head, b.getSHA1String());
if (head instanceof GitTagSCMHead) {
try {
ObjectId objectId = client.revParse(Constants.R_TAGS + head.getName());
return new GitTagSCMRevision((GitTagSCMHead) head, objectId.name());
} catch (GitException e) {
// tag does not exist
return null;
}
} else {
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;
}
},
new GitSCMSourceContext<>(null, SCMHeadObserver.none()).withTraits(getTraits()),
context,
listener, /* we don't prune remotes here, as we just want one head's revision */false);
}

Expand Down Expand Up @@ -400,7 +413,7 @@ public Void run(GitClient client, String remoteName) throws IOException, Interru
discoverBranches(repository, walk, request, remoteReferences);
}
if (context.wantTags()) {
// TODO
discoverTags(repository, walk, request, remoteReferences);
}
}
return null;
Expand Down Expand Up @@ -509,6 +522,112 @@ public void record(@NonNull SCMHead head, SCMRevision revision, boolean isMatch)
}
listener.getLogger().format("Processed %d branches%n", count);
}

private void discoverTags(final Repository repository,
final RevWalk walk, GitSCMSourceRequest request,
Map<String, ObjectId> remoteReferences)
throws IOException, InterruptedException {
listener.getLogger().println("Checking tags...");
walk.setRetainBody(false);
int count = 0;
for (final Map.Entry<String, ObjectId> ref : remoteReferences.entrySet()) {
if (!ref.getKey().startsWith(Constants.R_TAGS)) {
continue;
}
count++;
final String tagName = StringUtils.removeStart(ref.getKey(), Constants.R_TAGS);
RevCommit commit = walk.parseCommit(ref.getValue());
final long lastModified = TimeUnit.SECONDS.toMillis(commit.getCommitTime());
if (request.process(new GitTagSCMHead(tagName, lastModified),
new SCMSourceRequest.IntermediateLambda<ObjectId>() {
@Nullable
@Override
public ObjectId create() throws IOException, InterruptedException {
listener.getLogger().println(" Checking tag " + tagName);
return ref.getValue();
}
},
new SCMSourceRequest.ProbeLambda<GitTagSCMHead, ObjectId>() {
@NonNull
@Override
public SCMSourceCriteria.Probe create(@NonNull GitTagSCMHead head,
@Nullable ObjectId revisionInfo)
throws IOException, InterruptedException {
RevCommit commit = walk.parseCommit(revisionInfo);
final long lastModified = TimeUnit.SECONDS.toMillis(commit.getCommitTime());
final RevTree tree = commit.getTree();
return new SCMProbe() {
@Override
public void close() throws IOException {
// no-op
}

@Override
public String name() {
return tagName;
}

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

@Override
@NonNull
@SuppressFBWarnings(value = "NP_LOAD_OF_KNOWN_NULL_VALUE",
justification =
"TreeWalk.forPath can return null, compiler "
+ "generated code for try with resources handles it")
public SCMProbeStat stat(@NonNull String path) throws IOException {
try (TreeWalk tw = TreeWalk.forPath(repository, path, tree)) {
if (tw == null) {
return SCMProbeStat.fromType(SCMFile.Type.NONEXISTENT);
}
FileMode fileMode = tw.getFileMode(0);
if (fileMode == FileMode.MISSING) {
return SCMProbeStat.fromType(SCMFile.Type.NONEXISTENT);
}
if (fileMode == FileMode.EXECUTABLE_FILE) {
return SCMProbeStat.fromType(SCMFile.Type.REGULAR_FILE);
}
if (fileMode == FileMode.REGULAR_FILE) {
return SCMProbeStat.fromType(SCMFile.Type.REGULAR_FILE);
}
if (fileMode == FileMode.SYMLINK) {
return SCMProbeStat.fromType(SCMFile.Type.LINK);
}
if (fileMode == FileMode.TREE) {
return SCMProbeStat.fromType(SCMFile.Type.DIRECTORY);
}
return SCMProbeStat.fromType(SCMFile.Type.OTHER);
}
}
};
}
}, new SCMSourceRequest.LazyRevisionLambda<GitTagSCMHead, GitTagSCMRevision, ObjectId>() {
@NonNull
@Override
public GitTagSCMRevision create(@NonNull GitTagSCMHead head, @Nullable ObjectId intermediate)
throws IOException, InterruptedException {
return new GitTagSCMRevision(head, ref.getValue().name());
}
}, new SCMSourceRequest.Witness() {
@Override
public void record(@NonNull SCMHead head, SCMRevision revision, boolean isMatch) {
if (isMatch) {
listener.getLogger().println(" Met criteria");
} else {
listener.getLogger().println(" Does not meet criteria");
}
}
}
)) {
listener.getLogger().format("Processed %d tags (query complete)%n", count);
return;
}
}
listener.getLogger().format("Processed %d tags%n", count);
}
}, context, listener, true);
}

Expand Down Expand Up @@ -557,13 +676,17 @@ protected Set<String> retrieveRevisions(@NonNull final TaskListener listener) th
@Override
public Set<String> run(GitClient client, String remoteName) throws IOException, InterruptedException {
Set<String> revisions = new HashSet<String>();
for (Branch branch : client.getRemoteBranches()) {
revisions.add(branch.getName().replaceFirst(
"^" + Pattern.quote(context.remoteName()) + "/",
""
));
if (context.wantBranches()) {
for (Branch branch : client.getRemoteBranches()) {
revisions.add(branch.getName().replaceFirst(
"^" + Pattern.quote(context.remoteName()) + "/",
""
));
}
}
if (context.wantTags()) {
revisions.addAll(client.getTagNames("*"));
}
revisions.addAll(client.getTagNames("*"));
return revisions;
}
},
Expand Down Expand Up @@ -659,6 +782,17 @@ protected List<Action> retrieveActions(@NonNull SCMHead head, @CheckForNull SCMH
return Collections.emptyList();
}

/**
* {@inheritDoc}
*/
@Override
protected boolean isCategoryEnabled(@NonNull SCMHeadCategory category) {
if (category instanceof TagSCMHeadCategory) {
return new GitSCMSourceContext<>(null, SCMHeadObserver.none()).withTraits(getTraits()).wantTags();
}
return super.isCategoryEnabled(category);
}

protected String getCacheEntry() {
return getCacheEntry(getRemote());
}
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/jenkins/plugins/git/GitSCMSource.java
Expand Up @@ -72,6 +72,7 @@
import jenkins.plugins.git.traits.RemoteNameSCMSourceTrait;
import jenkins.scm.api.SCMEvent;
import jenkins.scm.api.SCMHead;
import jenkins.scm.api.SCMHeadCategory;
import jenkins.scm.api.SCMHeadEvent;
import jenkins.scm.api.SCMHeadObserver;
import jenkins.scm.api.SCMNavigator;
Expand All @@ -84,6 +85,8 @@
import jenkins.scm.api.trait.SCMSourceTrait;
import jenkins.scm.api.trait.SCMSourceTraitDescriptor;
import jenkins.scm.api.trait.SCMTrait;
import jenkins.scm.impl.TagSCMHeadCategory;
import jenkins.scm.impl.UncategorizedSCMHeadCategory;
import jenkins.scm.impl.form.NamedArrayList;
import jenkins.scm.impl.trait.Discovery;
import jenkins.scm.impl.trait.Selection;
Expand Down Expand Up @@ -534,6 +537,12 @@ public List<NamedArrayList<? extends SCMSourceTraitDescriptor>> getTraitsDescrip
public List<SCMSourceTrait> getTraitsDefaults() {
return Collections.<SCMSourceTrait>singletonList(new BranchDiscoveryTrait());
}

@NonNull
@Override
protected SCMHeadCategory[] createCategories() {
return new SCMHeadCategory[]{UncategorizedSCMHeadCategory.DEFAULT, TagSCMHeadCategory.DEFAULT};
}
}

@Extension
Expand Down
62 changes: 62 additions & 0 deletions src/main/java/jenkins/plugins/git/GitTagSCMHead.java
@@ -0,0 +1,62 @@
/*
* 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 jenkins.plugins.git;

import edu.umd.cs.findbugs.annotations.NonNull;
import jenkins.scm.api.SCMHead;
import jenkins.scm.api.mixin.TagSCMHead;

/**
* Represents a Git Tag.
*
* @since TODO
*/
public class GitTagSCMHead extends SCMHead implements TagSCMHead {
/**
* The timestamp of the tag, for lightweigh tags this should be the last commit, for annotated
* tags this should be the tag date.
*/
private final long timestamp;

/**
* Constructor.
*
* @param name the name.
* @param timestamp the timestamp of the tag, for lightweigh tags this should be the last commit, for annotated
* tags this should be the tag date.
*/
public GitTagSCMHead(@NonNull String name, long timestamp) {
super(name);
this.timestamp = timestamp;
}

/**
* {@inheritDoc}
*/
@Override
public long getTimestamp() {
return timestamp;
}

}
43 changes: 43 additions & 0 deletions src/main/java/jenkins/plugins/git/GitTagSCMRevision.java
@@ -0,0 +1,43 @@
/*
* 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 jenkins.plugins.git;

import edu.umd.cs.findbugs.annotations.NonNull;

/**
* Represents the revision of a Git Tag.
*
* @since TODO
*/
public class GitTagSCMRevision extends AbstractGitSCMSource.SCMRevisionImpl {
/**
* Constructor.
*
* @param head the head.
* @param hash the revision hash.
*/
public GitTagSCMRevision(@NonNull GitTagSCMHead head, @NonNull String hash) {
super(head, hash);
}
}
10 changes: 10 additions & 0 deletions src/main/java/jenkins/plugins/git/traits/BranchDiscoveryTrait.java
Expand Up @@ -33,6 +33,8 @@
import jenkins.scm.api.SCMHeadOrigin;
import jenkins.scm.api.SCMRevision;
import jenkins.scm.api.SCMSource;
import jenkins.scm.api.mixin.SCMHeadMixin;
import jenkins.scm.api.mixin.TagSCMHead;
import jenkins.scm.api.trait.SCMBuilder;
import jenkins.scm.api.trait.SCMHeadAuthority;
import jenkins.scm.api.trait.SCMHeadAuthorityDescriptor;
Expand Down Expand Up @@ -146,6 +148,14 @@ public String getDisplayName() {
public boolean isApplicableToOrigin(@NonNull Class<? extends SCMHeadOrigin> originClass) {
return SCMHeadOrigin.Default.class.isAssignableFrom(originClass);
}

/**
* {@inheritDoc}
*/
@Override
public boolean isApplicableToHead(@NonNull Class<? extends SCMHeadMixin> headClass) {
return super.isApplicableToHead(headClass) && !(TagSCMHead.class.isAssignableFrom(headClass));
}
}
}
}

0 comments on commit 837ae64

Please sign in to comment.