Skip to content

Commit

Permalink
Init
Browse files Browse the repository at this point in the history
  • Loading branch information
janfaracik committed Jan 4, 2025
1 parent 519eb19 commit 11e4b8d
Show file tree
Hide file tree
Showing 11 changed files with 158 additions and 42 deletions.
12 changes: 12 additions & 0 deletions core/src/main/java/hudson/Functions.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import hudson.model.ParameterDefinition;
import hudson.model.ParameterDefinition.ParameterDescriptor;
import hudson.model.PasswordParameterDefinition;
import hudson.model.Queue;
import hudson.model.Run;
import hudson.model.Slave;
import hudson.model.TimeZoneProperty;
Expand Down Expand Up @@ -1993,6 +1994,17 @@ public static String joinPath(String... components) {
return consoleUrl != null ? Stapler.getCurrentRequest().getContextPath() + '/' + consoleUrl : null;
}

public static @CheckForNull ConsoleUrlProvider getConsoleProvider(Queue.Executable executable) {
if (executable == null) {
return null;
} else if (executable instanceof Run) {
return ConsoleUrlProvider.getProvider((Run<?, ?>) executable);
} else {
// Handles cases such as PlaceholderExecutable for Pipeline node steps.
return getConsoleProvider(executable.getParentExecutable());
}
}

/**
* Escapes the character unsafe for e-mail address.
* See <a href="https://en.wikipedia.org/wiki/Email_address">the Wikipedia page</a> for the details,
Expand Down
20 changes: 14 additions & 6 deletions core/src/main/java/jenkins/console/ConsoleUrlProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,7 @@ default Descriptor<ConsoleUrlProvider> getDescriptor() {
return Stapler.getCurrentRequest().getContextPath() + '/' + run.getConsoleUrl();
}

/**
* Looks up the {@link #getConsoleUrl} value from the first provider to offer one.
* @since 2.476
*/
static @NonNull String consoleUrlOf(Run<?, ?> run) {
static List<ConsoleUrlProvider> all() {
final List<ConsoleUrlProvider> providers = new ArrayList<>();
User currentUser = User.current();
if (currentUser != null) {
Expand All @@ -105,8 +101,20 @@ default Descriptor<ConsoleUrlProvider> getDescriptor() {
if (globalProviders != null) {
providers.addAll(globalProviders);
}
return providers;
}

static ConsoleUrlProvider getProvider(Run<?, ?> run) {
return all().stream().filter(provider -> provider.getConsoleUrl(run) != null).findFirst().orElse(null);
}

/**
* Looks up the {@link #getConsoleUrl} value from the first provider to offer one.
* @since 2.476
*/
static @NonNull String consoleUrlOf(Run<?, ?> run) {
String url = null;
for (ConsoleUrlProvider provider : providers) {
for (ConsoleUrlProvider provider : all()) {
try {
String tempUrl = provider.getConsoleUrl(run);
if (tempUrl != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* 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.
*/

package jenkins.model.experimentalflags;

import edu.umd.cs.findbugs.annotations.Nullable;
import hudson.Extension;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;

@Extension
@Restricted(NoExternalUse.class)
public class ConsoleWidgetUserExperimentalFlag extends BooleanUserExperimentalFlag {
public ConsoleWidgetUserExperimentalFlag() {
super("console-widget.flag");
}

@Override
public String getDisplayName() {
return "Show Console Output on build pages";
}

@Nullable
@Override
public String getShortDescription() {
return "Shows the Console Output as a widget on build pages.";
}
}
38 changes: 38 additions & 0 deletions core/src/main/resources/hudson/model/Run/console-log.jelly
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:l="/lib/layout" xmlns:t="/lib/hudson">
<j:set var="threshold" value="${h.getSystemProperty('hudson.consoleTailKB')?:'150'}" />
<!-- Show at most last 150KB (can override with system property) unless consoleFull is set -->
<j:set var="offset" value="${empty(consoleFull) ? it.logText.length()-threshold*1024 : 0}" />
<j:choose>
<j:when test="${offset > 0}">
<a class="jenkins-button jenkins-!-accent-color jenkins-!-padding-2 jenkins-!-margin-bottom-2" style="width: 100%; justify-content: start" href="consoleFull">
<l:icon src="symbol-help-circle" />
${%skipSome(offset / 1024)}
</a>
</j:when>
<j:otherwise>
<j:set var="offset" value="${0}" />
</j:otherwise>
</j:choose>

<j:out value="${h.generateConsoleAnnotationScriptAndStylesheet()}"/>

<j:choose>
<!-- Do progressive console output -->
<j:when test="${it.isLogUpdated()}">
<pre id="out" class="console-output" />
<div id="spinner">
<l:progressAnimation/>
</div>
<t:progressiveText href="logText/progressiveHtml" idref="out" spinner="spinner"
startOffset="${offset}" onFinishEvent="jenkins:consoleFinished"/>
</j:when>
<!-- output is completed now. -->
<j:otherwise>
<pre class="console-output">
<st:getOutput var="output" />
<j:whitespace>${it.writeLogTo(offset,output)}</j:whitespace>
</pre>
</j:otherwise>
</j:choose>
</j:jelly>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
skipSome=This log is too long to show here, {0,number,integer} KB has been skipped — click to see the complete log
33 changes: 1 addition & 32 deletions core/src/main/resources/hudson/model/Run/console.jelly
Original file line number Diff line number Diff line change
Expand Up @@ -50,38 +50,7 @@ THE SOFTWARE.
${%Console Output}
</t:buildCaption>

<j:set var="threshold" value="${h.getSystemProperty('hudson.consoleTailKB')?:'150'}" />
<!-- Show at most last 150KB (can override with system property) unless consoleFull is set -->
<j:set var="offset" value="${empty(consoleFull) ? it.logText.length()-threshold*1024 : 0}" />
<j:choose>
<j:when test="${offset > 0}">
${%skipSome(offset/1024,"consoleFull")}
</j:when>
<j:otherwise>
<j:set var="offset" value="${0}" />
</j:otherwise>
</j:choose>

<j:out value="${h.generateConsoleAnnotationScriptAndStylesheet()}"/>

<j:choose>
<!-- Do progressive console output -->
<j:when test="${it.isLogUpdated()}">
<pre id="out" class="console-output" />
<div id="spinner">
<l:progressAnimation/>
</div>
<t:progressiveText href="logText/progressiveHtml" idref="out" spinner="spinner"
startOffset="${offset}" onFinishEvent="jenkins:consoleFinished"/>
</j:when>
<!-- output is completed now. -->
<j:otherwise>
<pre class="console-output" id="out">
<st:getOutput var="output" />
<j:whitespace>${it.writeLogTo(offset,output)}</j:whitespace>
</pre>
</j:otherwise>
</j:choose>
<st:include page="console-log.jelly" />
</l:main-panel>
</l:layout>
</j:jelly>
5 changes: 5 additions & 0 deletions core/src/main/resources/hudson/model/Run/index.jelly
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ THE SOFTWARE.
<t:editableDescription permission="${it.UPDATE}" hideButton="true"/>
</div>

<l:userExperimentalFlag var="showConsoleWidget" flagClassName="jenkins.model.experimentalflags.ConsoleWidgetUserExperimentalFlag" />
<j:if test="${showConsoleWidget}">
<st:include page="console.jelly" from="${h.getConsoleProvider(it)}" optional="true" />
</j:if>

<div style="float:right; z-index: 1; position:relative; margin-left: 1em">
<div style="margin-top:1em">
${%startedAgo(it.timestampString)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:l="/lib/layout">
<j:set var="controls">
<a href="consoleText" download="${it.displayName}.txt" tooltip="${%Download}" class="jenkins-card__reveal">
<l:icon src="symbol-download" />
</a>
</j:set>

<l:card title="${%Console Output}" expandable="console" controls="${controls}">
<div class="app-console-output-widget progressive-text-container">
<st:include page="console-log.jelly" />
</div>
</l:card>
</j:jelly>
2 changes: 1 addition & 1 deletion core/src/main/resources/lib/hudson/progressive-text.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Behaviour.specify(
let onFinishEvent = holder.getAttribute("data-on-finish-event");
let errorMessage = holder.getAttribute("data-error-message");

var scroller = new AutoScroller(document.body);
var scroller = new AutoScroller(holder.closest(".progressive-text-container") || document.body);
/*
fetches the latest update from the server
@param e
Expand Down
16 changes: 16 additions & 0 deletions src/main/scss/pages/_build.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,19 @@
flex-wrap: nowrap;
gap: 10px;
}

.app-console-output-widget {
min-height: 340px;
max-height: 340px;
overflow-y: auto;
margin: 0 -1rem -1rem;
padding: 0 1rem 1rem;

pre {
background: transparent;
margin: 0;
padding: 0;
line-height: 1.75;
font-size: var(--font-size-sm);
}
}
10 changes: 7 additions & 3 deletions war/src/main/webapp/scripts/hudson-behavior.js
Original file line number Diff line number Diff line change
Expand Up @@ -2030,10 +2030,14 @@ function AutoScroller(scrollContainer) {
scrollToBottom: function () {
var scrollDiv = this.scrollContainer;
var currentHeight = this.getCurrentHeight();
if (document.documentElement) {
document.documentElement.scrollTop = currentHeight;

if (scrollDiv === document.body) {
window.scrollTo({
top: currentHeight,
});
} else {
scrollDiv.scrollTop = currentHeight;
}
scrollDiv.scrollTop = currentHeight;
},
};
}
Expand Down

0 comments on commit 11e4b8d

Please sign in to comment.