mirror of
https://github.com/jenkinsci/jenkins.git
synced 2026-11-10 02:26:18 +00:00
[JENKINS-59136] Enable labels/categories with deprecated to show a warning for plugins (#4073)
* Enable labels/categories with `deprecated` to show a warning for plugins * fix doc * Wording Co-Authored-By: Daniel Beck <1831569+daniel-beck@users.noreply.github.com> * Add Restricted no external use * add since todo * spelling Co-Authored-By: Tim Jacomb <t.jacomb@kainos.com> * use stream api to collect deprecated plugins * link to plugin url * add break lines to deprecation message * fix regex * wording and separation of description * Hide deprecated plugins inside the available section on update center * Add a warning inside the installed section on plugin manager * Add support for top-level 'deprecations' element in update site * Add test * Make this a list to allow direct element access * This isn't a critical problem so go with warning style, rephrase label * Add an intermediate JSON object in the UC data for extensibility * Make 'deprecated' a meta label (not shown in the regular label list) * Make parsing this safer * Null safety with labels array * Link to the deprecation URL, not the plugin URL * Add issue reference * Add Javadoc Co-authored-by: Daniel Beck <1831569+daniel-beck@users.noreply.github.com> Co-authored-by: Tim Jacomb <t.jacomb@kainos.com> Co-authored-by: Daniel Beck <daniel-beck@users.noreply.github.com>
This commit is contained in:
12 files changed
+367
-1
No files matched your search
@@ -145,6 +145,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
@@ -2238,6 +2239,32 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AdministrativeMonitor} that checks if there are any plugins that are deprecated.
|
||||
*
|
||||
* @since TODO
|
||||
*/
|
||||
@Restricted(NoExternalUse.class)
|
||||
@Symbol("pluginDeprecation")
|
||||
@Extension
|
||||
public static final class PluginDeprecationMonitor extends AdministrativeMonitor {
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return Messages.PluginManager_PluginDeprecationMonitor_DisplayName();
|
||||
}
|
||||
|
||||
public boolean isActivated() {
|
||||
return !getDeprecatedPlugins().isEmpty();
|
||||
}
|
||||
|
||||
public Map<PluginWrapper, String> getDeprecatedPlugins() {
|
||||
return Jenkins.get().getPluginManager().getPlugins().stream()
|
||||
.filter(PluginWrapper::isDeprecated)
|
||||
.collect(Collectors.toMap(Function.identity(), it -> it.getDeprecations().get(0).url));
|
||||
}
|
||||
}
|
||||
|
||||
@Restricted(DoNotUse.class)
|
||||
public String unscientific(double d) {
|
||||
return String.format(Locale.US, "%15.4f", d);
|
||||
@@ -2254,7 +2281,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
|
||||
|
||||
@Restricted(DoNotUse.class) // Used from table.jelly
|
||||
public boolean isMetaLabel(String label) {
|
||||
return "adopt-this-plugin".equals(label);
|
||||
return "adopt-this-plugin".equals(label) || "deprecated".equals(label);
|
||||
}
|
||||
|
||||
@Restricted(DoNotUse.class) // Used from table.jelly
|
||||
|
||||
@@ -364,6 +364,21 @@ public class PluginWrapper implements Comparable<PluginWrapper>, ModelObject {
|
||||
return dependencies.stream().anyMatch(d -> !d.optional);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this plugin deprecated?
|
||||
*
|
||||
* @return {@code true} if and only if an update site reports deprecations for this plugin.
|
||||
* @since TODO
|
||||
*/
|
||||
@Restricted(NoExternalUse.class)
|
||||
public boolean isDeprecated() {
|
||||
/*
|
||||
While better handled in UpdateSite.Plugin that only exists for plugins actively being published.
|
||||
We have no good model for plugin metadata from update sites for plugins not being published.
|
||||
*/
|
||||
return !getDeprecations().isEmpty();
|
||||
}
|
||||
|
||||
@ExportedBean
|
||||
public static final class Dependency {
|
||||
@Exported
|
||||
@@ -553,8 +568,11 @@ public class PluginWrapper implements Comparable<PluginWrapper>, ModelObject {
|
||||
|
||||
/**
|
||||
* Returns a one-line descriptive name of this plugin.
|
||||
*
|
||||
* @deprecated For most purposes, use {@link #getDisplayName()}.
|
||||
*/
|
||||
@Exported
|
||||
@Deprecated
|
||||
public String getLongName() {
|
||||
String name = manifest.getMainAttributes().getValue("Long-Name");
|
||||
if(name!=null) return name;
|
||||
@@ -1284,6 +1302,20 @@ public class PluginWrapper implements Comparable<PluginWrapper>, ModelObject {
|
||||
return ExtensionList.lookupSingleton(UpdateSiteWarningsMonitor.class).getActivePluginWarningsByPlugin().getOrDefault(this, Collections.emptyList());
|
||||
}
|
||||
|
||||
@Restricted(NoExternalUse.class)
|
||||
public List<UpdateSite.Deprecation> getDeprecations() {
|
||||
/* Would be much nicer to go through getInfoFromAllSites but that only works for currently published plugins */
|
||||
List<UpdateSite.Deprecation> deprecations = new ArrayList<>();
|
||||
for (UpdateSite site : Jenkins.get().getUpdateCenter().getSites()) {
|
||||
for (Map.Entry<String, UpdateSite.Deprecation> entry : site.getData().getDeprecations().entrySet()) {
|
||||
if (entry.getKey().equals(this.shortName)) {
|
||||
deprecations.add(entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
return deprecations;
|
||||
}
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(PluginWrapper.class.getName());
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,13 +45,17 @@ import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.UUID;
|
||||
@@ -555,6 +559,13 @@ public class UpdateSite {
|
||||
*/
|
||||
private final Set<Warning> warnings = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Mapping of plugin IDs to deprecation notices
|
||||
*
|
||||
* @since TODO
|
||||
*/
|
||||
private final Map<String, Deprecation> deprecations = new HashMap<>();
|
||||
|
||||
/**
|
||||
* If this is non-null, Jenkins is going to check the connectivity to this URL to make sure
|
||||
* the network connection is up. Null to skip the check.
|
||||
@@ -581,6 +592,24 @@ public class UpdateSite {
|
||||
}
|
||||
}
|
||||
|
||||
JSONObject deprecations = o.optJSONObject("deprecations");
|
||||
if (deprecations != null) {
|
||||
for (Iterator it = deprecations.keys(); it.hasNext(); ) {
|
||||
try {
|
||||
String pluginId = it.next().toString();
|
||||
JSONObject entry = deprecations.getJSONObject(pluginId); // additional level of indirection to support future extensibility
|
||||
if (entry != null) {
|
||||
String referenceUrl = entry.getString("url");
|
||||
if (referenceUrl != null) {
|
||||
this.deprecations.put(pluginId, new Deprecation(referenceUrl));
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException ex) {
|
||||
LOGGER.log(Level.WARNING, "Failed to parse JSON for deprecation", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(Map.Entry<String,JSONObject> e : (Set<Map.Entry<String,JSONObject>>)o.getJSONObject("plugins").entrySet()) {
|
||||
Plugin p = new Plugin(sourceId, e.getValue());
|
||||
// JENKINS-33308 - include implied dependencies for older plugins that may need them
|
||||
@@ -593,6 +622,16 @@ public class UpdateSite {
|
||||
}
|
||||
}
|
||||
plugins.put(Util.intern(e.getKey()), p);
|
||||
|
||||
// compatibility with update sites that have no separate 'deprecated' top-level entry.
|
||||
// Also do this even if there are deprecations to potentially allow limiting the top-level entry to overridden URLs.
|
||||
if (p.categories != null) {
|
||||
if (Arrays.asList(p.categories).contains("deprecated")) {
|
||||
if (!this.deprecations.containsKey(p.name)) {
|
||||
this.deprecations.put(p.name, new Deprecation(p.wiki));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
connectionCheckUrl = (String)o.get("connectionCheckUrl");
|
||||
@@ -608,6 +647,16 @@ public class UpdateSite {
|
||||
return this.warnings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the deprecations provided by the update site
|
||||
* @return the deprecations provided by the update site
|
||||
* @since TODO
|
||||
*/
|
||||
@Restricted(NoExternalUse.class)
|
||||
public Map<String, Deprecation> getDeprecations() {
|
||||
return this.deprecations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is there a new version of the core?
|
||||
*/
|
||||
@@ -790,6 +839,37 @@ public class UpdateSite {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a deprecation of a certain component. Jenkins project policy determines exactly what it means.
|
||||
*
|
||||
* @since TODO
|
||||
*/
|
||||
@Restricted(NoExternalUse.class)
|
||||
public static final class Deprecation {
|
||||
/**
|
||||
* URL for this deprecation.
|
||||
*
|
||||
* Jenkins will show a link to this URL when displaying the deprecation message.
|
||||
*/
|
||||
public final String url;
|
||||
public Deprecation(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Deprecation that = (Deprecation) o;
|
||||
return Objects.equals(url, that.url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a warning about a certain component, mostly related to known security issues.
|
||||
*
|
||||
@@ -1106,7 +1186,15 @@ public class UpdateSite {
|
||||
|
||||
}
|
||||
|
||||
@Restricted(NoExternalUse.class)
|
||||
public boolean isDeprecated() {
|
||||
return getDeprecation() != null;
|
||||
}
|
||||
|
||||
@Restricted(NoExternalUse.class)
|
||||
public UpdateSite.Deprecation getDeprecation() {
|
||||
return Jenkins.get().getUpdateCenter().getSite(sourceId).getData().getDeprecations().get(this.name);
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
String displayName;
|
||||
|
||||
Reference in new issue
Block a user