This is an automated email from the ASF dual-hosted git repository.

matrei pushed a commit to branch banner-versions
in repository https://gitbox.apache.org/repos/asf/grails-core.git

commit e03070ec0e7a5751ef796bf51f32934c1774afe9
Author: Mattias Reichel <[email protected]>
AuthorDate: Fri Nov 14 12:17:32 2025 +0100

    feat: add versions to banner
---
 .../src/main/resources/grails-banner.txt           |  3 +-
 .../src/main/groovy/grails/boot/GrailsApp.groovy   |  4 +-
 .../main/groovy/grails/boot/GrailsBanner.groovy    | 99 ++++++++++++++++++++++
 3 files changed, 101 insertions(+), 5 deletions(-)

diff --git a/grails-bootstrap/src/main/resources/grails-banner.txt 
b/grails-bootstrap/src/main/resources/grails-banner.txt
index fdc3539439..c6792b01bc 100644
--- a/grails-bootstrap/src/main/resources/grails-banner.txt
+++ b/grails-bootstrap/src/main/resources/grails-banner.txt
@@ -1,4 +1,3 @@
-
 >                                                                            <
 >                            ____           _ _                              <
 >                           / ___|_ __ __ _(_) |___                          <
@@ -6,4 +5,4 @@
 >                          | |_| | | | (_| | | \__ \                         <
 >                           \____|_|  \__,_|_|_|___/                         <
 >                          https://grails.apache.org                         <
->                                                                            <
+>                                                                            <
\ No newline at end of file
diff --git a/grails-core/src/main/groovy/grails/boot/GrailsApp.groovy 
b/grails-core/src/main/groovy/grails/boot/GrailsApp.groovy
index 16bc28b2a8..2fd34aee6d 100644
--- a/grails-core/src/main/groovy/grails/boot/GrailsApp.groovy
+++ b/grails-core/src/main/groovy/grails/boot/GrailsApp.groovy
@@ -27,12 +27,10 @@ import 
org.codehaus.groovy.control.CompilationFailedException
 import org.codehaus.groovy.control.CompilationUnit
 import org.codehaus.groovy.control.CompilerConfiguration
 
-import org.springframework.boot.ResourceBanner
 import org.springframework.boot.SpringApplication
 import org.springframework.boot.web.context.WebServerApplicationContext
 import org.springframework.context.ConfigurableApplicationContext
 import org.springframework.core.env.ConfigurableEnvironment
-import org.springframework.core.io.ClassPathResource
 import org.springframework.core.io.ResourceLoader
 
 import grails.compiler.ast.ClassInjector
@@ -95,7 +93,7 @@ class GrailsApp extends SpringApplication {
      */
     GrailsApp(ResourceLoader resourceLoader, Class<?>... sources) {
         super(resourceLoader, sources)
-        banner = new ResourceBanner(new ClassPathResource(GRAILS_BANNER))
+        banner = new GrailsBanner(GRAILS_BANNER)
     }
 
     @Override
diff --git a/grails-core/src/main/groovy/grails/boot/GrailsBanner.groovy 
b/grails-core/src/main/groovy/grails/boot/GrailsBanner.groovy
new file mode 100644
index 0000000000..107e29315e
--- /dev/null
+++ b/grails-core/src/main/groovy/grails/boot/GrailsBanner.groovy
@@ -0,0 +1,99 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package grails.boot
+
+import groovy.transform.CompileStatic
+
+import org.springframework.boot.Banner
+import org.springframework.boot.SpringBootVersion
+import org.springframework.core.SpringVersion
+import org.springframework.core.env.Environment
+import org.springframework.core.io.ClassPathResource
+
+import grails.util.BuildSettings
+
+@CompileStatic
+class GrailsBanner implements Banner {
+
+    private static final int FALLBACK_BANNER_WIDTH = 80
+    private static final int VERSIONS_MARGIN = 4
+    private static final String VERSIONS_SEPARATOR = ' | '
+
+    private final String asciiArt
+    private final Map versions
+
+    GrailsBanner(String grailsBannerFile) {
+        def bannerResource = new ClassPathResource(grailsBannerFile)
+        def appNameResolver = { Environment env -> 
env.getProperty('info.app.name') ?: 'application' }
+        def appVersionResolver = { Environment env -> 
env.getProperty('info.app.version') ?: 'unknown' }
+        asciiArt = bannerResource.exists() ? bannerResource.inputStream.text : 
''
+        versions = [
+                (appNameResolver): appVersionResolver,
+                'Grails': BuildSettings.grailsVersion,
+                'Groovy': GroovySystem.version,
+                'JVM': System.getProperty('java.version'),
+                'Spring Boot': SpringBootVersion.version,
+                'Spring': SpringVersion.version
+        ]
+    }
+
+    @Override
+    void printBanner(Environment environment, Class<?> sourceClass, 
PrintStream out) {
+        int bannerWidth = longestLineLength(asciiArt) ?: FALLBACK_BANNER_WIDTH
+        def versionPairs = versions.collectEntries {
+            [(resolveValue(it.key, environment)): resolveValue(it.value, 
environment)]
+        }
+        out.println()
+        out.println(asciiArt)
+        buildVersionRows(versionPairs, 
bannerWidth).stream().forEach(out::println)
+    }
+
+    private static String resolveValue(Object value, Environment environment) {
+        value instanceof Closure ? value(environment) : value
+    }
+
+    private static int longestLineLength(String text) {
+        text.readLines()*.size()?.max() ?: 0
+    }
+
+    private static List buildVersionRows(Map versions, int bannerWidth) {
+        def maxWidth = bannerWidth - VERSIONS_MARGIN * 2
+        def rows = []
+        def currentRow = new StringBuilder()
+        def countInRow = 0
+        versions.each {
+            String value = "$it.key: $it.value"
+            int proposedLength = currentRow.size() + (countInRow > 0 ? 
VERSIONS_SEPARATOR.size() : 0) + value.size()
+            boolean wouldOverflow = proposedLength > maxWidth
+            if (wouldOverflow) {
+                rows << currentRow.center(bannerWidth)
+                currentRow.length = 0
+            }
+            if (currentRow.size() > 0) {
+                currentRow << VERSIONS_SEPARATOR
+            }
+            currentRow << value
+            countInRow++
+        }
+        if (countInRow > 0) {
+            rows << currentRow.center(bannerWidth)
+        }
+        rows
+    }
+}

Reply via email to