Skip to content

Commit

Permalink
Introduce HistoricalBuild interface (jenkinsci#9674)
Browse files Browse the repository at this point in the history
* Introduce HistoricalBuild interface

This allows to display Run-like objects in build history without having concrete Run present in the Jenkins instance.

* Remove BuildHistoryWidget#entries.jelly

* JDK17 level

Co-authored-by: Jesse Glick <[email protected]>

---------

Co-authored-by: Jesse Glick <[email protected]>
  • Loading branch information
Vlatombe and jglick authored Sep 11, 2024
1 parent 78e132d commit 35d7677
Show file tree
Hide file tree
Showing 10 changed files with 285 additions and 190 deletions.
9 changes: 5 additions & 4 deletions core/src/main/java/hudson/model/Job.java
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
import jenkins.model.BuildDiscarder;
import jenkins.model.BuildDiscarderProperty;
import jenkins.model.DirectlyModifiableTopLevelItemGroup;
import jenkins.model.HistoricalBuild;
import jenkins.model.Jenkins;
import jenkins.model.JenkinsLocationConfiguration;
import jenkins.model.ModelObjectWithChildren;
Expand Down Expand Up @@ -636,9 +637,9 @@ protected HistoryWidget createHistoryWidget() {
throw new IllegalStateException("HistoryWidget is now created via WidgetFactory implementation");
}

public static final HistoryWidget.Adapter<Run> HISTORY_ADAPTER = new Adapter<>() {
public static final HistoryWidget.Adapter<HistoricalBuild> HISTORY_ADAPTER = new Adapter<>() {
@Override
public int compare(Run record, String key) {
public int compare(HistoricalBuild record, String key) {
try {
int k = Integer.parseInt(key);
return record.getNumber() - k;
Expand All @@ -648,12 +649,12 @@ public int compare(Run record, String key) {
}

@Override
public String getKey(Run record) {
public String getKey(HistoricalBuild record) {
return String.valueOf(record.getNumber());
}

@Override
public boolean isBuilding(Run record) {
public boolean isBuilding(HistoricalBuild record) {
return record.isBuilding();
}

Expand Down
134 changes: 23 additions & 111 deletions core/src/main/java/hudson/model/Run.java
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@
import jenkins.model.ArtifactManagerConfiguration;
import jenkins.model.ArtifactManagerFactory;
import jenkins.model.BuildDiscarder;
import jenkins.model.HistoricalBuild;
import jenkins.model.Jenkins;
import jenkins.model.JenkinsLocationConfiguration;
import jenkins.model.RunAction2;
Expand Down Expand Up @@ -159,23 +160,14 @@
*/
@ExportedBean
public abstract class Run<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, RunT>>
extends Actionable implements ExtensionPoint, Comparable<RunT>, AccessControlled, PersistenceRoot, DescriptorByNameOwner, OnMaster, StaplerProxy, WithConsoleUrl {
extends Actionable implements ExtensionPoint, Comparable<RunT>, AccessControlled, PersistenceRoot, DescriptorByNameOwner, OnMaster, StaplerProxy, HistoricalBuild, WithConsoleUrl {

/**
* The original {@link Queue.Item#getId()} has not yet been mapped onto the {@link Run} instance.
* @since 1.601
*/
public static final long QUEUE_ID_UNKNOWN = -1;

/**
* Target size limit for truncated {@link #description}s in the Build History Widget.
* This is applied to the raw, unformatted description. Especially complex formatting
* like hyperlinks can result in much less text being shown than this might imply.
* Negative values will disable truncation, {@code 0} will enforce empty strings.
* @since 2.223
*/
private static /* non-final for Groovy */ int TRUNCATED_DESCRIPTION_LIMIT = SystemProperties.getInteger("historyWidget.descriptionLimit", 100);

protected final transient @NonNull JobT project;

/**
Expand Down Expand Up @@ -463,12 +455,12 @@ public int compareTo(@NonNull RunT that) {
}

/**
* Get the {@link Queue.Item#getId()} of the original queue item from where this Run instance
* originated.
* @return The queue item ID.
* {@inheritDoc}
*
* @since 1.601
*/
@Exported
@Override
public long getQueueId() {
return queueId;
}
Expand All @@ -484,15 +476,8 @@ public void setQueueId(long queueId) {
this.queueId = queueId;
}

/**
* Returns the build result.
*
* <p>
* When a build is {@link #isBuilding() in progress}, this method
* returns an intermediate result.
* @return The status of the build, if it has completed or some build step has set a status; may be null if the build is ongoing.
*/
@Exported
@Override
public @CheckForNull Result getResult() {
return result;
}
Expand Down Expand Up @@ -520,6 +505,7 @@ public void setResult(@NonNull Result r) {
/**
* Gets the subset of {@link #getActions()} that consists of {@link BuildBadgeAction}s.
*/
@Override
public @NonNull List<BuildBadgeAction> getBadgeActions() {
List<BuildBadgeAction> r = getActions(BuildBadgeAction.class);
if (isKeepLog()) {
Expand All @@ -529,11 +515,8 @@ public void setResult(@NonNull Result r) {
return r;
}

/**
* Returns true if the build is not completed yet.
* This includes "not started yet" state.
*/
@Exported
@Override
public boolean isBuilding() {
return state.compareTo(State.POST_PRODUCTION) < 0;
}
Expand Down Expand Up @@ -652,11 +635,11 @@ public final boolean isKeepLog() {
}

/**
* When the build is scheduled.
*
* {@inheritDoc}
* @see #getStartTimeInMillis()
*/
@Exported
@Override
public @NonNull Calendar getTimestamp() {
GregorianCalendar c = new GregorianCalendar();
c.setTimeInMillis(timestamp);
Expand Down Expand Up @@ -691,73 +674,12 @@ public final long getStartTimeInMillis() {
}

@Exported
@Override
@CheckForNull
public String getDescription() {
return description;
}


/**
* Returns the length-limited description.
* The method tries to take HTML tags within the description into account, but it is a best-effort attempt.
* Also, the method will likely not work properly if a non-HTML {@link hudson.markup.MarkupFormatter} is used.
* @return The length-limited description.
* @deprecated truncated description based on the {@link #TRUNCATED_DESCRIPTION_LIMIT} setting.
*/
@Deprecated
public @CheckForNull String getTruncatedDescription() {
if (TRUNCATED_DESCRIPTION_LIMIT < 0) { // disabled
return description;
}
if (TRUNCATED_DESCRIPTION_LIMIT == 0) { // Someone wants to suppress descriptions, why not?
return "";
}

final int maxDescrLength = TRUNCATED_DESCRIPTION_LIMIT;
final String localDescription = description;
if (localDescription == null || localDescription.length() < maxDescrLength) {
return localDescription;
}

final String ending = "...";
final int sz = localDescription.length(), maxTruncLength = maxDescrLength - ending.length();

boolean inTag = false;
int displayChars = 0;
int lastTruncatablePoint = -1;

for (int i = 0; i < sz; i++) {
char ch = localDescription.charAt(i);
if (ch == '<') {
inTag = true;
} else if (ch == '>') {
inTag = false;
if (displayChars <= maxTruncLength) {
lastTruncatablePoint = i + 1;
}
}
if (!inTag) {
displayChars++;
if (displayChars <= maxTruncLength && ch == ' ') {
lastTruncatablePoint = i;
}
}
}

String truncDesc = localDescription;

// Could not find a preferred truncatable index, force a trunc at maxTruncLength
if (lastTruncatablePoint == -1)
lastTruncatablePoint = maxTruncLength;

if (displayChars >= maxDescrLength) {
truncDesc = truncDesc.substring(0, lastTruncatablePoint) + ending;
}

return truncDesc;

}

/**
* Gets the string that says how long since this build has started.
*
Expand All @@ -776,9 +698,7 @@ public String getDescription() {
return Util.XS_DATETIME_FORMATTER2.format(Instant.ofEpochMilli(timestamp));
}

/**
* Gets the string that says how long the build took to run.
*/
@Override
public @NonNull String getDurationString() {
if (hasntStartedYet()) {
return Messages.Run_NotStartedYet();
Expand All @@ -797,9 +717,7 @@ public long getDuration() {
return duration;
}

/**
* Gets the icon color for display.
*/
@Override
public @NonNull BallColor getIconColor() {
if (!isBuilding()) {
// already built
Expand Down Expand Up @@ -834,6 +752,7 @@ public String toString() {
}

@Exported
@Override
public String getFullDisplayName() {
return project.getFullDisplayName() + ' ' + getDisplayName();
}
Expand All @@ -859,6 +778,7 @@ public void setDisplayName(String value) throws IOException {
}

@Exported(visibility = 2)
@Override
public int getNumber() {
return number;
}
Expand Down Expand Up @@ -1036,14 +956,8 @@ protected void dropLinks() {
return nextBuild;
}

/**
* Returns the URL of this {@link Run}, relative to the context root of Hudson.
*
* @return
* String like "job/foo/32/" with trailing slash but no leading slash.
*/
// I really messed this up. I'm hoping to fix this some time
// it shouldn't have trailing '/', and instead it should have leading '/'

@Override
public @NonNull String getUrl() {

// RUN may be accessed using permalinks, as "/lastSuccessful" or other, so try to retrieve this base URL
Expand Down Expand Up @@ -1107,6 +1021,12 @@ public String getConsoleUrl() {
return new File(project.getBuildDir(), Integer.toString(number));
}

@Override
public List<ParameterValue> getParameterValues() {
ParametersAction a = getAction(ParametersAction.class);
return a != null ? a.getParameters() : List.of();
}

/**
* Gets an object responsible for storing and retrieving build artifacts.
* If {@link #pickArtifactManager} has previously been called on this build,
Expand Down Expand Up @@ -2175,14 +2095,6 @@ public void doBuildStatus(StaplerRequest2 req, StaplerResponse2 rsp) throws IOEx
rsp.sendRedirect2(req.getContextPath() + "/images/48x48/" + getBuildStatusUrl());
}

public @NonNull String getBuildStatusUrl() {
return getIconColor().getImage();
}

public String getBuildStatusIconClassName() {
return getIconColor().getIconClassName();
}

public static class Summary {
/**
* Is this build worse or better, compared to the previous build?
Expand Down
4 changes: 2 additions & 2 deletions core/src/main/java/hudson/widgets/BuildHistoryWidget.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import jenkins.model.HistoricalBuild;
import jenkins.model.Jenkins;
import jenkins.model.queue.QueueItem;
import jenkins.widgets.HistoryPageFilter;
Expand All @@ -47,7 +48,7 @@
* <p>
* This widget enhances {@link HistoryWidget} by groking the notion
* that {@link #owner} can be in the queue toward the next build.
*
* @param <T> typically {@link HistoricalBuild}
* @author Kohsuke Kawaguchi
*/
public class BuildHistoryWidget<T> extends HistoryWidget<Task, T> {
Expand Down Expand Up @@ -84,7 +85,6 @@ public HistoryPageFilter getHistoryPageFilter() {
final HistoryPageFilter<T> historyPageFilter = newPageFilter();

historyPageFilter.add(baseList, getQueuedItems());
historyPageFilter.widget = this;

return updateFirstTransientBuildKey(historyPageFilter);
}
Expand Down
13 changes: 7 additions & 6 deletions core/src/main/java/hudson/widgets/HistoryWidget.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import hudson.model.Job;
import hudson.model.ModelObject;
import hudson.model.Queue;
import hudson.model.Run;
import hudson.util.AlternativeUiTextProvider;
import jakarta.servlet.ServletException;
import java.io.IOException;
Expand All @@ -40,6 +39,7 @@
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import jenkins.model.HistoricalBuild;
import jenkins.util.SystemProperties;
import jenkins.widgets.HistoryPageEntry;
import jenkins.widgets.HistoryPageFilter;
Expand All @@ -53,12 +53,12 @@
import org.kohsuke.stapler.StaplerResponse2;

/**
* Displays the history of records (normally {@link Run}s) on the side panel.
* Displays the history of records on the side panel.
*
* @param <O>
* Owner of the widget.
* Owner of the widget, typically {@link Job}
* @param <T>
* Type individual record.
* Type individual record, typically {@link HistoricalBuild}
* @author Kohsuke Kawaguchi
*/
public class HistoryWidget<O extends ModelObject, T> extends Widget {
Expand Down Expand Up @@ -142,7 +142,7 @@ public String getFirstTransientBuildKey() {
* @return
* The history page filter that was passed in.
*/
@SuppressWarnings("unchecked")
@SuppressWarnings("unchecked") // TODO actually not type-safe
protected HistoryPageFilter updateFirstTransientBuildKey(HistoryPageFilter historyPageFilter) {
updateFirstTransientBuildKey(historyPageFilter.runs);
return historyPageFilter;
Expand Down Expand Up @@ -195,7 +195,7 @@ private List<HistoryPageEntry<T>> toPageEntries(Iterable<T> historyItemList) {
/**
* Get a {@link jenkins.widgets.HistoryPageFilter} for rendering a page of queue items.
*/
public HistoryPageFilter getHistoryPageFilter() {
public HistoryPageFilter<T> getHistoryPageFilter() {
HistoryPageFilter<T> historyPageFilter = newPageFilter();

historyPageFilter.add(baseList);
Expand All @@ -205,6 +205,7 @@ public HistoryPageFilter getHistoryPageFilter() {

protected HistoryPageFilter<T> newPageFilter() {
HistoryPageFilter<T> historyPageFilter = new HistoryPageFilter<>(THRESHOLD);
historyPageFilter.widget = this;

if (newerThan != null) {
historyPageFilter.setNewerThan(newerThan);
Expand Down
Loading

0 comments on commit 35d7677

Please sign in to comment.