From ba5f3ebea64bc6501abe2f6e5b3d23d80fe1ef06 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Thu, 26 Jan 2023 16:21:53 -0800 Subject: [PATCH] Remove dependency on Jenkins core --- pom.xml | 233 ++++++------------ src/main/java/hudson/model/UpdateSite.java | 105 ++++++++ .../tools/test/PluginCompatTester.java | 81 +++--- .../tools/test/maven/ExternalMavenRunner.java | 4 +- .../jenkins/tools/test/model/MavenBom.java | 2 +- .../jenkins/tools/test/model/MavenPom.java | 4 +- .../tools/test/model/PluginCompatReport.java | 5 +- .../tools/test/NonStandardTagHookTest.java | 7 +- 8 files changed, 223 insertions(+), 218 deletions(-) create mode 100644 src/main/java/hudson/model/UpdateSite.java diff --git a/pom.xml b/pom.xml index e4602c0a8..c7b49310d 100644 --- a/pom.xml +++ b/pom.xml @@ -33,14 +33,14 @@ - commons-io - commons-io - 2.11.0 + commons-beanutils + commons-beanutils + 1.9.4 - net.java.dev.jna - jna - 5.13.0 + commons-collections + commons-collections + 3.2.2 org.slf4j @@ -56,178 +56,67 @@ jcommander 1.82 + + com.github.spotbugs + spotbugs-annotations + true + + + com.google.code.findbugs + jsr305 + + + + + com.thoughtworks.xstream + xstream + 1.4.20 + + + xpp3 + xpp3_min + + + + + commons-io + commons-io + 2.11.0 + + + commons-lang + commons-lang + 2.6 + io.jenkins.lib support-log-formatter 1.2 - - jakarta.servlet - jakarta.servlet-api - 4.0.4 - org.apache.maven maven-model 3.8.7 - org.jenkins-ci.main - jenkins-core - 2.386 + org.dom4j + dom4j + 2.1.3 + + + org.jenkins-ci + version-number + 1.11 + + + org.kohsuke.stapler + json-lib + 2.4-jenkins-3 - - args4j - args4j - - - com.google.inject - guice - - - com.infradna.tool - bridge-method-annotation - - - com.sun.solaris - embedded_su4j - - - com.sun.xml.txw2 - txw2 - - - commons-jelly - commons-jelly-tags-fmt - - - commons-jelly - commons-jelly-tags-xml - commons-logging commons-logging - - io.jenkins.stapler - jenkins-stapler-support - - - jakarta.servlet.jsp.jstl - jakarta.servlet.jsp.jstl-api - - - jaxen - jaxen - - - jline - jline - - - net.java.sezpoz - sezpoz - - - net.sf.kxml - kxml2 - - - org.antlr - antlr4-runtime - - - org.apache.ant - ant - - - org.apache.commons - commons-compress - - - org.codehaus.groovy - groovy-all - - - org.connectbot.jbcrypt - jbcrypt - - - org.fusesource.jansi - jansi - - - org.jenkins-ci - annotation-indexer - - - org.jenkins-ci - crypto-util - - - org.jenkins-ci - memory-monitor - - - org.jenkins-ci.main - cli - - - org.jenkins-ci.main - websocket-spi - - - org.jfree - jfreechart - - - org.jvnet.hudson - commons-jelly-tags-define - - - org.jvnet.robust-http-client - robust-http-client - - - org.jvnet.winp - winp - - - org.kohsuke - windows-package-checker - - - org.kohsuke.jinterop - j-interop - - - org.kohsuke.stapler - stapler-adjunct-codemirror - - - org.kohsuke.stapler - stapler-adjunct-timeline - - - org.ow2.asm - asm - - - org.ow2.asm - asm-analysis - - - org.ow2.asm - asm-commons - - - org.ow2.asm - asm-tree - - - org.ow2.asm - asm-util - @@ -241,16 +130,36 @@ + + org.slf4j + jcl-over-slf4j + ${slf4j.version} + + + org.slf4j + log4j-over-slf4j + ${slf4j.version} + org.slf4j slf4j-jdk14 ${slf4j.version} + + org.springframework + spring-core + 5.3.25 + junit junit test + + org.hamcrest + hamcrest-core + test + org.jenkins-ci test-annotations diff --git a/src/main/java/hudson/model/UpdateSite.java b/src/main/java/hudson/model/UpdateSite.java new file mode 100644 index 000000000..e4eaeaa9f --- /dev/null +++ b/src/main/java/hudson/model/UpdateSite.java @@ -0,0 +1,105 @@ +package hudson.model; + +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import net.sf.json.JSONObject; + +public final class UpdateSite { + + /** + * In-memory representation of the update center data. + */ + public static final class Data { + /** + * The {@link UpdateSite} ID. + */ + @NonNull public final String sourceId; + + /** + * The latest jenkins.war. + */ + @NonNull public final Entry core; + + /** + * Plugins in the repository, keyed by their artifact IDs. + */ + @NonNull + public final Map plugins = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + + public Data(JSONObject o) { + this.sourceId = o.getString("id"); + this.core = new Entry(sourceId, o.getJSONObject("core")); + for (Map.Entry e : (Set>) o.getJSONObject("plugins").entrySet()) { + Plugin p = new Plugin(sourceId, e.getValue()); + plugins.put(e.getKey(), p); + } + } + } + + public static class Entry { + /** + * {@link UpdateSite} ID. + */ + @NonNull public final String sourceId; + + /** + * Artifact ID. + */ + @NonNull public final String name; + + /** + * The version. + */ + @NonNull public final String version; + + /** + * Download URL. + */ + @NonNull public final String url; + + public Entry(String sourceId, JSONObject o) { + this.sourceId = sourceId; + this.name = o.getString("name"); + this.version = o.getString("version"); + this.url = o.getString("url"); + } + } + + public static class Plugin extends Entry { + /** + * Human readable title of the plugin. + */ + @CheckForNull public final String title; + + /** + * Dependencies of this plugin, a name -> version mapping. + */ + @NonNull public final Map dependencies = new HashMap<>(); + + /** + * Optional dependencies of this plugin. + */ + @NonNull public final Map optionalDependencies = new HashMap<>(); + + public Plugin(String sourceId, JSONObject o) { + super(sourceId, o); + this.title = o.optString("title"); + for (Object jo : o.getJSONArray("dependencies")) { + JSONObject depObj = (JSONObject) jo; + if (Boolean.parseBoolean(depObj.getString("optional"))) { + optionalDependencies.put(depObj.getString("name"), depObj.getString("version")); + } else { + dependencies.put(depObj.getString("name"), depObj.getString("version")); + } + } + } + + public final String getDisplayName() { + return title != null ? title : name; + } + } +} diff --git a/src/main/java/org/jenkins/tools/test/PluginCompatTester.java b/src/main/java/org/jenkins/tools/test/PluginCompatTester.java index 48bb69118..cd07960b5 100644 --- a/src/main/java/org/jenkins/tools/test/PluginCompatTester.java +++ b/src/main/java/org/jenkins/tools/test/PluginCompatTester.java @@ -26,10 +26,7 @@ package org.jenkins.tools.test; import edu.umd.cs.findbugs.annotations.NonNull; -import hudson.AbortException; -import hudson.Functions; import hudson.model.UpdateSite; -import hudson.model.UpdateSite.Plugin; import hudson.util.VersionNumber; import io.jenkins.lib.versionnumber.JavaSpecificationVersion; import java.io.BufferedReader; @@ -38,13 +35,16 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.io.PrintWriter; +import java.io.StringWriter; import java.io.UncheckedIOException; -import java.lang.reflect.Constructor; import java.net.MalformedURLException; import java.net.URL; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; @@ -75,13 +75,12 @@ import javax.xml.transform.stream.StreamSource; import net.sf.json.JSONArray; import net.sf.json.JSONObject; +import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.maven.model.Dependency; import org.apache.maven.model.Model; import org.apache.maven.model.Parent; -import org.codehaus.plexus.util.FileUtils; -import org.codehaus.plexus.util.io.RawInputStreamFacade; import org.codehaus.plexus.util.xml.pull.XmlPullParserException; import org.jenkins.tools.test.exception.PluginSourcesUnavailableException; import org.jenkins.tools.test.exception.PomExecutionException; @@ -172,8 +171,11 @@ public PluginCompatReport testPlugins() // Providing XSL Stylesheet along xml report file if(config.reportFile != null){ if(config.isProvideXslReport()){ + Files.createDirectories(Paths.get(PluginCompatReport.getBaseFilepath(config.reportFile))); File xslFilePath = PluginCompatReport.getXslFilepath(config.reportFile); - FileUtils.copyStreamToFile(new RawInputStreamFacade(getXslTransformerResource().getInputStream()), xslFilePath); + try (InputStream is = getXslTransformerResource().getInputStream()) { + Files.copy(is, xslFilePath.toPath(), StandardCopyOption.REPLACE_EXISTING); + } } } @@ -204,7 +206,7 @@ public PluginCompatReport testPlugins() } } - final Map pluginsToCheck; + final Map pluginsToCheck; final List pluginsToInclude = config.getIncludePlugins(); if (data.plugins.isEmpty() && pluginsToInclude != null && !pluginsToInclude.isEmpty()) { // Update Center returns empty info OR the "-war" option is specified for WAR without bundled plugins @@ -231,7 +233,7 @@ public PluginCompatReport testPlugins() if (onlyOnePluginIncluded() && localCheckoutProvided() && !pluginsToCheck.containsKey(config.getIncludePlugins().get(0))) { String artifactId = config.getIncludePlugins().get(0); try { - Plugin extracted = extractFromLocalCheckout(); + UpdateSite.Plugin extracted = extractFromLocalCheckout(); pluginsToCheck.put(artifactId, extracted); } catch (PluginSourcesUnavailableException e) { LOGGER.log(Level.SEVERE, "Cannot test {0} because plugin sources are not available despite a local checkout being provided", artifactId); @@ -251,7 +253,7 @@ public PluginCompatReport testPlugins() boolean failed = false; ROOT_CYCLE: for(MavenCoordinates coreCoordinates : testedCores){ LOGGER.log(Level.INFO, "Starting plugin tests on core coordinates {0}", coreCoordinates); - for (Plugin plugin : pluginsToCheck.values()) { + for (UpdateSite.Plugin plugin : pluginsToCheck.values()) { if(config.getIncludePlugins()==null || config.getIncludePlugins().contains(plugin.name.toLowerCase())){ PluginInfos pluginInfos = new PluginInfos(plugin.name, plugin.version, plugin.url); @@ -363,7 +365,7 @@ && new VersionNumber(coreCoordinates.version).compareTo(new VersionNumber("1.485 if(config.reportFile != null){ if(!config.reportFile.exists()){ - FileUtils.fileWrite(config.reportFile.getAbsolutePath(), ""); + FileUtils.touch(config.reportFile); } report.save(config.reportFile); } @@ -388,20 +390,20 @@ && new VersionNumber(coreCoordinates.version).compareTo(new VersionNumber("1.485 } if (failed && config.isFailOnError()) { - throw new AbortException("Execution was aborted due to the failure in a plugin test (-failOnError is set)"); + throw new RuntimeException("Execution was aborted due to the failure in a plugin test (-failOnError is set)"); } return report; } - private Plugin extractFromLocalCheckout() throws PluginSourcesUnavailableException { + private UpdateSite.Plugin extractFromLocalCheckout() throws PluginSourcesUnavailableException { PomData data = new PluginRemoting(new File(config.getLocalCheckoutDir(), "pom.xml")).retrievePomData(); JSONObject o = new JSONObject(); o.put("name", data.artifactId); o.put("version", ""); // version is not required o.put("url", data.getConnectionUrl()); o.put("dependencies", new JSONArray()); - return new UpdateSite(DEFAULT_SOURCE_ID, null).new Plugin(DEFAULT_SOURCE_ID, o); + return new UpdateSite.Plugin(DEFAULT_SOURCE_ID, o); } protected void generateHtmlReportFile() throws IOException { @@ -438,7 +440,7 @@ private static String createBuildLogFilePathFor(String pluginName, String plugin return String.format("logs/%s/v%s_against_%s_%s_%s.log", pluginName, pluginVersion, coreCoords.groupId, coreCoords.artifactId, coreCoords.version); } - private TestExecutionResult testPluginAgainst(MavenCoordinates coreCoordinates, Plugin plugin, MavenRunner.Config mconfig, PomData pomData, Map otherPlugins, Map pluginGroupIds, PluginCompatTesterHooks pcth, List overridenPlugins) + private TestExecutionResult testPluginAgainst(MavenCoordinates coreCoordinates, UpdateSite.Plugin plugin, MavenRunner.Config mconfig, PomData pomData, Map otherPlugins, Map pluginGroupIds, PluginCompatTesterHooks pcth, List overridenPlugins) throws PluginSourcesUnavailableException, PomExecutionException, IOException, PomTransformationException { LOGGER.log(Level.INFO, "\n\n\n\n\n\n#############################################\n#############################################\n##\n## Starting to test {0} {1} against {2}\n##\n#############################################\n#############################################\n\n\n\n\n", new Object[]{plugin.name, plugin.version, coreCoordinates}); @@ -475,7 +477,7 @@ private TestExecutionResult testPluginAgainst(MavenCoordinates coreCoordinates, File pomLocalCheckoutPluginDir = new File(localCheckoutPluginDir, "pom.xml"); if(pomLocalCheckoutPluginDir.exists()) { LOGGER.log(Level.INFO, "Copying plugin directory from {0}", localCheckoutPluginDir.getAbsolutePath()); - FileUtils.copyDirectoryStructure(localCheckoutPluginDir, pluginCheckoutDir); + org.codehaus.plexus.util.FileUtils.copyDirectoryStructure(localCheckoutPluginDir, pluginCheckoutDir); } else { cloneFromSCM(pomData, plugin.name, plugin.version, pluginCheckoutDir, ""); } @@ -484,7 +486,7 @@ private TestExecutionResult testPluginAgainst(MavenCoordinates coreCoordinates, // and even up-to-date versions of org.apache.commons.io.FileUtils seem to not handle links, // so may need to use something like http://docs.oracle.com/javase/tutorial/displayCode.html?code=http://docs.oracle.com/javase/tutorial/essential/io/examples/Copy.java LOGGER.log(Level.INFO, "Copy plugin directory from {0}", config.getLocalCheckoutDir().getAbsolutePath()); - FileUtils.copyDirectoryStructure(config.getLocalCheckoutDir(), pluginCheckoutDir); + org.codehaus.plexus.util.FileUtils.copyDirectoryStructure(config.getLocalCheckoutDir(), pluginCheckoutDir); } } else { // These hooks could redirect the SCM, skip checkout (if multiple plugins use the same preloaded repo) @@ -507,7 +509,7 @@ private TestExecutionResult testPluginAgainst(MavenCoordinates coreCoordinates, File buildLogFile = createBuildLogFile(config.reportFile, plugin.name, plugin.version, coreCoordinates); FileUtils.forceMkdir(buildLogFile.getParentFile()); // Creating log directory - FileUtils.fileWrite(buildLogFile.getAbsolutePath(), ""); // Creating log file + FileUtils.touch(buildLogFile); // Creating log file // Ran the BeforeCompileHooks Map beforeCompile = new HashMap<>(); @@ -549,7 +551,9 @@ private TestExecutionResult testPluginAgainst(MavenCoordinates coreCoordinates, } catch (Exception x) { LOGGER.log(Level.WARNING, "Failed to transform POM; continuing", x); - pomData.getWarningMessages().add(Functions.printThrowable(x)); + StringWriter sw = new StringWriter(); + x.printStackTrace(new PrintWriter(sw)); + pomData.getWarningMessages().add(sw.toString()); // but continue } @@ -663,7 +667,7 @@ public static void clone(String connectionURL, String scmTag, File checkoutDirec * git checkout FETCH_HEAD */ if (checkoutDirectory.isDirectory()) { - org.apache.commons.io.FileUtils.deleteDirectory(checkoutDirectory); + FileUtils.deleteDirectory(checkoutDirectory); } Files.createDirectories(checkoutDirectory.toPath()); @@ -784,10 +788,9 @@ private UpdateSite.Data extractUpdateCenterData(Map groupIDs){ } String json = jsonp.substring(jsonp.indexOf('(')+1,jsonp.lastIndexOf(')')); - UpdateSite us = new UpdateSite(DEFAULT_SOURCE_ID, url.toExternalForm()); JSONObject jsonObj = JSONObject.fromObject(json); - UpdateSite.Data site = newUpdateSiteData(us, jsonObj); + UpdateSite.Data site = new UpdateSite.Data(jsonObj); // UpdateSite.Plugin does not contain gav object, so we process the JSON object on our own here for(Map.Entry e : (Set>)jsonObj.getJSONObject("plugins").entrySet()) { @@ -870,7 +873,7 @@ private UpdateSite.Data scanBom(HashMap pluginGroupIds, String p top.put("core", new JSONObject().accumulate("name", "core").accumulate("version",core).accumulate("url", "https://foobar")); } LOGGER.log(Level.INFO, "Read contents of {0}: {1}", new Object[]{config.getBom(), top}); - return newUpdateSiteData(new UpdateSite(DEFAULT_SOURCE_ID, null), top); + return new UpdateSite.Data(top); } private List getBomEntries() throws IOException, XmlPullParserException, PomExecutionException { @@ -881,10 +884,10 @@ private List getBomEntries() throws IOException, XmlPullParserException, P File buildLogFile = new File(config.workDirectory + File.separator + "bom-download.log"); - FileUtils.fileWrite(buildLogFile.getAbsolutePath(), ""); // Creating log file + FileUtils.touch(buildLogFile); // Creating log file runner.run(mconfig, fullDepPom.getParentFile(), buildLogFile, "dependency:copy-dependencies", "-P consume-incrementals", "-N"); - return FileUtils.getFiles(new File(config.workDirectory, "bom" + File.separator + "target" + File.separator + "dependency"),null, null); + return org.codehaus.plexus.util.FileUtils.getFiles(new File(config.workDirectory, "bom" + File.separator + "target" + File.separator + "dependency"),null, null); } /** @@ -994,7 +997,7 @@ private UpdateSite.Data scanWAR(File war, @NonNull Map pluginGro throw new IOException("no jenkins-core.jar in " + war); } LOGGER.log(Level.INFO, "Scanned contents of {0} with {1} plugins", new Object[]{war, plugins.size()}); - return newUpdateSiteData(new UpdateSite(DEFAULT_SOURCE_ID, null), top); + return new UpdateSite.Data(top); } private SortedSet coreVersionFromWAR(UpdateSite.Data data) { @@ -1003,16 +1006,6 @@ private SortedSet coreVersionFromWAR(UpdateSite.Data data) { return result; } - private UpdateSite.Data newUpdateSiteData(UpdateSite us, JSONObject jsonO) throws RuntimeException { - try { - Constructor dataConstructor = UpdateSite.Data.class.getDeclaredConstructor(UpdateSite.class, JSONObject.class); - dataConstructor.setAccessible(true); - return dataConstructor.newInstance(us, jsonO); - }catch(Exception e){ - throw new RuntimeException("UpdateSite.Data instantiation problems", e); - } - } - /** * Provides the Maven module used for a plugin on a {@code mvn [...] -pl} operation in the parent path */ @@ -1040,7 +1033,7 @@ public static String getMavenModule(String plugin, File pluginPath, MavenRunner return null; } - private void addSplitPluginDependencies(String thisPlugin, MavenRunner.Config mconfig, File pluginCheckoutDir, MavenPom pom, Map otherPlugins, Map pluginGroupIds, String coreVersion, List overridenPlugins, String parentFolder) throws Exception { + private void addSplitPluginDependencies(String thisPlugin, MavenRunner.Config mconfig, File pluginCheckoutDir, MavenPom pom, Map otherPlugins, Map pluginGroupIds, String coreVersion, List overridenPlugins, String parentFolder) throws Exception { File tmp = File.createTempFile("dependencies", ".log"); VersionNumber coreDep = null; Map pluginDeps = new HashMap<>(); @@ -1145,7 +1138,7 @@ private void addSplitPluginDependencies(String thisPlugin, MavenRunner.Config mc VersionNumber splitPoint = new VersionNumber(pieces[1]); VersionNumber declaredMinimum = new VersionNumber(pieces[2]); if (coreDep.compareTo(splitPoint) < 0 && new VersionNumber(coreVersion).compareTo(splitPoint) >=0 && !pluginDeps.containsKey(plugin)) { - Plugin bundledP = otherPlugins.get(plugin); + UpdateSite.Plugin bundledP = otherPlugins.get(plugin); if (bundledP != null) { VersionNumber bundledV; try { @@ -1187,13 +1180,13 @@ private void addSplitPluginDependencies(String thisPlugin, MavenRunner.Config mc throw new PomTransformationException("No jenkins core dependency found, aborting!", new Throwable()); } } - private void checkDefinedDeps(Map pluginList, Map adding, Map replacing, Map otherPlugins) { + private void checkDefinedDeps(Map pluginList, Map adding, Map replacing, Map otherPlugins) { checkDefinedDeps(pluginList, adding, replacing, otherPlugins, new ArrayList<>(), null); } - private void checkDefinedDeps(Map pluginList, Map adding, Map replacing, Map otherPlugins, List inTest, List toConvertFromTest) { + private void checkDefinedDeps(Map pluginList, Map adding, Map replacing, Map otherPlugins, List inTest, List toConvertFromTest) { for (Map.Entry pluginDep : pluginList.entrySet()) { String plugin = pluginDep.getKey(); - Plugin bundledP = otherPlugins.get(plugin); + UpdateSite.Plugin bundledP = otherPlugins.get(plugin); if (bundledP != null) { VersionNumber bundledV = new VersionNumber(bundledP.version); if (bundledV.isNewerThan(pluginDep.getValue())) { @@ -1207,7 +1200,7 @@ private void checkDefinedDeps(Map pluginList, Map pluginList, Map pluginList, Map adding, Map replacing, Map otherPlugins, List inTest, List toConvertFromTest) { + private void updateAllDependents(String parent, UpdateSite.Plugin dependent, Map pluginList, Map adding, Map replacing, Map otherPlugins, List inTest, List toConvertFromTest) { // Check if this exists with an undesired scope String pluginName = dependent.name; if (inTest.contains(pluginName)) { @@ -1241,7 +1234,7 @@ private void updateAllDependents(String parent, Plugin dependent, Map