diff --git a/core/src/main/java/hudson/Functions.java b/core/src/main/java/hudson/Functions.java index 67801475233d..ea677293cb60 100644 --- a/core/src/main/java/hudson/Functions.java +++ b/core/src/main/java/hudson/Functions.java @@ -143,6 +143,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.SortedMap; import java.util.TimeZone; @@ -158,6 +159,7 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import jenkins.console.ConsoleUrlProvider; +import jenkins.console.DefaultConsoleUrlProvider; import jenkins.console.WithConsoleUrl; import jenkins.model.GlobalConfiguration; import jenkins.model.GlobalConfigurationCategory; @@ -1993,6 +1995,14 @@ public static String joinPath(String... components) { return consoleUrl != null ? Stapler.getCurrentRequest().getContextPath() + '/' + consoleUrl : null; } + /** + * @param run the run + * @return the Console Provider for the given run, if null, the default Console Provider + */ + public static ConsoleUrlProvider getConsoleProviderFor(Run run) { + return Optional.ofNullable(ConsoleUrlProvider.getProvider(run)).orElse(new DefaultConsoleUrlProvider()); + } + /** * Escapes the character unsafe for e-mail address. * See the Wikipedia page for the details, diff --git a/core/src/main/java/jenkins/console/ConsoleUrlProvider.java b/core/src/main/java/jenkins/console/ConsoleUrlProvider.java index 637cf10145c6..9c6080ac02fa 100644 --- a/core/src/main/java/jenkins/console/ConsoleUrlProvider.java +++ b/core/src/main/java/jenkins/console/ConsoleUrlProvider.java @@ -83,11 +83,7 @@ default Descriptor 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 all() { final List providers = new ArrayList<>(); User currentUser = User.current(); if (currentUser != null) { @@ -105,8 +101,20 @@ default Descriptor 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) { diff --git a/core/src/main/java/jenkins/model/experimentalflags/NewBuildPageUserExperimentalFlag.java b/core/src/main/java/jenkins/model/experimentalflags/NewBuildPageUserExperimentalFlag.java new file mode 100644 index 000000000000..cbe6e908cdc1 --- /dev/null +++ b/core/src/main/java/jenkins/model/experimentalflags/NewBuildPageUserExperimentalFlag.java @@ -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 NewBuildPageUserExperimentalFlag extends BooleanUserExperimentalFlag { + public NewBuildPageUserExperimentalFlag() { + super("new-build-page.flag"); + } + + @Override + public String getDisplayName() { + return "New build page"; + } + + @Nullable + @Override + public String getShortDescription() { + return "Enables a revamped build page. This feature is still a work in progress, so some things might not work perfectly yet."; + } +} diff --git a/core/src/main/resources/hudson/model/Run/console-log.jelly b/core/src/main/resources/hudson/model/Run/console-log.jelly new file mode 100644 index 000000000000..bb2aad579a92 --- /dev/null +++ b/core/src/main/resources/hudson/model/Run/console-log.jelly @@ -0,0 +1,38 @@ + + + + + + + + + + ${%skipSome(offset / 1024)} + + + + + + + + + + + + +
+      
+ +
+ + + + +
+        
+        ${it.writeLogTo(offset,output)}
+      
+
+ + diff --git a/core/src/main/resources/hudson/model/Run/console-log.properties b/core/src/main/resources/hudson/model/Run/console-log.properties new file mode 100644 index 000000000000..12f13f7125d6 --- /dev/null +++ b/core/src/main/resources/hudson/model/Run/console-log.properties @@ -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 diff --git a/core/src/main/resources/hudson/model/Run/console.jelly b/core/src/main/resources/hudson/model/Run/console.jelly index ad38828dad5a..333fc741d9c0 100644 --- a/core/src/main/resources/hudson/model/Run/console.jelly +++ b/core/src/main/resources/hudson/model/Run/console.jelly @@ -50,38 +50,7 @@ THE SOFTWARE. ${%Console Output} - - - - - - ${%skipSome(offset/1024,"consoleFull")} - - - - - - - - - - - -
-            
- -
- - - - -
-            
-            ${it.writeLogTo(offset,output)}
-          
-
- + diff --git a/core/src/main/resources/hudson/model/Run/index.jelly b/core/src/main/resources/hudson/model/Run/index.jelly index b164817a164f..c358324918f2 100644 --- a/core/src/main/resources/hudson/model/Run/index.jelly +++ b/core/src/main/resources/hudson/model/Run/index.jelly @@ -25,53 +25,62 @@ THE SOFTWARE. - - + - - - - - - - - + + + + + + + - ${it.displayName} () + + + + + + + + -
- -
+ ${it.displayName} () -
-
- ${%startedAgo(it.timestampString)} -
-
- - ${%beingExecuted(it.executor.timestampString)} - - - ${%Took} ${it.durationString} - - -
-
+
+ +
+
+
+ ${%startedAgo(it.timestampString)} +
+
+ + ${%beingExecuted(it.executor.timestampString)} + + + ${%Took} ${it.durationString} + + +
+
- - - - - - +
+ - -
+ + + + - -
-
+ + + + +
+
+ +
diff --git a/core/src/main/resources/hudson/model/Run/new-build-page.jelly b/core/src/main/resources/hudson/model/Run/new-build-page.jelly new file mode 100644 index 000000000000..cf1c270163e5 --- /dev/null +++ b/core/src/main/resources/hudson/model/Run/new-build-page.jelly @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + ${it.displayName} () + +
+ +
+ + + +
+
+ ${%startedAgo(it.timestampString)} +
+
+ + ${%beingExecuted(it.executor.timestampString)} + + + ${%Took} ${it.durationString} + + +
+
+ + + + + + + + + + +
+ + +
+
+
diff --git a/core/src/main/resources/hudson/model/Run/new-build-page.properties b/core/src/main/resources/hudson/model/Run/new-build-page.properties new file mode 100644 index 000000000000..c52678dca5a0 --- /dev/null +++ b/core/src/main/resources/hudson/model/Run/new-build-page.properties @@ -0,0 +1,2 @@ +startedAgo=Started {0} ago +beingExecuted=Build has been executing for {0} diff --git a/core/src/main/resources/jenkins/console/DefaultConsoleUrlProvider/console.jelly b/core/src/main/resources/jenkins/console/DefaultConsoleUrlProvider/console.jelly new file mode 100644 index 000000000000..ad11b66b7699 --- /dev/null +++ b/core/src/main/resources/jenkins/console/DefaultConsoleUrlProvider/console.jelly @@ -0,0 +1,14 @@ + + + + + + + + + +
+ +
+
+
diff --git a/core/src/main/resources/lib/hudson/progressive-text.js b/core/src/main/resources/lib/hudson/progressive-text.js index 71f84b2b2238..696a4d144c76 100644 --- a/core/src/main/resources/lib/hudson/progressive-text.js +++ b/core/src/main/resources/lib/hudson/progressive-text.js @@ -10,7 +10,9 @@ 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 diff --git a/src/main/scss/pages/_build.scss b/src/main/scss/pages/_build.scss index 6bcf404773fa..cb5e3298faf1 100644 --- a/src/main/scss/pages/_build.scss +++ b/src/main/scss/pages/_build.scss @@ -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); + } +} diff --git a/war/src/main/webapp/scripts/hudson-behavior.js b/war/src/main/webapp/scripts/hudson-behavior.js index 715984f6ca33..93918dac11c5 100644 --- a/war/src/main/webapp/scripts/hudson-behavior.js +++ b/war/src/main/webapp/scripts/hudson-behavior.js @@ -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; }, }; }