diff --git a/.prettierignore b/.prettierignore index 72610f04b419..7bdc346011d5 100644 --- a/.prettierignore +++ b/.prettierignore @@ -9,7 +9,6 @@ node/ # libraries / external deps / generated files src/main/js/plugin-setup-wizard/bootstrap-detached.js -war/src/main/webapp/scripts/yui war/src/main/webapp/jsbundles/ src/main/scss/_bootstrap.scss diff --git a/bom/pom.xml b/bom/pom.xml index ecd962f2bdb6..d2912addc6a4 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -41,7 +41,7 @@ THE SOFTWARE. 2.0.0-M2 2.4.21 1.1-jenkins-20250108 - 1940.v41211a_a_b_b_d8b_ + 1942.v708e07325402 @@ -132,11 +132,6 @@ THE SOFTWARE. commons-io 2.18.0 - - commons-jelly - commons-jelly-tags-xml - 1.1 - commons-lang commons-lang @@ -243,11 +238,21 @@ THE SOFTWARE. annotation-indexer 1.18 + + org.jenkins-ci + commons-jelly + ${jelly.version} + org.jenkins-ci commons-jelly-tags-fmt ${jelly.version} + + org.jenkins-ci + commons-jelly-tags-xml + ${jelly.version} + org.jenkins-ci commons-jexl diff --git a/core/pom.xml b/core/pom.xml index 6821b4c99bf7..2f5745023595 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -166,40 +166,6 @@ THE SOFTWARE. commons-io commons-io - - commons-jelly - commons-jelly-tags-xml - - - commons-jelly - commons-jelly - - - commons-jelly - commons-jelly-tags-junit - - - commons-jexl - commons-jexl - - - dom4j - dom4j - - - xalan - xalan - - - xerces - xercesImpl - - - xml-apis - xml-apis - - - commons-lang @@ -310,6 +276,20 @@ THE SOFTWARE. org.jenkins-ci commons-jelly-tags-fmt + + org.jenkins-ci + commons-jelly-tags-xml + + + jaxen + jaxen + + + org.dom4j + dom4j + + + org.jenkins-ci commons-jexl diff --git a/core/src/main/java/hudson/Functions.java b/core/src/main/java/hudson/Functions.java index ea677293cb60..3e509ecb6313 100644 --- a/core/src/main/java/hudson/Functions.java +++ b/core/src/main/java/hudson/Functions.java @@ -36,6 +36,7 @@ import hudson.init.InitMilestone; import hudson.model.AbstractProject; import hudson.model.Action; +import hudson.model.Actionable; import hudson.model.Computer; import hudson.model.Describable; import hudson.model.Descriptor; @@ -161,6 +162,9 @@ import jenkins.console.ConsoleUrlProvider; import jenkins.console.DefaultConsoleUrlProvider; import jenkins.console.WithConsoleUrl; +import jenkins.model.Detail; +import jenkins.model.DetailFactory; +import jenkins.model.DetailGroup; import jenkins.model.GlobalConfiguration; import jenkins.model.GlobalConfigurationCategory; import jenkins.model.Jenkins; @@ -664,17 +668,14 @@ public static String validateIconSize(String iconSize) throws SecurityException } /** - * Gets the suffix to use for YUI JavaScript. - */ - public static String getYuiSuffix() { - return DEBUG_YUI ? "debug" : "min"; - } - - /** - * Set to true if you need to use the debug version of YUI. + * No longer used, to be removed after enough plugins have adopted a version of the test harness with + * jenkins-test-harness/pull/874 in it. + * + * @deprecated removed without replacement */ @SuppressFBWarnings(value = "MS_SHOULD_BE_FINAL", justification = "for script console") - public static boolean DEBUG_YUI = SystemProperties.getBoolean("debug.YUI"); + @Deprecated(forRemoval = true, since = "TODO") + public static boolean DEBUG_YUI; /** * Creates a sub map by using the given range (both ends inclusive). @@ -2589,6 +2590,33 @@ public static String generateItemId() { return String.valueOf(Math.floor(Math.random() * 3000)); } + /** + * Returns a grouped list of Detail objects for the given Actionable object + */ + @Restricted(NoExternalUse.class) + public static Map> getDetailsFor(Actionable object) { + List details = new ArrayList<>(); + + for (DetailFactory taf : DetailFactory.factoriesFor(object.getClass())) { + details.addAll(taf.createFor(object)); + } + + Map> orderedMap = new TreeMap<>(Comparator.comparingInt(DetailGroup::getOrder)); + + for (Detail detail : details) { + if (detail.isApplicable()) { + 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/cli/CLICommand.java b/core/src/main/java/hudson/cli/CLICommand.java index ba7fd4250fdd..eefaf90c9ecc 100644 --- a/core/src/main/java/hudson/cli/CLICommand.java +++ b/core/src/main/java/hudson/cli/CLICommand.java @@ -53,11 +53,6 @@ import java.util.logging.Logger; import jenkins.model.Jenkins; import jenkins.util.SystemProperties; -import org.apache.commons.discovery.ResourceClassIterator; -import org.apache.commons.discovery.ResourceNameIterator; -import org.apache.commons.discovery.resource.ClassLoaders; -import org.apache.commons.discovery.resource.classes.DiscoverClasses; -import org.apache.commons.discovery.resource.names.DiscoverServiceNames; import org.jvnet.hudson.annotation_indexer.Index; import org.jvnet.tiger_types.Types; import org.kohsuke.accmod.Restricted; @@ -566,27 +561,13 @@ public static CLICommand getCurrent() { if (j != null) { // only when running on the controller // Register OptionHandlers through META-INF/services/annotations and Annotation Indexer try { - for (Class c : Index.list(OptionHandlerExtension.class, Jenkins.get().pluginManager.uberClassLoader, Class.class)) { + for (Class c : Index.list(OptionHandlerExtension.class, j.getPluginManager().uberClassLoader, Class.class)) { Type t = Types.getBaseClass(c, OptionHandler.class); CmdLineParser.registerHandler(Types.erasure(Types.getTypeArgument(t, 0)), c); } } catch (IOException e) { throw new UncheckedIOException(e); } - - // Register OptionHandlers through META-INF/services and Commons Discovery - ClassLoaders cls = new ClassLoaders(); - cls.put(j.getPluginManager().uberClassLoader); - ResourceNameIterator servicesIter = - new DiscoverServiceNames(cls).findResourceNames(OptionHandler.class.getName()); - final ResourceClassIterator itr = - new DiscoverClasses(cls).findResourceClasses(servicesIter); - - while (itr.hasNext()) { - Class h = itr.nextResourceClass().loadClass(); - Class c = Types.erasure(Types.getTypeArgument(Types.getBaseClass(h, OptionHandler.class), 0)); - CmdLineParser.registerHandler(c, h); - } } } diff --git a/core/src/main/java/hudson/cli/handlers/GenericItemOptionHandler.java b/core/src/main/java/hudson/cli/handlers/GenericItemOptionHandler.java index 99bae388ca68..6941334f5be2 100644 --- a/core/src/main/java/hudson/cli/handlers/GenericItemOptionHandler.java +++ b/core/src/main/java/hudson/cli/handlers/GenericItemOptionHandler.java @@ -42,7 +42,7 @@ /** * Refers to an {@link Item} by its name. * May be subclassed to handle specific kinds of items. - * (Use {@code @MetaInfServices(OptionHandler.class)} to register the subclass.) + * (Use {@code @OptionHandlerExtension} to register the subclass.) * @param the kind of item being handled * @since 1.538 */ diff --git a/core/src/main/java/hudson/model/Run.java b/core/src/main/java/hudson/model/Run.java index ec7eaabf32c6..623d4c70aac8 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(target), new DurationDetail(target)); + } + } } 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..01549f79275a --- /dev/null +++ b/core/src/main/java/hudson/model/details/DurationDetail.java @@ -0,0 +1,14 @@ +package hudson.model.details; + +import hudson.model.Run; +import jenkins.model.Detail; + +/** + * Displays the duration of the given run, or, if the run has completed, shows the total time it took to execute + */ +public class DurationDetail extends Detail { + + public DurationDetail(Run run) { + super(run); + } +} 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..ecc503e5288b --- /dev/null +++ b/core/src/main/java/hudson/model/details/TimestampDetail.java @@ -0,0 +1,14 @@ +package hudson.model.details; + +import hudson.model.Run; +import jenkins.model.Detail; + +/** + * Displays the start time of the given run + */ +public class TimestampDetail extends Detail { + + public TimestampDetail(Run run) { + super(run); + } +} diff --git a/core/src/main/java/hudson/security/AbstractPasswordBasedSecurityRealm.java b/core/src/main/java/hudson/security/AbstractPasswordBasedSecurityRealm.java index b008697bbb88..5b7c362589cb 100644 --- a/core/src/main/java/hudson/security/AbstractPasswordBasedSecurityRealm.java +++ b/core/src/main/java/hudson/security/AbstractPasswordBasedSecurityRealm.java @@ -186,7 +186,10 @@ public GroupDetails loadGroupByGroupname(String groupname) throws org.acegisecur class Authenticator extends AbstractUserDetailsAuthenticationProvider { @Override protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { - // authentication is assumed to be done already in the retrieveUser method + // Authentication is done in the retrieveUser method. Note that this method being a no-op is only safe + // because we use Spring Security's default NullUserCache. If caching was enabled, it would be possible to + // log in as any cached user with any password unless we updated this method to check the provided + // authentication as recommended in the superclass method's documentation, so be careful reusing this code. } @Override 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..7a50972a6c3c --- /dev/null +++ b/core/src/main/java/jenkins/model/Detail.java @@ -0,0 +1,74 @@ +package jenkins.model; + +import edu.umd.cs.findbugs.annotations.Nullable; +import hudson.model.Actionable; +import hudson.model.ModelObject; +import hudson.model.Run; +import org.jenkins.ui.icon.IconSpec; + +/** + * {@link Detail} represents a piece of information about a {@link Run}. + * Such information could include: + *
    + *
  • the date and time the run started
  • + *
  • the amount of time the run took to complete
  • + *
  • SCM information for the build
  • + *
  • who kicked the build off
  • + *
+ * @since TODO + */ +public abstract class Detail implements ModelObject, IconSpec { + + private final Actionable object; + + public Detail(Actionable object) { + this.object = object; + } + + public Actionable getObject() { + return object; + } + + /** + * {@inheritDoc} + */ + public @Nullable String getIconClassName() { + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public @Nullable String getDisplayName() { + return null; + } + + /** + * Optional URL for the {@link Detail} + */ + public @Nullable String getUrl() { + return null; + } + + /** + * Returns true if this detail is applicable to the given Actionable object + */ + public boolean isApplicable() { + return true; + } + + /** + * @return the grouping of the detail + */ + public DetailGroup getGroup() { + return DetailGroup.GENERAL; + } + + /** + * @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/experimentalflags/RemoveYuiUserExperimentalFlag.java b/core/src/main/java/jenkins/model/DetailFactory.java similarity index 55% rename from core/src/main/java/jenkins/model/experimentalflags/RemoveYuiUserExperimentalFlag.java rename to core/src/main/java/jenkins/model/DetailFactory.java index 418f9c4b5406..e6857c60d56e 100644 --- a/core/src/main/java/jenkins/model/experimentalflags/RemoveYuiUserExperimentalFlag.java +++ b/core/src/main/java/jenkins/model/DetailFactory.java @@ -1,7 +1,7 @@ /* * The MIT License * - * Copyright (c) 2024, Markus Winter + * Copyright 2025 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 @@ -22,36 +22,37 @@ * THE SOFTWARE. */ -package jenkins.model.experimentalflags; +package jenkins.model; import edu.umd.cs.findbugs.annotations.NonNull; -import edu.umd.cs.findbugs.annotations.Nullable; -import hudson.Extension; -import jenkins.util.SystemProperties; +import hudson.ExtensionList; +import hudson.ExtensionPoint; +import hudson.model.Actionable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; -@Extension -@Restricted(NoExternalUse.class) -public class RemoveYuiUserExperimentalFlag extends BooleanUserExperimentalFlag { - public RemoveYuiUserExperimentalFlag() { - super("remove-yui.flag"); - } +/** + * Allows you to add multiple details to an Actionable object at once. + * @param the type of object to add to; typically an {@link Actionable} subtype + * @since TODO + */ +public abstract class DetailFactory implements ExtensionPoint { - @Override - public String getDisplayName() { - return "Remove YUI"; - } + public abstract Class type(); - @Nullable - @Override - public String getShortDescription() { - return "Remove YUI from all Jenkins UI pages. This will break anything that depends on YUI"; - } + public abstract @NonNull Collection createFor(@NonNull T target); - @NonNull - @Override - public Boolean getDefaultValue() { - return SystemProperties.getBoolean(RemoveYuiUserExperimentalFlag.class.getName() + ".defaultValue", true); + @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/DetailGroup.java b/core/src/main/java/jenkins/model/DetailGroup.java new file mode 100644 index 000000000000..64067a188222 --- /dev/null +++ b/core/src/main/java/jenkins/model/DetailGroup.java @@ -0,0 +1,25 @@ +package jenkins.model; + +/** + * Represents a group for categorizing {@link Detail}, each with an associated order. + */ +public class DetailGroup { + + private final int order; + + private DetailGroup(int order) { + if (order < 0) { + throw new RuntimeException("Orders cannot be less than 0"); + } + + this.order = order; + } + + public static DetailGroup SCM = new DetailGroup(0); + + public static DetailGroup GENERAL = new DetailGroup(Integer.MAX_VALUE); + + public int getOrder() { + return order; + } +} diff --git a/core/src/main/java/jenkins/model/GlobalBuildDiscarderConfiguration.java b/core/src/main/java/jenkins/model/GlobalBuildDiscarderConfiguration.java index 869ec901757b..1f8231b0289f 100644 --- a/core/src/main/java/jenkins/model/GlobalBuildDiscarderConfiguration.java +++ b/core/src/main/java/jenkins/model/GlobalBuildDiscarderConfiguration.java @@ -26,6 +26,7 @@ import hudson.Extension; import hudson.ExtensionList; +import hudson.security.Permission; import hudson.util.DescribableList; import java.io.IOException; import java.util.List; @@ -64,6 +65,11 @@ public DescribableList - + +