Skip to content

Commit d70bc2e

Browse files
alecharpmawinter69timjadaniel-beck
authored
[JENKINS-75350] Displays plugin health score in Plugin Manager (#10351)
Co-authored-by: Markus Winter <m.winter@sap.com> Co-authored-by: Tim Jacomb <21194782+timja@users.noreply.github.com> Co-authored-by: Daniel Beck <1831569+daniel-beck@users.noreply.github.com> Co-authored-by: Daniel Beck <daniel-beck@users.noreply.github.com>
1 parent d1fd537 commit d70bc2e

File tree

18 files changed

+199
-6
lines changed

18 files changed

+199
-6
lines changed

core/src/main/java/hudson/PluginManager.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1557,6 +1557,10 @@ public HttpResponse doPluginsSearch(@QueryParameter String query, @QueryParamete
15571557
releaseTimestamp.put("displayValue", Messages.PluginManager_ago(Functions.getTimeSpanString(plugin.releaseTimestamp)));
15581558
jsonObject.put("releaseTimestamp", releaseTimestamp);
15591559
}
1560+
if (plugin.healthScore != null) {
1561+
jsonObject.put("healthScore", plugin.healthScore);
1562+
jsonObject.put("healthScoreClass", plugin.healthScoreClass);
1563+
}
15601564
return jsonObject;
15611565
})
15621566
.collect(toList());

core/src/main/java/hudson/PluginWrapper.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,8 @@ public boolean hasDerivedDependencyErrors() {
222222
*/
223223
private static Set<String> CORE_ONLY_DEPENDANT = Set.of("jenkins-core");
224224

225+
private Integer healthScore;
226+
225227
/**
226228
* Set the list of components that depend on this plugin.
227229
* @param dependents The list of components that depend on this plugin.
@@ -423,6 +425,32 @@ public void injectJarsToClasspath(File... jars) throws Exception {
423425

424426
}
425427

428+
@Restricted(NoExternalUse.class) // Jelly use only
429+
public Integer getHealthScore() {
430+
if (this.healthScore == null) {
431+
this.healthScore = getInfoFromAllSites().stream()
432+
.filter(Objects::nonNull)
433+
.filter(p -> p.healthScore != null)
434+
.findFirst()
435+
.map(plugin -> plugin.healthScore)
436+
.orElse(null);
437+
}
438+
return this.healthScore;
439+
}
440+
441+
@Restricted(NoExternalUse.class)
442+
public static String getHealthScoreClassForScore(int score) {
443+
if (score > 80) return "top";
444+
if (score > 60) return "middle";
445+
return "bottom";
446+
}
447+
448+
@Restricted(NoExternalUse.class) // Jelly use only
449+
public String getHealthScoreClass() {
450+
if (this.healthScore == null) return null;
451+
return getHealthScoreClassForScore(this.healthScore);
452+
}
453+
426454
@ExportedBean
427455
public static final class Dependency {
428456
@Exported

core/src/main/java/hudson/model/UpdateCenter.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,20 @@ public String getDefaultBaseUrl() {
765765
return result;
766766
}
767767

768+
@Restricted(NoExternalUse.class)
769+
public boolean isHealthScoresAvailable() {
770+
for (UpdateSite site : sites) {
771+
final Data data = site.getData();
772+
if (data == null) {
773+
continue;
774+
}
775+
if (data.healthScoresAvailable) {
776+
return true;
777+
}
778+
}
779+
return false;
780+
}
781+
768782
private boolean checkMinVersion(@CheckForNull Plugin p, @CheckForNull VersionNumber minVersion) {
769783
return p != null
770784
&& (minVersion == null || !minVersion.isNewerThan(new VersionNumber(p.version)));

core/src/main/java/hudson/model/UpdateSite.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,9 @@ public final class Data {
611611
*/
612612
public final String connectionCheckUrl;
613613

614+
@Restricted(NoExternalUse.class)
615+
public final boolean healthScoresAvailable;
616+
614617
Data(JSONObject o) {
615618
this.sourceId = Util.intern((String) o.get("id"));
616619
JSONObject c = o.optJSONObject("core");
@@ -649,6 +652,8 @@ public final class Data {
649652
}
650653
}
651654

655+
boolean healthScoresAvailable = false;
656+
652657
for (Map.Entry<String, JSONObject> e : (Set<Map.Entry<String, JSONObject>>) o.getJSONObject("plugins").entrySet()) {
653658
Plugin p = new Plugin(sourceId, e.getValue());
654659
// JENKINS-33308 - include implied dependencies for older plugins that may need them
@@ -662,6 +667,10 @@ public final class Data {
662667
}
663668
plugins.put(Util.intern(e.getKey()), p);
664669

670+
if (p.healthScore != null) {
671+
healthScoresAvailable = true;
672+
}
673+
665674
// compatibility with update sites that have no separate 'deprecated' top-level entry.
666675
// Also do this even if there are deprecations to potentially allow limiting the top-level entry to overridden URLs.
667676
if (p.hasCategory("deprecated")) {
@@ -671,6 +680,8 @@ public final class Data {
671680
}
672681
}
673682

683+
this.healthScoresAvailable = healthScoresAvailable;
684+
674685
connectionCheckUrl = (String) o.get("connectionCheckUrl");
675686
}
676687

@@ -1256,6 +1267,12 @@ public final class Plugin extends Entry {
12561267
@Restricted(NoExternalUse.class)
12571268
public IssueTracker[] issueTrackers;
12581269

1270+
@Restricted(NoExternalUse.class)
1271+
public final Integer healthScore;
1272+
1273+
@Restricted(NoExternalUse.class)
1274+
public final String healthScoreClass;
1275+
12591276
@DataBoundConstructor
12601277
public Plugin(String sourceId, JSONObject o) {
12611278
super(sourceId, o, UpdateSite.this.url);
@@ -1292,6 +1309,12 @@ public Plugin(String sourceId, JSONObject o) {
12921309
int optionalDepCount = (int) ja.stream().filter(IS_DEP_PREDICATE.and(IS_NOT_OPTIONAL.negate())).count();
12931310
dependencies = getPresizedMutableMap(depCount);
12941311
optionalDependencies = getPresizedMutableMap(optionalDepCount);
1312+
this.healthScore = o.has("health") ? o.getInt("health") : null;
1313+
if (healthScore != null) {
1314+
this.healthScoreClass = PluginWrapper.getHealthScoreClassForScore(healthScore);
1315+
} else {
1316+
this.healthScoreClass = null;
1317+
}
12951318

12961319
for (Object jo : o.getJSONArray("dependencies")) {
12971320
JSONObject depObj = (JSONObject) jo;

core/src/main/resources/hudson/PluginManager/available.jelly

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ THE SOFTWARE.
7272

7373
<form id="form" method="post" action="install">
7474
<table id="plugins" class="jenkins-table sortable"
75-
data-hasAdmin="${h.hasPermission(app.ADMINISTER)}">
75+
data-hasAdmin="${h.hasPermission(app.ADMINISTER)}"
76+
data-health="${app.updateCenter.healthScoresAvailable}">
7677
<thead>
7778
<tr>
7879
<l:isAdmin>
@@ -82,6 +83,14 @@ THE SOFTWARE.
8283
</l:isAdmin>
8384
<th initialSortDir="down">${%Name}</th>
8485
<th>${%Released}</th>
86+
<j:if test="${app.updateCenter.healthScoresAvailable}">
87+
<th>
88+
<j:set var="healthTooltip">${%healthTooltip}</j:set>
89+
<span data-html-tooltip="${healthTooltip}" data-tooltip-interactive="true">
90+
${%Health}
91+
</span>
92+
</th>
93+
</j:if>
8594
</tr>
8695
</thead>
8796
<tbody/>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
healthTooltip=\
2+
A score evaluating the health of a plugin, using metrics such as actively maintained, recommended repository configuration applied and documentation provided. \
3+
<a href="https://jenkins.io/redirect/plugin-health-score" target="_blank">Learn more</a>

core/src/main/resources/hudson/PluginManager/available_fr.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@ Install\ after\ restart=Installer après le redémarrage
2626
Install=Installer
2727
Name=Nom
2828
Released=Publié
29+
Health=Santé

core/src/main/resources/hudson/PluginManager/installed.jelly

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,14 @@ THE SOFTWARE.
7878
<thead>
7979
<tr>
8080
<th initialSortDir="down">${%Name}</th>
81+
<j:if test="${app.updateCenter.healthScoresAvailable}">
82+
<th>
83+
<j:set var="healthTooltip">${%healthTooltip}</j:set>
84+
<span data-html-tooltip="${healthTooltip}" data-tooltip-interactive="true">
85+
${%Health}
86+
</span>
87+
</th>
88+
</j:if>
8189
<th class="jenkins-table__cell--tight">${%Enabled}</th>
8290
<l:isAdmin>
8391
<th data-sort-disable="true"/>
@@ -161,6 +169,22 @@ THE SOFTWARE.
161169
</div>
162170
</j:if>
163171
</td>
172+
<j:if test="${app.updateCenter.healthScoresAvailable}">
173+
<td class="jenkins-table__cell">
174+
<span>
175+
<j:choose>
176+
<j:when test="${p.healthScore != null}">
177+
<a href="${p.url}/healthScore" class="jenkins-healthScore--badge jenkins-healthScore--${p.healthScoreClass}" target="_blank" rel="noopener noreferrer">
178+
<st:out value="${p.healthScore}"/>
179+
</a>
180+
</j:when>
181+
<j:otherwise>
182+
<l:icon src="symbol-status-aborted" class="icon-lg" tooltip="${%No health score available}"/>
183+
</j:otherwise>
184+
</j:choose>
185+
</span>
186+
</td>
187+
</j:if>
164188
<j:set var="state" value="${p.enabled?'true':null}"/>
165189
<td class="jenkins-table__cell--tight enable" data="${state}">
166190
<div class="jenkins-table__cell__button-wrapper">

core/src/main/resources/hudson/PluginManager/installed.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,6 @@ adoptThisPlugin=\
3434
reportIssue=Report an issue with this plugin
3535
uninstall-title=Are you sure you want to uninstall {0}?
3636
uninstall-description=This will remove the plugin binary from your $JENKINS_HOME, but it will leave the configuration files of the plugin untouched
37+
healthTooltip=\
38+
A score evaluating the health of a plugin, using metrics such as actively maintained, recommended repository configuration applied and documentation provided. \
39+
<a href="https://jenkins.io/redirect/plugin-health-score" target="_blank">Learn more</a>

core/src/main/resources/hudson/PluginManager/installed_fr.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@ Uninstall=Désinstaller
3232
Version=Version
3333
Previously\ installed\ version=Version précédente
3434
downgradeTo=Rétrograder à {0}
35+
Health=Santé

0 commit comments

Comments
 (0)