Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
<!-- https://www.jenkins.io/doc/developer/plugin-development/choosing-jenkins-baseline/ -->
<jenkins.baseline>2.528</jenkins.baseline>
<jenkins.version>2.539</jenkins.version>
<hpi.bundledArtifacts>natural-comparator</hpi.bundledArtifacts>
<spotless.check.skip>false</spotless.check.skip>
<useBeta>true</useBeta>
</properties>
Expand Down Expand Up @@ -77,6 +78,11 @@
<groupId>io.jenkins.plugins</groupId>
<artifactId>okhttp-api</artifactId>
</dependency>
<dependency>
<groupId>net.grey-panther</groupId>
<artifactId>natural-comparator</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>credentials</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ public static GitHubConfiguration get() {

private ApiRateLimitChecker apiRateLimitChecker;

private boolean tagDescendingOrder;

private int maxTagCount;

public GitHubConfiguration() {
load();
}
Expand Down Expand Up @@ -83,6 +87,44 @@ public synchronized void setApiRateLimitChecker(@CheckForNull ApiRateLimitChecke
save();
}

/**
* Returns whether tags should be scanned in descending order by default.
*
* @return {@code true} if tags should be scanned in descending order by default.
*/
public synchronized boolean isTagDescendingOrder() {
return tagDescendingOrder;
}

/**
* Sets whether tags should be scanned in descending order by default.
*
* @param tagDescendingOrder {@code true} to scan tags in descending order by default.
*/
public synchronized void setTagDescendingOrder(boolean tagDescendingOrder) {
this.tagDescendingOrder = tagDescendingOrder;
save();
}

/**
* Returns the default maximum number of tags to process (0 = unlimited).
*
* @return the default maximum number of tags to process.
*/
public synchronized int getMaxTagCount() {
return maxTagCount;
}

/**
* Sets the default maximum number of tags to process.
*
* @param maxTagCount maximum number of tags (0 = unlimited).
*/
public synchronized void setMaxTagCount(int maxTagCount) {
this.maxTagCount = maxTagCount;
save();
}

/**
* Fix an apiUri.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@
import jenkins.scm.impl.trait.Selection;
import jenkins.scm.impl.trait.WildcardSCMHeadFilterTrait;
import jenkins.util.SystemProperties;
import net.greypanther.natsort.SimpleNaturalComparator;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jgit.lib.Constants;
import org.jenkinsci.Symbol;
Expand Down Expand Up @@ -1185,6 +1186,11 @@
listener.getLogger().format("%n %d tags were processed (query completed)%n", count);
break;
}
int maxTagCount = request.getMaxTagCount();
if (maxTagCount > 0 && count >= maxTagCount) {
listener.getLogger().format("%n %d tags were processed (limit reached)%n", count);
break;

Check warning on line 1192 in src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubSCMSource.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 1189-1192 are not covered by tests
}
}
listener.getLogger().format("%n %d tags were processed%n", count);
}
Expand Down Expand Up @@ -2665,10 +2671,12 @@
static class LazyTags extends LazyIterable<GHRef> {
private final GitHubSCMSourceRequest request;
private final GHRepository repo;
private final boolean descendingOrder;

public LazyTags(GitHubSCMSourceRequest request, GHRepository repo) {
this.request = request;
this.repo = repo;
this.descendingOrder = request.isTagDescendingOrder();
}

@Override
Expand Down Expand Up @@ -2701,77 +2709,102 @@
//
// Instead we just return a wrapped iterator that does the right thing.
final Iterable<GHRef> iterable = repo.listRefs("tags");

if (descendingOrder) {
// Collect all tags and reverse for descending order.
// This loses lazy-loading but the GitHub REST API does not support
// reverse ordering on refs, so eager collection is unavoidable.
List<GHRef> allTags = collectTags(iterable);
allTags.sort(Comparator.comparing(
(GHRef ref) -> ref.getRef(),
SimpleNaturalComparator.getInstance().reversed()));
return allTags;
}

return new Iterable<GHRef>() {
@Override
public Iterator<GHRef> iterator() {
final Iterator<GHRef> iterator;
try {
iterator = iterable.iterator();
} catch (Error e) {
if (e.getCause() instanceof GHFileNotFoundException) {
return Collections.emptyIterator();
}
throw e;
}
return new Iterator<GHRef>() {
boolean hadAtLeastOne;
boolean hasNone;

@Override
public boolean hasNext() {
try {
boolean hasNext = iterator.hasNext();
hadAtLeastOne = hadAtLeastOne || hasNext;
return hasNext;
} catch (Error e) {
// pre https://github.com/kohsuke/github-api/commit
// /a17ce04552ddd3f6bd8210c740184e6c7ad13ae4
// we at least got the cause, even if wrapped in an Error
if (e.getCause() instanceof GHFileNotFoundException) {
return false;
}
throw e;
} catch (GHException e) {
// JENKINS-52397 I have no clue why https://github.com/kohsuke/github-api/commit
// /a17ce04552ddd3f6bd8210c740184e6c7ad13ae4 does what it does, but it makes
// it rather difficult to distinguish between a network outage and the file
// not found.
if (hadAtLeastOne) {
throw e;
}
try {
hasNone = hasNone || repo.getRefs("tags").length == 0;
if (hasNone) return false;
throw e;
} catch (FileNotFoundException e1) {
hasNone = true;
return false;
} catch (IOException e1) {
e.addSuppressed(e1);
throw e;
}
}
}

@Override
public GHRef next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return iterator.next();
}

@Override
public void remove() {
throw new UnsupportedOperationException("remove");
}
};
return safeTagIterator(iterable);
}
};
} catch (IOException e) {
throw new GitHubSCMSource.WrappedException(e);

Check warning on line 2731 in src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubSCMSource.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 2730-2731 are not covered by tests
}
}

private List<GHRef> collectTags(Iterable<GHRef> iterable) {
List<GHRef> result = new ArrayList<>();
Iterator<GHRef> it = safeTagIterator(iterable);
while (it.hasNext()) {
result.add(it.next());
}
return result;
}

private Iterator<GHRef> safeTagIterator(Iterable<GHRef> iterable) {
final Iterator<GHRef> iterator;
try {
iterator = iterable.iterator();
} catch (Error e) {
if (e.getCause() instanceof GHFileNotFoundException) {
return Collections.emptyIterator();
}
throw e;
}
return new Iterator<GHRef>() {
boolean hadAtLeastOne;
boolean hasNone;

@Override
public boolean hasNext() {
try {
boolean hasNext = iterator.hasNext();
hadAtLeastOne = hadAtLeastOne || hasNext;
return hasNext;
} catch (Error e) {
// pre https://github.com/kohsuke/github-api/commit
// /a17ce04552ddd3f6bd8210c740184e6c7ad13ae4
// we at least got the cause, even if wrapped in an Error
if (e.getCause() instanceof GHFileNotFoundException) {
return false;
}
throw e;
} catch (GHException e) {
// JENKINS-52397 I have no clue why https://github.com/kohsuke/github-api/commit
// /a17ce04552ddd3f6bd8210c740184e6c7ad13ae4 does what it does, but it makes
// it rather difficult to distinguish between a network outage and the file
// not found.
if (hadAtLeastOne) {
throw e;
}
try {
hasNone = hasNone || repo.getRefs("tags").length == 0;
if (hasNone) return false;
throw e;
} catch (FileNotFoundException e1) {
hasNone = true;
return false;
} catch (IOException e1) {
e.addSuppressed(e1);
throw e;
}
}
}

@Override
public GHRef next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return iterator.next();
}

@Override
public void remove() {
throw new UnsupportedOperationException("remove");
}
};
}
}

private static class CriteriaWitness implements SCMSourceRequest.Witness {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ public class GitHubSCMSourceContext extends SCMSourceContext<GitHubSCMSourceCont
/** Set of {@link ChangeRequestCheckoutStrategy} to create for each fork pull request. */
@NonNull
private Set<ChangeRequestCheckoutStrategy> forkPRStrategies = EnumSet.noneOf(ChangeRequestCheckoutStrategy.class);
/** {@code true} if tags should be scanned in descending (reverse alphabetical) order. */
private boolean tagDescendingOrder;
/** Maximum number of tags to process (0 = unlimited). */
private int maxTagCount;
/** {@code true} if notifications should be disabled in this context. */
private boolean notificationsDisabled;
/**
Expand Down Expand Up @@ -102,6 +106,24 @@ public final boolean wantTags() {
return wantTags;
}

/**
* Returns {@code true} if tags should be scanned in descending order.
*
* @return {@code true} if tags should be scanned in descending order.
*/
public final boolean isTagDescendingOrder() {
return tagDescendingOrder;
}

/**
* Returns the maximum number of tags to process (0 = unlimited).
*
* @return the maximum number of tags to process.
*/
public final int getMaxTagCount() {
return maxTagCount;
}

/**
* Returns {@code true} if the {@link GitHubSCMSourceRequest} will need information about pull
* requests.
Expand Down Expand Up @@ -203,6 +225,30 @@ public GitHubSCMSourceContext wantTags(boolean include) {
return this;
}

/**
* Sets whether tags should be scanned in descending (reverse alphabetical) order.
*
* @param descending {@code true} to scan tags in descending order.
* @return {@code this} for method chaining.
*/
@NonNull
public GitHubSCMSourceContext withTagDescendingOrder(boolean descending) {
tagDescendingOrder = descending;
return this;
}

/**
* Sets the maximum number of tags to process.
*
* @param maxTagCount maximum number of tags (0 = unlimited).
* @return {@code this} for method chaining.
*/
@NonNull
public GitHubSCMSourceContext withMaxTagCount(int maxTagCount) {
this.maxTagCount = maxTagCount;
return this;
}

/**
* Adds a requirement for origin pull request details to any {@link GitHubSCMSourceRequest} for
* this context.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@
private final boolean fetchBranches;
/** {@code true} if tag details need to be fetched. */
private final boolean fetchTags;
/** {@code true} if tags should be scanned in descending order. */
private final boolean tagDescendingOrder;
/** Maximum number of tags to process (0 = unlimited). */
private final int maxTagCount;
/** {@code true} if origin pull requests need to be fetched. */
private final boolean fetchOriginPRs;
/** {@code true} if fork pull requests need to be fetched. */
Expand Down Expand Up @@ -124,6 +128,8 @@
super(source, context, listener);
fetchBranches = context.wantBranches();
fetchTags = context.wantTags();
tagDescendingOrder = context.isTagDescendingOrder();
maxTagCount = context.getMaxTagCount();
fetchOriginPRs = context.wantOriginPRs();
fetchForkPRs = context.wantForkPRs();
originPRStrategies = fetchOriginPRs && !context.originPRStrategies().isEmpty()
Expand Down Expand Up @@ -177,6 +183,24 @@
return fetchTags;
}

/**
* Returns {@code true} if tags should be scanned in descending order.
*
* @return {@code true} if tags should be scanned in descending order.
*/
public final boolean isTagDescendingOrder() {
return tagDescendingOrder;
}

/**
* Returns the maximum number of tags to process (0 = unlimited).
*
* @return the maximum number of tags to process.
*/
public final int getMaxTagCount() {
return maxTagCount;

Check warning on line 201 in src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubSCMSourceRequest.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 201 is not covered by tests
}

/**
* Returns {@code true} if pull request details need to be fetched.
*
Expand Down
Loading
Loading