From 62d8d525c75ffe39a8cf83343f9743995869d6af Mon Sep 17 00:00:00 2001 From: Tim Jacomb <21194782+timja@users.noreply.github.com> Date: Sat, 15 Aug 2026 21:55:31 +0100 Subject: [PATCH] Switch to forking jvm for development mode with hpi:run (#27239) --- .github/renovate.json | 9 -- CONTRIBUTING.md | 23 ++++- pom.xml | 3 +- war/nbactions.xml | 7 +- war/pom.xml | 126 +++++++++++++------------ war/src/main/java/executable/Main.java | 42 +++++++-- war/src/realm.properties | 29 ------ 7 files changed, 121 insertions(+), 118 deletions(-) delete mode 100644 war/src/realm.properties diff --git a/.github/renovate.json b/.github/renovate.json index ea9952e5a3..e8b7afefd4 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -4,15 +4,6 @@ "prHourlyLimit": 0, "prConcurrentLimit": 0, "packageRules": [ - { - "description": "Should be upgraded in lockstep in order to keep their corresponding Jetty versions aligned", - "matchManagers": ["maven"], - "groupName": "Winstone and Jetty", - "matchPackageNames": [ - "org.eclipse.jetty.ee9:jetty-ee9-maven-plugin", - "org.jenkins-ci:winstone" - ] - }, { "description": "Provided by Jetty and should be aligned with the version provided by the version of Jetty we deliver. See: https://github.com/jenkinsci/jenkins/pull/5211", "matchManagers": ["maven"], diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8e3fba0624..77d14d54cf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -59,10 +59,22 @@ and then attach IDE Debugger to it. To launch a development instance, after [building the WAR file](#building-the-war-file), run: ```sh -MAVEN_OPTS='--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED' mvn -pl war jetty:run +mvn -pl war hpi:run ``` -(Beware that `maven-plugin` builds will not work in this mode, due to class loading conflicts.) +This forks a JVM that serves `war/target/jenkins` under Winstone, the same way `jenkins.war` is +served in production. Jenkins is available on and uses +`war/work` as its `JENKINS_HOME`; pass `-Dport=`, `-Dhost=` or `-DjenkinsHome=` to change that. + +Edits to `war/src/main/webapp`, and to Jelly views and their sibling `.properties` files under +`core/src/main/resources`, are picked up on the next page load. Edits to a `Messages.properties` +take effect when you restart the development instance — no repackaging needed, but the bundle is +cached for the life of the JVM. Changes to Java code still need a rebuild. + +Because Jenkins now runs in its own JVM, starting Maven under a debugger no longer attaches to +Jenkins. Pass `-Dmaven.hpi.debug=true` to have the forked JVM suspend and wait for a debugger, or +give the option a JVM debug string of your own; see +[maven-hpi-plugin#862](https://github.com/jenkinsci/maven-hpi-plugin/pull/862). ### Running the Yarn frontend build @@ -85,10 +97,10 @@ yarn To work on the `war` module frontend assets, two processes are needed at the same time: -On one terminal, start a development server that will not process frontend assets: +On one terminal, start a development server: ```sh -MAVEN_OPTS='--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED' mvn -pl war jetty:run -Dskip.frontend +mvn -pl war hpi:run ``` Open another terminal and start a [webpack](https://webpack.js.org/) dev server, after [optionally adding Node and Yarn to your path](#running-the-yarn-frontend-build): @@ -97,6 +109,9 @@ Open another terminal and start a [webpack](https://webpack.js.org/) dev server, yarn start ``` +`yarn start` writes to `war/src/main/webapp/jsbundles`, which the development server serves +directly, so a browser reload is enough to pick up the rebuilt bundles. + ### Linting For linting we use a number of tools: diff --git a/pom.xml b/pom.xml index 62509cd890..d0425f13e7 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci jenkins - 2.1391.vb_93f2a_83691d + 2.1395.v8534005506ec @@ -103,7 +103,6 @@ THE SOFTWARE. false - 8.1047.v331d3186f463 24.19.0 diff --git a/war/nbactions.xml b/war/nbactions.xml index 04a8d17042..1867202abb 100644 --- a/war/nbactions.xml +++ b/war/nbactions.xml @@ -30,16 +30,17 @@ Ensures that NetBeans knows which goals to run when Run Project is selected. run - jetty:run + hpi:run debug - jetty:run + hpi:run - maven + + true diff --git a/war/pom.xml b/war/pom.xml index c4c6e505b3..b361e9c66a 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -262,28 +262,6 @@ THE SOFTWARE. true - - - support-log-formatter - - copy - - generate-resources - - - - io.jenkins.lib - support-log-formatter - 123.v72513b_f61500 - - - ${project.build.directory} - true - true - false - true - - @@ -329,35 +307,75 @@ THE SOFTWARE. - org.eclipse.jetty.ee9 - jetty-ee9-maven-plugin - 12.1.12 + org.jenkins-ci.tools + maven-hpi-plugin + + ${project.build.directory}/${project.build.finalName}.war + ${project.build.directory}/${project.build.finalName} + + ${contextPath} + - 0 - - ${host} - ${port} - - - - default - - ${basedir}/src/realm.properties - - - + + + jsbundles + ${project.basedir}/src/main/webapp/jsbundles + + + images + ${project.basedir}/src/main/webapp/images + + + scripts + ${project.basedir}/src/main/webapp/scripts + + + css + ${project.basedir}/src/main/webapp/css + + + help + ${project.basedir}/src/main/webapp/help + + + WEB-INF/classes/images + ${project.basedir}/src/main/resources/images + + + WEB-INF/classes/hudson + ${project.basedir}/../core/src/main/resources/hudson + + + WEB-INF/classes/jenkins + ${project.basedir}/../core/src/main/resources/jenkins + + + WEB-INF/classes/lib + ${project.basedir}/../core/src/main/resources/lib + + - ${JENKINS_HOME} - - true + true true @@ -365,26 +383,10 @@ THE SOFTWARE. true - ../core/src/main/resources; - - - ${project.build.directory}/${project.build.finalName}/WEB-INF/plugins/*.hpi + ${project.basedir}/../core/src/main/resources true - true - - com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl - - - ${project.basedir}/../core/src/main/resources,${project.build.directory}/support-log-formatter.jar - ${contextPath} - .*(jenkins-core|target/classes).* - diff --git a/war/src/main/java/executable/Main.java b/war/src/main/java/executable/Main.java index 61ae444930..ca982a458d 100644 --- a/war/src/main/java/executable/Main.java +++ b/war/src/main/java/executable/Main.java @@ -213,6 +213,19 @@ public class Main { } } + File explodedWar = null; + for (String arg : args) { + if (arg.startsWith("--explodedWar=")) { + explodedWar = new File(arg.substring("--explodedWar=".length())); + if (!new File(explodedWar, "WEB-INF/web.xml").isFile()) { + System.err.println("The explodedWar value is not an expanded WAR file: no WEB-INF/web.xml in " + explodedWar); + System.exit(1); + } + // if specified multiple times, the first one wins + break; + } + } + for (String arg : args) { if (arg.startsWith("--pluginroot=")) { System.setProperty("hudson.PluginManager.workDir", @@ -231,14 +244,22 @@ public class Main { // figure out the arguments trimOffOurOptions(arguments); - arguments.add(0, "--warfile=" + me.getAbsolutePath()); - if (!hasOption(arguments, "--webroot=")) { - // defaults to ~/.jenkins/war since many users reported that cron job attempts to clean up - // the contents in the temporary directory. - final File jenkinsHome = getJenkinsHome(); - final File webRoot = new File(jenkinsHome, "war"); - System.out.println("webroot: " + webRoot); - arguments.add("--webroot=" + webRoot); + if (explodedWar != null) { + // Serve the directory as it is. Passing --warfile as well would make Winstone extract the + // WAR over the top of it, wiping it in the process. + arguments.removeIf(arg -> arg.startsWith("--webroot=")); + System.out.println("webroot: " + explodedWar + " (already expanded)"); + arguments.add("--webroot=" + explodedWar); + } else { + arguments.add(0, "--warfile=" + me.getAbsolutePath()); + if (!hasOption(arguments, "--webroot=")) { + // defaults to ~/.jenkins/war since many users reported that cron job attempts to clean up + // the contents in the temporary directory. + final File jenkinsHome = getJenkinsHome(); + final File webRoot = new File(jenkinsHome, "war"); + System.out.println("webroot: " + webRoot); + arguments.add("--webroot=" + webRoot); + } } // only do a cleanup if you set the extractedFilesFolder property. @@ -292,6 +313,8 @@ public class Main { "\n" + "Options:\n" + " --webroot = folder where the WAR file is expanded into. Default is ${JENKINS_HOME}/war\n" + + " --explodedWar = folder holding an already expanded WAR file, served as is. No extraction\n" + + " is performed and --webroot is ignored\n" + " --pluginroot = folder where the plugin archives are expanded into. Default is ${JENKINS_HOME}/plugins\n" + " (NOTE: this option does not change the directory where the plugin archives are stored)\n" + " --extractedFilesFolder = folder where extracted files are to be located. Default is the temp folder\n" + @@ -348,7 +371,8 @@ public class Main { private static void trimOffOurOptions(List arguments) { arguments.removeIf(arg -> arg.startsWith("--extractedFilesFolder") - || arg.startsWith("--pluginroot") || arg.startsWith(ENABLE_FUTURE_JAVA_CLI_SWITCH)); + || arg.startsWith("--pluginroot") || arg.startsWith("--explodedWar") + || arg.startsWith(ENABLE_FUTURE_JAVA_CLI_SWITCH)); } /** diff --git a/war/src/realm.properties b/war/src/realm.properties deleted file mode 100644 index 5211348e39..0000000000 --- a/war/src/realm.properties +++ /dev/null @@ -1,29 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi -# -# 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. - -# For the syntax of this file see: -# https://wiki.eclipse.org/Jetty/Tutorial/Realms#HashLoginService - -guest: guest -admin: admin,admin -alice: alice,admin -david: david,developer