mirror of https://github.com/jenkinsci/jenkins.git
Add experimental Parameters details for builds
This commit is contained in:
parent
7aa344749f
commit
78c8f15755
|
@ -48,6 +48,7 @@ import java.util.TreeSet;
|
|||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import jenkins.model.RunAction2;
|
||||
import jenkins.model.experimentalflags.NewBuildPageUserExperimentalFlag;
|
||||
import jenkins.util.SystemProperties;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
import org.kohsuke.accmod.restrictions.NoExternalUse;
|
||||
|
@ -208,6 +209,12 @@ public class ParametersAction implements RunAction2, Iterable<ParameterValue>, Q
|
|||
|
||||
@Override
|
||||
public String getIconFileName() {
|
||||
Boolean newBuildPageEnabled = new NewBuildPageUserExperimentalFlag().getFlagValue();
|
||||
|
||||
if (newBuildPageEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return "symbol-parameters";
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,8 @@ import static jakarta.servlet.http.HttpServletResponse.SC_CONFLICT;
|
|||
import static jakarta.servlet.http.HttpServletResponse.SC_CREATED;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.CheckForNull;
|
||||
import edu.umd.cs.findbugs.annotations.NonNull;
|
||||
import hudson.Extension;
|
||||
import hudson.Util;
|
||||
import hudson.cli.declarative.CLIMethod;
|
||||
import hudson.cli.declarative.CLIResolver;
|
||||
|
@ -60,6 +62,9 @@ import java.util.Arrays;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import jenkins.model.details.Detail;
|
||||
import jenkins.model.details.DetailFactory;
|
||||
import jenkins.model.details.ParameterizedDetail;
|
||||
import jenkins.model.lazy.LazyBuildMixIn;
|
||||
import jenkins.security.stapler.StaplerNotDispatchable;
|
||||
import jenkins.triggers.SCMTriggerItem;
|
||||
|
@ -561,6 +566,25 @@ public abstract class ParameterizedJobMixIn<JobT extends Job<JobT, RunT> & Param
|
|||
return !isDisabled() && !((Job) this).isHoldOffBuildUntilSave();
|
||||
}
|
||||
|
||||
@Extension
|
||||
final class ParameterizedDetailFactory extends DetailFactory<Run> {
|
||||
|
||||
@Override
|
||||
public Class<Run> type() {
|
||||
return Run.class;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override public List<? extends Detail> createFor(@NonNull Run target) {
|
||||
var action = target.getAction(ParametersAction.class);
|
||||
|
||||
if (action == null || action.getParameters().isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
return List.of(new ParameterizedDetail(target));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
package jenkins.model.details;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.Nullable;
|
||||
import hudson.model.ParametersAction;
|
||||
import hudson.model.Run;
|
||||
|
||||
/**
|
||||
* Displays if a run has parameters
|
||||
*/
|
||||
public class ParameterizedDetail extends Detail {
|
||||
|
||||
public final ParametersAction action;
|
||||
|
||||
public ParameterizedDetail(Run<?, ?> run) {
|
||||
super(run);
|
||||
this.action = getObject().getAction(ParametersAction.class);
|
||||
}
|
||||
|
||||
public @Nullable String getIconClassName() {
|
||||
return "symbol-parameters";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getDisplayName() {
|
||||
return action.getDisplayName();
|
||||
}
|
||||
}
|
|
@ -27,8 +27,8 @@ THE SOFTWARE.
|
|||
xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form"
|
||||
xmlns:i="jelly:fmt" xmlns:p="/lib/hudson/project">
|
||||
<j:set var="escapeEntryTitleAndDescription" value="false"/>
|
||||
<f:entry description="${it.formattedDescription}">
|
||||
<j:set var="readOnlyMode" value="true"/>
|
||||
<f:checkbox title="${h.escape(it.name)}" name="value" checked="${it.value}"/>
|
||||
</f:entry>
|
||||
<j:set var="readOnlyMode" value="true"/>
|
||||
<div>
|
||||
<f:checkbox title="${h.escape(it.name)}" name="value" checked="${it.value}" description="${it.formattedDescription}" />
|
||||
</div>
|
||||
</j:jelly>
|
||||
|
|
|
@ -28,7 +28,9 @@ THE SOFTWARE.
|
|||
xmlns:i="jelly:fmt" xmlns:p="/lib/hudson/project">
|
||||
<j:set var="escapeEntryTitleAndDescription" value="false"/>
|
||||
<f:entry title="${h.escape(it.name)}" description="${it.formattedDescription}">
|
||||
<j:set var="readOnlyMode" value="true"/>
|
||||
<f:textbox name="value" value="${it.value}"/>
|
||||
<div class="jenkins-quote jenkins-quote--full-width jenkins-quote--monospace" id="example">
|
||||
${it.value}
|
||||
<l:copyButton text="${it.value}" iconOnly="true" />
|
||||
</div>
|
||||
</f:entry>
|
||||
</j:jelly>
|
|
@ -0,0 +1,52 @@
|
|||
<!--
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 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
|
||||
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.
|
||||
-->
|
||||
|
||||
<?jelly escape-by-default='true'?>
|
||||
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:l="/lib/layout">
|
||||
<l:dialog title="${it.displayName}">
|
||||
<div class="app-parameters-dialog">
|
||||
<j:invokeStatic var="currentThread" className="java.lang.Thread" method="currentThread" />
|
||||
<j:invoke var="buildClass" on="${currentThread.contextClassLoader}" method="loadClass">
|
||||
<j:arg value="hudson.model.Run" />
|
||||
</j:invoke>
|
||||
<j:set var="build" value="${request2.findAncestorObject(buildClass)}" />
|
||||
<j:set var="escapeEntryTitleAndDescription" value="true" /> <!-- SECURITY-353 defense unless overridden -->
|
||||
<j:set var="readOnlyMode" value="true"/>
|
||||
<j:forEach var="parameterValue" items="${it.action.parameters}">
|
||||
<st:include it="${parameterValue}" page="value.jelly" />
|
||||
</j:forEach>
|
||||
</div>
|
||||
</l:dialog>
|
||||
|
||||
<button class="jenkins-details__item"
|
||||
data-type="dialog-opener"
|
||||
data-dialog-id="${dialogId}">
|
||||
<div class="jenkins-details__item__icon">
|
||||
<l:icon src="${it.iconClassName}" />
|
||||
</div>
|
||||
<span>
|
||||
${it.displayName}
|
||||
</span>
|
||||
</button>
|
||||
</j:jelly>
|
|
@ -0,0 +1,44 @@
|
|||
<!--
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 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
|
||||
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.
|
||||
-->
|
||||
<?jelly escape-by-default='true'?>
|
||||
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout">
|
||||
<st:documentation>
|
||||
<st:attribute name="title" use="required">
|
||||
The title for the dialog.
|
||||
</st:attribute>
|
||||
|
||||
A lazy-loaded dialog. Set 'data-type="dialog-opener"' and 'data-dialog-id="${dialogId}"'
|
||||
on your button to open the dialog.
|
||||
</st:documentation>
|
||||
|
||||
<j:set var="dialogId" value="${h.generateId()}" scope="parent" />
|
||||
|
||||
<l:renderOnDemand clazz="${dialogId}-template" tag="template" capture="it,dialogId">
|
||||
<l:ajax>
|
||||
<template id="${dialogId}-template" data-title="${attrs.title}">
|
||||
<d:invokeBody />
|
||||
</template>
|
||||
</l:ajax>
|
||||
</l:renderOnDemand>
|
||||
</j:jelly>
|
|
@ -292,6 +292,36 @@ function init() {
|
|||
return dialog.show();
|
||||
},
|
||||
};
|
||||
|
||||
behaviorShim.specify(
|
||||
"[data-type='dialog-opener']",
|
||||
"-dialog-",
|
||||
1000,
|
||||
(element) => {
|
||||
element.addEventListener("click", () => {
|
||||
const templateId = element.dataset.dialogId + "-template";
|
||||
|
||||
function render() {
|
||||
const template = document.querySelector("#" + templateId);
|
||||
const title = template.dataset.title;
|
||||
const content = template.content.firstElementChild.cloneNode(true);
|
||||
|
||||
behaviorShim.applySubtree(content, false);
|
||||
dialog.modal(content, {
|
||||
maxWidth: "550px",
|
||||
title: title,
|
||||
});
|
||||
}
|
||||
|
||||
if (document.querySelector("#" + templateId)) {
|
||||
render();
|
||||
return;
|
||||
}
|
||||
|
||||
renderOnDemand(document.querySelector("." + templateId), render);
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export default { init };
|
||||
|
|
|
@ -1,16 +1,33 @@
|
|||
@use "../abstracts/mixins";
|
||||
|
||||
$icon-size: 1.125rem;
|
||||
|
||||
.jenkins-details {
|
||||
display: flex;
|
||||
gap: 0.75rem 1.25rem;
|
||||
align-items: center;
|
||||
gap: 0.625rem 1.25rem;
|
||||
flex-wrap: wrap;
|
||||
|
||||
button.jenkins-details__item,
|
||||
a.jenkins-details__item {
|
||||
@include mixins.item($border: false);
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
inset: 0 -0.5rem;
|
||||
pointer-events: all;
|
||||
}
|
||||
}
|
||||
|
||||
&__item {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
font-weight: normal;
|
||||
color: var(--text-color-secondary);
|
||||
color: var(--text-color-secondary) !important;
|
||||
padding: 0;
|
||||
min-height: 2rem;
|
||||
|
||||
&__icon {
|
||||
display: flex;
|
||||
|
@ -18,7 +35,7 @@ $icon-size: 1.125rem;
|
|||
justify-content: center;
|
||||
align-self: start;
|
||||
width: $icon-size;
|
||||
height: 1lh;
|
||||
height: 2rem;
|
||||
|
||||
svg {
|
||||
width: $icon-size;
|
||||
|
@ -34,5 +51,6 @@ $icon-size: 1.125rem;
|
|||
&__separator {
|
||||
color: var(--text-color-secondary);
|
||||
opacity: 0.5;
|
||||
margin-inline: -0.5rem;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
$jenkins-dialog-padding: 1.5rem;
|
||||
$jenkins-dialog-font-size: 0.9375rem;
|
||||
$jenkins-dialog-padding: 1.25rem;
|
||||
|
||||
.jenkins-dialog {
|
||||
border-radius: 1rem;
|
||||
|
@ -25,12 +24,13 @@ $jenkins-dialog-font-size: 0.9375rem;
|
|||
}
|
||||
|
||||
&__title {
|
||||
font-size: 1.125rem;
|
||||
font-size: 1rem;
|
||||
font-weight: var(--font-bold-weight);
|
||||
padding: 0 $jenkins-dialog-padding;
|
||||
color: var(--text-color);
|
||||
overflow-wrap: anywhere;
|
||||
text-box: cap alphabetic;
|
||||
margin-top: 0.5625rem;
|
||||
}
|
||||
|
||||
&__contents {
|
||||
|
@ -38,7 +38,6 @@ $jenkins-dialog-font-size: 0.9375rem;
|
|||
overflow-wrap: break-word;
|
||||
padding: 0 $jenkins-dialog-padding;
|
||||
max-height: 75vh;
|
||||
font-size: $jenkins-dialog-font-size;
|
||||
color: var(--text-color-secondary);
|
||||
|
||||
&--modal {
|
||||
|
@ -72,7 +71,7 @@ $jenkins-dialog-font-size: 0.9375rem;
|
|||
}
|
||||
|
||||
&__subtitle {
|
||||
font-size: 1rem;
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: var(--font-bold-weight);
|
||||
color: var(--text-color-secondary);
|
||||
padding: 0;
|
||||
|
@ -81,10 +80,11 @@ $jenkins-dialog-font-size: 0.9375rem;
|
|||
|
||||
&__close-button {
|
||||
position: absolute;
|
||||
top: $jenkins-dialog-padding - 0.5rem;
|
||||
right: $jenkins-dialog-padding - 0.5rem;
|
||||
aspect-ratio: 1;
|
||||
top: $jenkins-dialog-padding;
|
||||
right: $jenkins-dialog-padding;
|
||||
padding: 0;
|
||||
width: 2rem;
|
||||
min-height: 2rem;
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
|
|
|
@ -87,8 +87,19 @@
|
|||
border-radius: calc(var(--form-input-border-radius) - 0.25rem);
|
||||
}
|
||||
|
||||
&--full-width {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
}
|
||||
|
||||
&--monospace {
|
||||
font-family: var(--font-family-mono);
|
||||
|
||||
// Copy buttons' tooltips inherit font-family from the parent element,
|
||||
// so override it
|
||||
span {
|
||||
font-family: var(--font-family-sans);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
@use "../base/breakpoints";
|
||||
@use "../components/dialogs";
|
||||
|
||||
.build-caption-progress-container {
|
||||
display: flex;
|
||||
|
@ -107,3 +108,13 @@
|
|||
font-size: var(--font-size-sm);
|
||||
}
|
||||
}
|
||||
|
||||
.app-parameters-dialog {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: dialogs.$jenkins-dialog-padding;
|
||||
|
||||
& > * {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue