From 1c71f4c958c0f52cc906f147c59b986868c7b830 Mon Sep 17 00:00:00 2001 From: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Thu, 9 Jan 2025 16:04:02 +0000 Subject: [PATCH 01/20] Init --- core/src/main/java/hudson/Functions.java | 28 ++++++ core/src/main/java/hudson/model/Run.java | 18 ++++ .../hudson/model/details/DurationDetail.java | 20 ++++ .../hudson/model/details/TimestampDetail.java | 20 ++++ core/src/main/java/jenkins/model/Detail.java | 31 ++++++ .../java/jenkins/model/DetailFactory.java | 52 ++++++++++ core/src/main/java/jenkins/model/Group.java | 26 +++++ .../hudson/model/Run/new-build-page.jelly | 67 +++++++------ .../model/details/DurationDetail/detail.jelly | 46 +++++++++ .../details/DurationDetail/detail.properties | 23 +++++ .../details/TimestampDetail/detail.jelly | 50 ++++++++++ .../details/TimestampDetail/detail.properties | 23 +++++ src/main/js/pages/job/index.js | 19 ++++ src/main/scss/components/_cards.scss | 57 ++++++++++- src/main/scss/pages/_build.scss | 99 +++++++++++++++++++ .../main/resources/images/symbols/timer.svg | 1 + webpack.config.js | 1 + 17 files changed, 552 insertions(+), 29 deletions(-) create mode 100644 core/src/main/java/hudson/model/details/DurationDetail.java create mode 100644 core/src/main/java/hudson/model/details/TimestampDetail.java create mode 100644 core/src/main/java/jenkins/model/Detail.java create mode 100644 core/src/main/java/jenkins/model/DetailFactory.java create mode 100644 core/src/main/java/jenkins/model/Group.java create mode 100644 core/src/main/resources/hudson/model/details/DurationDetail/detail.jelly create mode 100644 core/src/main/resources/hudson/model/details/DurationDetail/detail.properties create mode 100644 core/src/main/resources/hudson/model/details/TimestampDetail/detail.jelly create mode 100644 core/src/main/resources/hudson/model/details/TimestampDetail/detail.properties create mode 100644 src/main/js/pages/job/index.js create mode 100644 war/src/main/resources/images/symbols/timer.svg diff --git a/core/src/main/java/hudson/Functions.java b/core/src/main/java/hudson/Functions.java index ea677293cb60..4f2cdcb281bc 100644 --- a/core/src/main/java/hudson/Functions.java +++ b/core/src/main/java/hudson/Functions.java @@ -161,8 +161,11 @@ import jenkins.console.ConsoleUrlProvider; import jenkins.console.DefaultConsoleUrlProvider; import jenkins.console.WithConsoleUrl; +import jenkins.model.Detail; +import jenkins.model.DetailFactory; import jenkins.model.GlobalConfiguration; import jenkins.model.GlobalConfigurationCategory; +import jenkins.model.Group; import jenkins.model.Jenkins; import jenkins.model.ModelObjectWithChildren; import jenkins.model.ModelObjectWithContextMenu; @@ -2589,6 +2592,31 @@ public static String generateItemId() { return String.valueOf(Math.floor(Math.random() * 3000)); } + /** + * Returns a grouped list of Detail objects for the given run + */ + @Restricted(NoExternalUse.class) + public static Map> getDetailsFor(Run object) { + List details = new ArrayList<>(); + + for (DetailFactory taf : DetailFactory.factoriesFor(Run.class)) { + details.addAll(taf.createFor(object)); + } + + Map> orderedMap = new TreeMap<>(Comparator.comparingInt(Group::getOrder)); + + for (Detail detail : details) { + orderedMap.computeIfAbsent(detail.getGroup(), k -> new ArrayList<>()).add(detail); + } + + for (Map.Entry> entry : orderedMap.entrySet()) { + List detailList = entry.getValue(); + detailList.sort(Comparator.comparingInt(Detail::getOrder)); + } + + return orderedMap; + } + @Restricted(NoExternalUse.class) public static ExtensionList getSearchFactories() { return SearchFactory.all(); diff --git a/core/src/main/java/hudson/model/Run.java b/core/src/main/java/hudson/model/Run.java index ec7eaabf32c6..6d951a1c1ca4 100644 --- a/core/src/main/java/hudson/model/Run.java +++ b/core/src/main/java/hudson/model/Run.java @@ -40,6 +40,7 @@ import hudson.AbortException; import hudson.BulkChange; import hudson.EnvVars; +import hudson.Extension; import hudson.ExtensionList; import hudson.ExtensionPoint; import hudson.FeedAdapter; @@ -53,6 +54,8 @@ import hudson.console.ModelHyperlinkNote; import hudson.console.PlainTextConsoleOutputStream; import hudson.model.Descriptor.FormException; +import hudson.model.details.DurationDetail; +import hudson.model.details.TimestampDetail; import hudson.model.listeners.RunListener; import hudson.model.listeners.SaveableListener; import hudson.model.queue.SubTask; @@ -115,6 +118,8 @@ import jenkins.model.ArtifactManagerConfiguration; import jenkins.model.ArtifactManagerFactory; import jenkins.model.BuildDiscarder; +import jenkins.model.Detail; +import jenkins.model.DetailFactory; import jenkins.model.HistoricalBuild; import jenkins.model.Jenkins; import jenkins.model.JenkinsLocationConfiguration; @@ -2669,4 +2674,17 @@ public void doDynamic(StaplerRequest2 req, StaplerResponse2 rsp) throws IOExcept out.flush(); } } + + @Extension + public static final class BasicRunDetailFactory extends DetailFactory { + + @Override + public Class type() { + return Run.class; + } + + @NonNull @Override public Collection createFor(@NonNull Run target) { + return List.of(new TimestampDetail(), new DurationDetail()); + } + } } diff --git a/core/src/main/java/hudson/model/details/DurationDetail.java b/core/src/main/java/hudson/model/details/DurationDetail.java new file mode 100644 index 000000000000..2edd8b9c98c1 --- /dev/null +++ b/core/src/main/java/hudson/model/details/DurationDetail.java @@ -0,0 +1,20 @@ +package hudson.model.details; + +import jenkins.model.Detail; + +/** + * Displays the duration of the given build, or, if the build has completed, shows the total time it took to execute + * @implNote This will render Jelly, hence the fields return null + */ +public class DurationDetail extends Detail { + + @Override + public String getIconFileName() { + return null; + } + + @Override + public String getDisplayName() { + return null; + } +} diff --git a/core/src/main/java/hudson/model/details/TimestampDetail.java b/core/src/main/java/hudson/model/details/TimestampDetail.java new file mode 100644 index 000000000000..da3aa917e173 --- /dev/null +++ b/core/src/main/java/hudson/model/details/TimestampDetail.java @@ -0,0 +1,20 @@ +package hudson.model.details; + +import jenkins.model.Detail; + +/** + * Displays the start time of the given build + * @implNote This will render Jelly, hence the fields return null + */ +public class TimestampDetail extends Detail { + + @Override + public String getIconFileName() { + return null; + } + + @Override + public String getDisplayName() { + return null; + } +} diff --git a/core/src/main/java/jenkins/model/Detail.java b/core/src/main/java/jenkins/model/Detail.java new file mode 100644 index 000000000000..f4a7d7bb58a7 --- /dev/null +++ b/core/src/main/java/jenkins/model/Detail.java @@ -0,0 +1,31 @@ +package jenkins.model; + +import hudson.model.ModelObject; + +public abstract class Detail implements ModelObject { + + /** + * {@inheritDoc} + */ + public abstract String getIconFileName(); + + /** + * {@inheritDoc} + */ + @Override + public abstract String getDisplayName(); + + /** + * @return the grouping of the detail + */ + public Group getGroup() { + return Group.GENERIC; + } + + /** + * @return order in the group, zero is first, MAX_VALUE is any order + */ + public int getOrder() { + return Integer.MAX_VALUE; + } +} diff --git a/core/src/main/java/jenkins/model/DetailFactory.java b/core/src/main/java/jenkins/model/DetailFactory.java new file mode 100644 index 000000000000..ad99d13e8281 --- /dev/null +++ b/core/src/main/java/jenkins/model/DetailFactory.java @@ -0,0 +1,52 @@ +/* + * The MIT License + * + * Copyright 2024 Jan Faracik + * + * 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.model; + +import edu.umd.cs.findbugs.annotations.NonNull; +import hudson.ExtensionList; +import hudson.ExtensionPoint; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +public abstract class DetailFactory implements ExtensionPoint { + + public abstract Class type(); + + public abstract @NonNull Collection createFor(@NonNull T target); + + @Restricted(NoExternalUse.class) + public static Iterable> factoriesFor(Class type) { + List> result = new ArrayList<>(); + for (DetailFactory wf : ExtensionList.lookup(DetailFactory.class)) { + if (wf.type().isAssignableFrom(type)) { + result.add(wf); + } + } + return result; + } +} diff --git a/core/src/main/java/jenkins/model/Group.java b/core/src/main/java/jenkins/model/Group.java new file mode 100644 index 000000000000..c9f979d6799f --- /dev/null +++ b/core/src/main/java/jenkins/model/Group.java @@ -0,0 +1,26 @@ +package jenkins.model; + +public class Group { + + private final int order; + + private Group(int order) { + if (order < 0) { + throw new RuntimeException("Orders cannot be less than 0"); + } + + this.order = order; + } + + public static Group SCM = of(0); + + public static Group GENERIC = of(Integer.MAX_VALUE); + + public static Group of(int customOrder) { + return new Group(customOrder); + } + + public int getOrder() { + return order; + } +} diff --git a/core/src/main/resources/hudson/model/Run/new-build-page.jelly b/core/src/main/resources/hudson/model/Run/new-build-page.jelly index cf1c270163e5..8a0c06a49e17 100644 --- a/core/src/main/resources/hudson/model/Run/new-build-page.jelly +++ b/core/src/main/resources/hudson/model/Run/new-build-page.jelly @@ -24,12 +24,14 @@ THE SOFTWARE. --> - + +