From 4213391a570853b450afe41deb345f478bb8a4e4 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 20 Mar 2019 16:26:03 -0400 Subject: [PATCH 01/19] [JENKINS-56643] Util.createSymlink: prefer Files.move(..., StandardCopyOption.ATOMIC_MOVE) Atomic moves mean that there is no time point during which the symlink will not exist. Otherwise, if there is a user trying to access the symlink and there is a process trying to update the symlink, there will be a window during which there is no symlink. When that happens, builds can randomly fail. Implementation: Introduce a private createSymlinkAtomic which will atomically create a symlink in the right place and return true, or return false leaving no changes to the file system. It does not throw exceptions. When it fails, the previous code is used instead. Changes to the existing code are designed to slightly clarify what objects are and reuse objects instead of creating them multiple times. --- core/src/main/java/hudson/Util.java | 73 +++++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/hudson/Util.java b/core/src/main/java/hudson/Util.java index dc3de475bf..e5b4c00c7d 100644 --- a/core/src/main/java/hudson/Util.java +++ b/core/src/main/java/hudson/Util.java @@ -85,6 +85,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.annotation.CheckForNull; +import javax.annotation.CheckReturnValue; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.crypto.SecretKey; @@ -1168,6 +1169,65 @@ public class Util { return createFileSet(baseDir,includes,null); } + private static void tryToDeleteSymlink(@Nonnull File symlink) { + if (!symlink.delete()) { + LogRecord record = new LogRecord(Level.FINE, "Failed to delete temporary symlink {0}"); + record.setParameters(new Object[]{symlink.getAbsolutePath()}); + LOGGER.log(record); + } + } + + private static void reportAtomicFailure(@Nonnull Path pathForSymlink, @Nonnull Exception ex) { + LogRecord record = new LogRecord(Level.FINE, "Failed to atomically create/replace symlink {0}"); + record.setParameters(new Object[]{pathForSymlink.toAbsolutePath().toString()}); + record.setThrown(ex); + LOGGER.log(record); + } + + /** + * Creates a symlink to targetPath at baseDir+symlinkPath. + * + * @param pathForSymlink + * The absolute path of the symlink itself as a path object. + * @param fileForSymlink + * The absolute path of the symlink itself as a file object. + * @param target + * The path that the symlink should point to. Usually relative to the directory of the symlink but may instead be an absolute path. + * @param symlinkPath + * Where to create a symlink in (relative to {@code baseDir}) + * + * Returns true on success + */ + @CheckReturnValue + private static boolean createSymlinkAtomic(@Nonnull Path pathForSymlink, @Nonnull File fileForSymlink, @Nonnull Path target, @Nonnull String symlinkPath) { + try { + File symlink = File.createTempFile("symtmp", null, fileForSymlink); + tryToDeleteSymlink(symlink); + Path tempSymlinkPath = symlink.toPath(); + Files.createSymbolicLink(tempSymlinkPath, target); + try { + Files.move(tempSymlinkPath, pathForSymlink, java.nio.file.StandardCopyOption.ATOMIC_MOVE); + return true; + } catch ( + UnsupportedOperationException | + SecurityException | + IOException ex) { + // If we couldn't perform an atomic move or the setup, we fall through to another approach + reportAtomicFailure(pathForSymlink, ex); + } + // If we didn't return after our atomic move, then we want to clean up our symlink + tryToDeleteSymlink(symlink); + } catch ( + SecurityException | + InvalidPathException | + UnsupportedOperationException | + IOException ex) { + // We couldn't perform an atomic move or the setup. + reportAtomicFailure(pathForSymlink, ex); + } + return false; + } + /** * Creates a symlink to targetPath at baseDir+symlinkPath. *

@@ -1182,16 +1242,21 @@ public class Util { */ public static void createSymlink(@Nonnull File baseDir, @Nonnull String targetPath, @Nonnull String symlinkPath, @Nonnull TaskListener listener) throws InterruptedException { + File fileForSymlink = new File(baseDir, symlinkPath); try { - Path path = fileToPath(new File(baseDir, symlinkPath)); + Path pathForSymlink = fileToPath(fileForSymlink); Path target = Paths.get(targetPath, MemoryReductionUtil.EMPTY_STRING_ARRAY); + if (createSymlinkAtomic(pathForSymlink, fileForSymlink, target, symlinkPath)) { + return; + } + final int maxNumberOfTries = 4; final int timeInMillis = 100; for (int tryNumber = 1; tryNumber <= maxNumberOfTries; tryNumber++) { - Files.deleteIfExists(path); + Files.deleteIfExists(pathForSymlink); try { - Files.createSymbolicLink(path, target); + Files.createSymbolicLink(pathForSymlink, target); break; } catch (FileAlreadyExistsException fileAlreadyExistsException) { if (tryNumber < maxNumberOfTries) { @@ -1212,7 +1277,7 @@ public class Util { return; } PrintStream log = listener.getLogger(); - log.printf("ln %s %s failed%n",targetPath, new File(baseDir, symlinkPath)); + log.printf("ln %s %s failed%n", targetPath, fileForSymlink); Functions.printStackTrace(e, log); } } From 5d0cc4fb25cc398556376fd6b0f3a34c0df30e34 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Tue, 14 May 2019 15:05:35 -0400 Subject: [PATCH 02/19] IntelliJ/Java: Duplicate code -- Refactored as AntClassLoader.storeAndConvert(...Convert) --- .../java/jenkins/util/AntClassLoader.java | 54 +++++++++---------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/core/src/main/java/jenkins/util/AntClassLoader.java b/core/src/main/java/jenkins/util/AntClassLoader.java index 3362c2f543..25a8d3ac4d 100644 --- a/core/src/main/java/jenkins/util/AntClassLoader.java +++ b/core/src/main/java/jenkins/util/AntClassLoader.java @@ -33,6 +33,7 @@ import org.apache.tools.ant.util.VectorSet; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; +import javax.annotation.Nonnull; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; @@ -772,6 +773,28 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { return parent == null ? super.getResourceAsStream(name) : parent.getResourceAsStream(name); } + private interface Convert { + T convert(@Nonnull JarFile jarFile, @Nonnull JarEntry entry) throws IOException; + } + + private T storeAndConvert(JarFile jarFile, File file, String resourceName, Convert convert) throws IOException { + if (jarFile == null) { + if (file.exists()) { + jarFile = new JarFile(file); + jarFiles.put(file, jarFile); + } else { + return null; + } + //to eliminate a race condition, retrieve the entry + //that is in the hash table under that filename + jarFile = (JarFile) jarFiles.get(file); + } + JarEntry entry = jarFile.getJarEntry(resourceName); + if (entry == null) + return null; + return convert.convert(jarFile, entry); + } + /** * Returns an inputstream to a given resource in the given file which may * either be a directory or a zip file. @@ -793,21 +816,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { return Files.newInputStream(resource.toPath()); } } else { - if (jarFile == null) { - if (file.exists()) { - jarFile = new JarFile(file); - jarFiles.put(file, jarFile); - } else { - return null; - } - //to eliminate a race condition, retrieve the entry - //that is in the hash table under that filename - jarFile = (JarFile) jarFiles.get(file); - } - JarEntry entry = jarFile.getJarEntry(resourceName); - if (entry != null) { - return jarFile.getInputStream(entry); - } + return (InputStream) storeAndConvert(jarFile, file, resourceName, (jar, entry) -> jar.getInputStream(entry)); } } catch (Exception e) { log("Ignoring Exception " + e.getClass().getName() + ": " + e.getMessage() @@ -1012,24 +1021,13 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { } } } else { - if (jarFile == null) { - if (file.exists()) { - jarFile = new JarFile(file); - jarFiles.put(file, jarFile); - } else { - return null; - } - // potential race-condition - jarFile = (JarFile) jarFiles.get(file); - } - JarEntry entry = jarFile.getJarEntry(resourceName); - if (entry != null) { + return (URL) storeAndConvert(jarFile, file, resourceName, (jar, entry) -> { try { return new URL("jar:" + FILE_UTILS.getFileURL(file) + "!/" + entry); } catch (MalformedURLException ex) { return null; } - } + }); } } catch (Exception e) { String msg = "Unable to obtain resource from " + file + ": "; From 6485ce053cb6d967fc9347846cb9af3aa1f2ab5e Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 10 Jul 2019 09:44:33 -0400 Subject: [PATCH 03/19] Apply suggestions from code review Co-Authored-By: Oleg Nenashev --- core/src/main/java/jenkins/util/AntClassLoader.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/jenkins/util/AntClassLoader.java b/core/src/main/java/jenkins/util/AntClassLoader.java index 25a8d3ac4d..894d890f39 100644 --- a/core/src/main/java/jenkins/util/AntClassLoader.java +++ b/core/src/main/java/jenkins/util/AntClassLoader.java @@ -816,7 +816,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { return Files.newInputStream(resource.toPath()); } } else { - return (InputStream) storeAndConvert(jarFile, file, resourceName, (jar, entry) -> jar.getInputStream(entry)); + return storeAndConvert(jarFile, file, resourceName, (jar, entry) -> jar.getInputStream(entry)); } } catch (Exception e) { log("Ignoring Exception " + e.getClass().getName() + ": " + e.getMessage() @@ -1021,7 +1021,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { } } } else { - return (URL) storeAndConvert(jarFile, file, resourceName, (jar, entry) -> { + return storeAndConvert(jarFile, file, resourceName, (jar, entry) -> { try { return new URL("jar:" + FILE_UTILS.getFileURL(file) + "!/" + entry); } catch (MalformedURLException ex) { From de8bd30ddcf44d7c241045d3ac940e40d6734af0 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 17 Jul 2019 19:25:49 -0400 Subject: [PATCH 04/19] undo oleg-nenashev's changes per alecharp Co-Authored-By: Adrien Lecharpentier --- core/src/main/java/jenkins/util/AntClassLoader.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/jenkins/util/AntClassLoader.java b/core/src/main/java/jenkins/util/AntClassLoader.java index 894d890f39..003858fb46 100644 --- a/core/src/main/java/jenkins/util/AntClassLoader.java +++ b/core/src/main/java/jenkins/util/AntClassLoader.java @@ -816,7 +816,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { return Files.newInputStream(resource.toPath()); } } else { - return storeAndConvert(jarFile, file, resourceName, (jar, entry) -> jar.getInputStream(entry)); + return storeAndConvert(jarFile, file, resourceName, (jar, entry) -> jar.getInputStream(entry)); } } catch (Exception e) { log("Ignoring Exception " + e.getClass().getName() + ": " + e.getMessage() @@ -1021,7 +1021,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { } } } else { - return storeAndConvert(jarFile, file, resourceName, (jar, entry) -> { + return storeAndConvert(jarFile, file, resourceName, (jar, entry) -> { try { return new URL("jar:" + FILE_UTILS.getFileURL(file) + "!/" + entry); } catch (MalformedURLException ex) { From 734aee5c214f3e7226070afce0c97181d5569270 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 17 Jul 2019 19:29:02 -0400 Subject: [PATCH 05/19] use JarFile::getInputStream per alecharp --- core/src/main/java/jenkins/util/AntClassLoader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/jenkins/util/AntClassLoader.java b/core/src/main/java/jenkins/util/AntClassLoader.java index 003858fb46..2dd61a98dc 100644 --- a/core/src/main/java/jenkins/util/AntClassLoader.java +++ b/core/src/main/java/jenkins/util/AntClassLoader.java @@ -816,7 +816,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { return Files.newInputStream(resource.toPath()); } } else { - return storeAndConvert(jarFile, file, resourceName, (jar, entry) -> jar.getInputStream(entry)); + return storeAndConvert(jarFile, file, resourceName, JarFile::getInputStream); } } catch (Exception e) { log("Ignoring Exception " + e.getClass().getName() + ": " + e.getMessage() From 62dde614a930548c2458fb288701c4b92681e514 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Wed, 17 Jul 2019 19:29:28 -0400 Subject: [PATCH 06/19] Rename interface to Converter and mark it as @FunctionalInterface --- core/src/main/java/jenkins/util/AntClassLoader.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/jenkins/util/AntClassLoader.java b/core/src/main/java/jenkins/util/AntClassLoader.java index 2dd61a98dc..8d693218f4 100644 --- a/core/src/main/java/jenkins/util/AntClassLoader.java +++ b/core/src/main/java/jenkins/util/AntClassLoader.java @@ -773,11 +773,12 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { return parent == null ? super.getResourceAsStream(name) : parent.getResourceAsStream(name); } - private interface Convert { + @FunctionalInterface + private interface Converter { T convert(@Nonnull JarFile jarFile, @Nonnull JarEntry entry) throws IOException; } - private T storeAndConvert(JarFile jarFile, File file, String resourceName, Convert convert) throws IOException { + private T storeAndConvert(JarFile jarFile, File file, String resourceName, Converter converter) throws IOException { if (jarFile == null) { if (file.exists()) { jarFile = new JarFile(file); @@ -792,7 +793,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { JarEntry entry = jarFile.getJarEntry(resourceName); if (entry == null) return null; - return convert.convert(jarFile, entry); + return converter.convert(jarFile, entry); } /** From 7897ee0fe271aebd4b948db5b60488a038ff3725 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Fri, 27 Dec 2019 13:06:07 -0500 Subject: [PATCH 07/19] Add @CheckForNull annotation Co-Authored-By: Oleg Nenashev --- core/src/main/java/jenkins/util/AntClassLoader.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/jenkins/util/AntClassLoader.java b/core/src/main/java/jenkins/util/AntClassLoader.java index 8d693218f4..86dcba3bf9 100644 --- a/core/src/main/java/jenkins/util/AntClassLoader.java +++ b/core/src/main/java/jenkins/util/AntClassLoader.java @@ -778,6 +778,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { T convert(@Nonnull JarFile jarFile, @Nonnull JarEntry entry) throws IOException; } + @CheckForNull private T storeAndConvert(JarFile jarFile, File file, String resourceName, Converter converter) throws IOException { if (jarFile == null) { if (file.exists()) { From f8f5b25a68f0999c45de865f30dabcbea5781295 Mon Sep 17 00:00:00 2001 From: Raihaan Shouhell Date: Wed, 19 Feb 2020 15:43:26 +0800 Subject: [PATCH 08/19] Static nested classes --- core/src/main/java/hudson/FilePath.java | 2 +- core/src/main/java/hudson/TcpSlaveAgentListener.java | 2 +- core/src/main/java/hudson/cli/CLIAction.java | 2 +- core/src/main/java/hudson/diagnosis/MemoryUsageMonitor.java | 2 +- core/src/main/java/hudson/fsp/WorkspaceSnapshotSCM.java | 2 +- core/src/main/java/hudson/model/Queue.java | 2 +- core/src/main/java/hudson/tasks/BuildWrapper.java | 2 +- core/src/main/java/hudson/tasks/LogRotator.java | 2 +- .../main/java/hudson/tools/DownloadFromUrlInstaller.java | 2 +- .../main/java/hudson/triggers/SlowTriggerAdminMonitor.java | 2 +- core/src/main/java/hudson/util/XStream2.java | 2 +- core/src/main/java/jenkins/FilePathFilterAggregator.java | 2 +- core/src/test/java/hudson/BulkChangeTest.java | 2 +- core/src/test/java/hudson/model/AbstractItemTest.java | 4 ++-- .../hudson/model/EnvironmentContributingActionTest.java | 6 +++--- .../java/hudson/model/FingerprintCleanupThreadTest.java | 5 ++--- core/src/test/java/hudson/model/LoadStatisticsTest.java | 2 +- core/src/test/java/hudson/util/CyclicGraphDetectorTest.java | 4 ++-- core/src/test/java/hudson/util/IsOverriddenTest.java | 2 +- core/src/test/java/hudson/util/io/TarArchiverTest.java | 2 +- .../test/java/jenkins/util/MarkFindingOutputStreamTest.java | 2 +- test/src/test/java/hudson/ExtensionListListenerTest.java | 2 +- test/src/test/java/hudson/model/ApiSEC1704Test.java | 2 +- .../java/hudson/model/UpdateCenterConnectionStatusTest.java | 2 +- test/src/test/java/hudson/security/SecurityRealmTest.java | 2 +- test/src/test/java/hudson/security/WhoAmITest.java | 2 +- .../test/java/hudson/tools/ZipExtractionInstallerTest.java | 2 +- .../java/jenkins/security/stapler/DoActionFilterTest.java | 2 +- 28 files changed, 33 insertions(+), 34 deletions(-) diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index 01a8d3cc0e..60d68e6306 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -3275,7 +3275,7 @@ public final class FilePath implements SerializableOnlyOverRemoting { return act(new IsDescendant(potentialChildRelativePath)); } - private class IsDescendant extends SecureFileCallable { + private static class IsDescendant extends SecureFileCallable { private static final long serialVersionUID = 1L; private String potentialChildRelativePath; diff --git a/core/src/main/java/hudson/TcpSlaveAgentListener.java b/core/src/main/java/hudson/TcpSlaveAgentListener.java index 39ba388d2d..dfe9f4ce20 100644 --- a/core/src/main/java/hudson/TcpSlaveAgentListener.java +++ b/core/src/main/java/hudson/TcpSlaveAgentListener.java @@ -360,7 +360,7 @@ public final class TcpSlaveAgentListener extends Thread { } // This is essentially just to be able to pass the parent thread into the callback, as it can't access it otherwise - private abstract class ConnectionHandlerFailureCallback { + private static abstract class ConnectionHandlerFailureCallback { private Thread parentThread; public ConnectionHandlerFailureCallback(Thread parentThread) { diff --git a/core/src/main/java/hudson/cli/CLIAction.java b/core/src/main/java/hudson/cli/CLIAction.java index 102dbbc00b..efca422c44 100644 --- a/core/src/main/java/hudson/cli/CLIAction.java +++ b/core/src/main/java/hudson/cli/CLIAction.java @@ -190,7 +190,7 @@ public class CLIAction implements UnprotectedRootAction, StaplerProxy { } } - class ServerSideImpl extends PlainCLIProtocol.ServerSide { + static class ServerSideImpl extends PlainCLIProtocol.ServerSide { private Thread runningThread; private boolean ready; private final List args = new ArrayList<>(); diff --git a/core/src/main/java/hudson/diagnosis/MemoryUsageMonitor.java b/core/src/main/java/hudson/diagnosis/MemoryUsageMonitor.java index eb0c10584a..b46eb9ec28 100644 --- a/core/src/main/java/hudson/diagnosis/MemoryUsageMonitor.java +++ b/core/src/main/java/hudson/diagnosis/MemoryUsageMonitor.java @@ -53,7 +53,7 @@ public final class MemoryUsageMonitor extends PeriodicWork { /** * A memory group is conceptually a set of memory pools. */ - public final class MemoryGroup { + public static final class MemoryGroup { private final List pools = new ArrayList<>(); /** diff --git a/core/src/main/java/hudson/fsp/WorkspaceSnapshotSCM.java b/core/src/main/java/hudson/fsp/WorkspaceSnapshotSCM.java index e8c19e62a8..426ad5b7e1 100644 --- a/core/src/main/java/hudson/fsp/WorkspaceSnapshotSCM.java +++ b/core/src/main/java/hudson/fsp/WorkspaceSnapshotSCM.java @@ -69,7 +69,7 @@ public class WorkspaceSnapshotSCM extends SCM { /** * {@link Exception} indicating that the resolution of the job/permalink failed. */ - private final class ResolvedFailedException extends Exception { + private static final class ResolvedFailedException extends Exception { private ResolvedFailedException(String message) { super(message); } diff --git a/core/src/main/java/hudson/model/Queue.java b/core/src/main/java/hudson/model/Queue.java index 0ceb35fbce..cbbb41a80b 100644 --- a/core/src/main/java/hudson/model/Queue.java +++ b/core/src/main/java/hudson/model/Queue.java @@ -2419,7 +2419,7 @@ public class Queue extends ResourceController implements Saveable { @Restricted(NoExternalUse.class) @ExportedBean(defaultVisibility = 999) @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD", justification = "it is exported, so it might be used") - public class StubItem { + public static class StubItem { @Exported public StubTask task; diff --git a/core/src/main/java/hudson/tasks/BuildWrapper.java b/core/src/main/java/hudson/tasks/BuildWrapper.java index bce32eae14..4c9ea301c3 100644 --- a/core/src/main/java/hudson/tasks/BuildWrapper.java +++ b/core/src/main/java/hudson/tasks/BuildWrapper.java @@ -76,7 +76,7 @@ public abstract class BuildWrapper extends AbstractDescribableImpl * It is expected that the subclasses of {@link BuildWrapper} extends this * class and implements its own semantics. */ - public abstract class Environment extends hudson.model.Environment { + public static abstract class Environment extends hudson.model.Environment { /** * Runs after the {@link Builder} completes, and performs a tear down. * diff --git a/core/src/main/java/hudson/tasks/LogRotator.java b/core/src/main/java/hudson/tasks/LogRotator.java index b73aea0912..8c44d1ca5a 100644 --- a/core/src/main/java/hudson/tasks/LogRotator.java +++ b/core/src/main/java/hudson/tasks/LogRotator.java @@ -64,7 +64,7 @@ public class LogRotator extends BuildDiscarder { /** @deprecated Replaced by more generic {@link CompositeIOException}. */ @Deprecated - public class CollatedLogRotatorException extends IOException { + public static class CollatedLogRotatorException extends IOException { private static final long serialVersionUID = 5944233808072651101L; public final Collection collated; diff --git a/core/src/main/java/hudson/tools/DownloadFromUrlInstaller.java b/core/src/main/java/hudson/tools/DownloadFromUrlInstaller.java index 6fa7dd8371..adbc0ea500 100644 --- a/core/src/main/java/hudson/tools/DownloadFromUrlInstaller.java +++ b/core/src/main/java/hudson/tools/DownloadFromUrlInstaller.java @@ -248,7 +248,7 @@ public abstract class DownloadFromUrlInstaller extends ToolInstaller { * Convenient abstract class to implement a NodeSpecificInstallable based on an existing Installable * @since 1.626 */ - public abstract class NodeSpecificInstallable extends Installable implements NodeSpecific { + public static abstract class NodeSpecificInstallable extends Installable implements NodeSpecific { public NodeSpecificInstallable(Installable inst) { this.id = inst.id; diff --git a/core/src/main/java/hudson/triggers/SlowTriggerAdminMonitor.java b/core/src/main/java/hudson/triggers/SlowTriggerAdminMonitor.java index 131181c0be..15bcdc9fe2 100644 --- a/core/src/main/java/hudson/triggers/SlowTriggerAdminMonitor.java +++ b/core/src/main/java/hudson/triggers/SlowTriggerAdminMonitor.java @@ -92,7 +92,7 @@ public class SlowTriggerAdminMonitor extends AdministrativeMonitor { return HttpResponses.redirectViaContextPath("/manage"); } - public class Value { + public static class Value { private final LocalDateTime time; private final String msg; diff --git a/core/src/main/java/hudson/util/XStream2.java b/core/src/main/java/hudson/util/XStream2.java index 43f0dfd08c..fb3e4a8431 100644 --- a/core/src/main/java/hudson/util/XStream2.java +++ b/core/src/main/java/hudson/util/XStream2.java @@ -327,7 +327,7 @@ public class XStream2 extends XStream { mapperInjectionPoint.setDelegate(m); } - final class MapperInjectionPoint extends MapperDelegate { + final static class MapperInjectionPoint extends MapperDelegate { public MapperInjectionPoint(Mapper wrapped) { super(wrapped); } diff --git a/core/src/main/java/jenkins/FilePathFilterAggregator.java b/core/src/main/java/jenkins/FilePathFilterAggregator.java index 407d8da2cb..f338d25888 100644 --- a/core/src/main/java/jenkins/FilePathFilterAggregator.java +++ b/core/src/main/java/jenkins/FilePathFilterAggregator.java @@ -20,7 +20,7 @@ import java.util.concurrent.CopyOnWriteArrayList; class FilePathFilterAggregator extends FilePathFilter { private final CopyOnWriteArrayList all = new CopyOnWriteArrayList<>(); - private class Entry implements Comparable { + private static class Entry implements Comparable { final FilePathFilter filter; final double ordinal; diff --git a/core/src/test/java/hudson/BulkChangeTest.java b/core/src/test/java/hudson/BulkChangeTest.java index 7e93dc4e09..e738e9259f 100644 --- a/core/src/test/java/hudson/BulkChangeTest.java +++ b/core/src/test/java/hudson/BulkChangeTest.java @@ -37,7 +37,7 @@ import java.io.IOException; */ public class BulkChangeTest { - private class Point implements Saveable { + private static class Point implements Saveable { /** * Don't actually do any save, but just remember how many the actual I/O would have happened. */ diff --git a/core/src/test/java/hudson/model/AbstractItemTest.java b/core/src/test/java/hudson/model/AbstractItemTest.java index b5dcdf41d2..4ee09dfad0 100644 --- a/core/src/test/java/hudson/model/AbstractItemTest.java +++ b/core/src/test/java/hudson/model/AbstractItemTest.java @@ -16,7 +16,7 @@ import org.jvnet.hudson.test.Issue; @SuppressWarnings("unchecked") public class AbstractItemTest { - private class StubAbstractItem extends AbstractItem { + private static class StubAbstractItem extends AbstractItem { protected StubAbstractItem() { // sending in null as parent as I don't care for my current tests @@ -91,7 +91,7 @@ public class AbstractItemTest { assertEquals(displayName, i.getDisplayName()); } - private class NameNotEditableItem extends AbstractItem { + private static class NameNotEditableItem extends AbstractItem { protected NameNotEditableItem(ItemGroup parent, String name){ super(parent, name); diff --git a/core/src/test/java/hudson/model/EnvironmentContributingActionTest.java b/core/src/test/java/hudson/model/EnvironmentContributingActionTest.java index 8bf6c69b30..aee451908a 100644 --- a/core/src/test/java/hudson/model/EnvironmentContributingActionTest.java +++ b/core/src/test/java/hudson/model/EnvironmentContributingActionTest.java @@ -11,7 +11,7 @@ import static org.mockito.Mockito.when; public class EnvironmentContributingActionTest { - class OverrideRun extends InvisibleAction implements EnvironmentContributingAction { + static class OverrideRun extends InvisibleAction implements EnvironmentContributingAction { private boolean wasCalled = false; @Override @@ -24,7 +24,7 @@ public class EnvironmentContributingActionTest { } } - class OverrideAbstractBuild extends InvisibleAction implements EnvironmentContributingAction { + static class OverrideAbstractBuild extends InvisibleAction implements EnvironmentContributingAction { private boolean wasCalled = false; @Override @@ -38,7 +38,7 @@ public class EnvironmentContributingActionTest { } } - class OverrideBoth extends InvisibleAction implements EnvironmentContributingAction { + static class OverrideBoth extends InvisibleAction implements EnvironmentContributingAction { private boolean wasCalledAbstractBuild = false; private boolean wasCalledRun = false; diff --git a/core/src/test/java/hudson/model/FingerprintCleanupThreadTest.java b/core/src/test/java/hudson/model/FingerprintCleanupThreadTest.java index 61a1a72645..549a50df50 100644 --- a/core/src/test/java/hudson/model/FingerprintCleanupThreadTest.java +++ b/core/src/test/java/hudson/model/FingerprintCleanupThreadTest.java @@ -107,7 +107,7 @@ public class FingerprintCleanupThreadTest { tempDirectory.toFile().deleteOnExit(); } - private class TestTaskListener implements TaskListener { + private static class TestTaskListener implements TaskListener { private ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); private PrintStream logStream = new PrintStream(outputStream); @@ -126,7 +126,6 @@ public class FingerprintCleanupThreadTest { public TestFingerprintCleanupThread(Fingerprint fingerprintToLoad) throws IOException { this.fingerprintToLoad = fingerprintToLoad; - return; } @Override @@ -146,7 +145,7 @@ public class FingerprintCleanupThreadTest { } - private class TestFingerprint extends Fingerprint { + private static class TestFingerprint extends Fingerprint { private boolean isAlive = true; diff --git a/core/src/test/java/hudson/model/LoadStatisticsTest.java b/core/src/test/java/hudson/model/LoadStatisticsTest.java index 246bdb3f1d..d49fad2609 100644 --- a/core/src/test/java/hudson/model/LoadStatisticsTest.java +++ b/core/src/test/java/hudson/model/LoadStatisticsTest.java @@ -101,7 +101,7 @@ public class LoadStatisticsTest { assertThat(LoadStatistics.isModern(LoadStatistics.class), is(false)); } - private class Modern extends LoadStatistics { + private static class Modern extends LoadStatistics { protected Modern(int initialOnlineExecutors, int initialBusyExecutors) { super(initialOnlineExecutors, initialBusyExecutors); diff --git a/core/src/test/java/hudson/util/CyclicGraphDetectorTest.java b/core/src/test/java/hudson/util/CyclicGraphDetectorTest.java index d99640068b..42c68e0e27 100644 --- a/core/src/test/java/hudson/util/CyclicGraphDetectorTest.java +++ b/core/src/test/java/hudson/util/CyclicGraphDetectorTest.java @@ -16,7 +16,7 @@ import java.util.Set; */ public class CyclicGraphDetectorTest { - private class Edge { + private static class Edge { String src,dst; private Edge(String src, String dst) { @@ -25,7 +25,7 @@ public class CyclicGraphDetectorTest { } } - private class Graph extends ArrayList { + private static class Graph extends ArrayList { Graph e(String src, String dst) { add(new Edge(src,dst)); return this; diff --git a/core/src/test/java/hudson/util/IsOverriddenTest.java b/core/src/test/java/hudson/util/IsOverriddenTest.java index 36f45c3de5..0048a69553 100644 --- a/core/src/test/java/hudson/util/IsOverriddenTest.java +++ b/core/src/test/java/hudson/util/IsOverriddenTest.java @@ -62,7 +62,7 @@ public class IsOverriddenTest { Util.isOverridden(Base.class, Intermediate.class, "aPrivateMethod"); } - public abstract class Base { + public static abstract class Base { protected abstract void method(); private void aPrivateMethod() {} public void setX(T t) {} diff --git a/core/src/test/java/hudson/util/io/TarArchiverTest.java b/core/src/test/java/hudson/util/io/TarArchiverTest.java index 8900f83193..0983d9a199 100644 --- a/core/src/test/java/hudson/util/io/TarArchiverTest.java +++ b/core/src/test/java/hudson/util/io/TarArchiverTest.java @@ -136,7 +136,7 @@ public class TarArchiverTest { t1.join(); } - private class GrowingFileRunnable implements Runnable { + private static class GrowingFileRunnable implements Runnable { private boolean finish = false; private Exception ex = null; private File file; diff --git a/core/src/test/java/jenkins/util/MarkFindingOutputStreamTest.java b/core/src/test/java/jenkins/util/MarkFindingOutputStreamTest.java index 3de38b3449..19e18af62b 100644 --- a/core/src/test/java/jenkins/util/MarkFindingOutputStreamTest.java +++ b/core/src/test/java/jenkins/util/MarkFindingOutputStreamTest.java @@ -84,7 +84,7 @@ public class MarkFindingOutputStreamTest { m.write(s.charAt(i)); } - class MarkCountingOutputStream extends MarkFindingOutputStream { + static class MarkCountingOutputStream extends MarkFindingOutputStream { int count = 0; MarkCountingOutputStream(OutputStream base) { diff --git a/test/src/test/java/hudson/ExtensionListListenerTest.java b/test/src/test/java/hudson/ExtensionListListenerTest.java index b3449dbeb9..3a6088e50d 100644 --- a/test/src/test/java/hudson/ExtensionListListenerTest.java +++ b/test/src/test/java/hudson/ExtensionListListenerTest.java @@ -56,7 +56,7 @@ public class ExtensionListListenerTest { Assert.assertEquals(1, listListener.onChangeCallCount); } - private class MyExtensionListListener extends ExtensionListListener { + private static class MyExtensionListListener extends ExtensionListListener { private int onChangeCallCount = 0; @Override public void onChange() { diff --git a/test/src/test/java/hudson/model/ApiSEC1704Test.java b/test/src/test/java/hudson/model/ApiSEC1704Test.java index e0a8bcf4b6..3aca1048b5 100644 --- a/test/src/test/java/hudson/model/ApiSEC1704Test.java +++ b/test/src/test/java/hudson/model/ApiSEC1704Test.java @@ -99,7 +99,7 @@ public class ApiSEC1704Test { } @ExportedBean - class CustomData { + static class CustomData { private String secret; CustomData(String secret){ this.secret = secret; diff --git a/test/src/test/java/hudson/model/UpdateCenterConnectionStatusTest.java b/test/src/test/java/hudson/model/UpdateCenterConnectionStatusTest.java index b06ee59627..50c2eb69a9 100644 --- a/test/src/test/java/hudson/model/UpdateCenterConnectionStatusTest.java +++ b/test/src/test/java/hudson/model/UpdateCenterConnectionStatusTest.java @@ -124,7 +124,7 @@ public class UpdateCenterConnectionStatusTest { Assert.assertEquals(ConnectionStatus.FAILED, job.connectionStates.get(ConnectionStatus.UPDATE_SITE)); } - private class TestConfig extends UpdateCenter.UpdateCenterConfiguration { + private static class TestConfig extends UpdateCenter.UpdateCenterConfiguration { private IOException checkConnectionException; private IOException checkUpdateCenterException; diff --git a/test/src/test/java/hudson/security/SecurityRealmTest.java b/test/src/test/java/hudson/security/SecurityRealmTest.java index 5d4ef3868f..da4f4a05e7 100644 --- a/test/src/test/java/hudson/security/SecurityRealmTest.java +++ b/test/src/test/java/hudson/security/SecurityRealmTest.java @@ -72,7 +72,7 @@ public class SecurityRealmTest { assertThat(response.getResponseHeaderValue("Expires"), is("0")); } - private class DummyCaptcha extends CaptchaSupport { + private static class DummyCaptcha extends CaptchaSupport { @Override public boolean validateCaptcha(String id, String text) { return false; diff --git a/test/src/test/java/hudson/security/WhoAmITest.java b/test/src/test/java/hudson/security/WhoAmITest.java index b59bc98cbc..e74e07ccf2 100644 --- a/test/src/test/java/hudson/security/WhoAmITest.java +++ b/test/src/test/java/hudson/security/WhoAmITest.java @@ -201,7 +201,7 @@ public class WhoAmITest { ))); } - private class SecurityRealmImpl extends AbstractPasswordBasedSecurityRealm { + private static class SecurityRealmImpl extends AbstractPasswordBasedSecurityRealm { @Override protected UserDetails authenticate(String username, String password) throws AuthenticationException { diff --git a/test/src/test/java/hudson/tools/ZipExtractionInstallerTest.java b/test/src/test/java/hudson/tools/ZipExtractionInstallerTest.java index 22a211ddfd..75f41e5d6e 100644 --- a/test/src/test/java/hudson/tools/ZipExtractionInstallerTest.java +++ b/test/src/test/java/hudson/tools/ZipExtractionInstallerTest.java @@ -136,7 +136,7 @@ public class ZipExtractionInstallerTest { assertThat(lastRequest.getResponseText(), containsString(Messages.ZipExtractionInstaller_malformed_url())); } - private class SpyingJavaScriptEngine extends JavaScriptEngine { + private static class SpyingJavaScriptEngine extends JavaScriptEngine { private List storedRequests = new ArrayList<>(); private String urlToMatch; private HttpMethod method; diff --git a/test/src/test/java/jenkins/security/stapler/DoActionFilterTest.java b/test/src/test/java/jenkins/security/stapler/DoActionFilterTest.java index ed5bafdfca..d59bed0d8a 100644 --- a/test/src/test/java/jenkins/security/stapler/DoActionFilterTest.java +++ b/test/src/test/java/jenkins/security/stapler/DoActionFilterTest.java @@ -73,7 +73,7 @@ public class DoActionFilterTest extends StaplerAbstractTest { private TestAccessModifier getPrivate() {return new TestAccessModifier();} - public class TestAccessModifier { + public static class TestAccessModifier { @GET public String doValue() { return "hello"; From 5ba7c464cf31a7649d83763a8c66c0125602c07f Mon Sep 17 00:00:00 2001 From: Raihaan Shouhell Date: Thu, 20 Feb 2020 07:54:15 +0800 Subject: [PATCH 09/19] Revert public classes --- core/src/main/java/hudson/diagnosis/MemoryUsageMonitor.java | 2 +- core/src/main/java/hudson/model/Queue.java | 2 +- core/src/main/java/hudson/tasks/BuildWrapper.java | 2 +- core/src/main/java/hudson/tasks/LogRotator.java | 2 +- core/src/main/java/hudson/tools/DownloadFromUrlInstaller.java | 2 +- core/src/main/java/hudson/triggers/SlowTriggerAdminMonitor.java | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/hudson/diagnosis/MemoryUsageMonitor.java b/core/src/main/java/hudson/diagnosis/MemoryUsageMonitor.java index b46eb9ec28..eb0c10584a 100644 --- a/core/src/main/java/hudson/diagnosis/MemoryUsageMonitor.java +++ b/core/src/main/java/hudson/diagnosis/MemoryUsageMonitor.java @@ -53,7 +53,7 @@ public final class MemoryUsageMonitor extends PeriodicWork { /** * A memory group is conceptually a set of memory pools. */ - public static final class MemoryGroup { + public final class MemoryGroup { private final List pools = new ArrayList<>(); /** diff --git a/core/src/main/java/hudson/model/Queue.java b/core/src/main/java/hudson/model/Queue.java index cbbb41a80b..0ceb35fbce 100644 --- a/core/src/main/java/hudson/model/Queue.java +++ b/core/src/main/java/hudson/model/Queue.java @@ -2419,7 +2419,7 @@ public class Queue extends ResourceController implements Saveable { @Restricted(NoExternalUse.class) @ExportedBean(defaultVisibility = 999) @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD", justification = "it is exported, so it might be used") - public static class StubItem { + public class StubItem { @Exported public StubTask task; diff --git a/core/src/main/java/hudson/tasks/BuildWrapper.java b/core/src/main/java/hudson/tasks/BuildWrapper.java index 4c9ea301c3..bce32eae14 100644 --- a/core/src/main/java/hudson/tasks/BuildWrapper.java +++ b/core/src/main/java/hudson/tasks/BuildWrapper.java @@ -76,7 +76,7 @@ public abstract class BuildWrapper extends AbstractDescribableImpl * It is expected that the subclasses of {@link BuildWrapper} extends this * class and implements its own semantics. */ - public static abstract class Environment extends hudson.model.Environment { + public abstract class Environment extends hudson.model.Environment { /** * Runs after the {@link Builder} completes, and performs a tear down. * diff --git a/core/src/main/java/hudson/tasks/LogRotator.java b/core/src/main/java/hudson/tasks/LogRotator.java index 8c44d1ca5a..b73aea0912 100644 --- a/core/src/main/java/hudson/tasks/LogRotator.java +++ b/core/src/main/java/hudson/tasks/LogRotator.java @@ -64,7 +64,7 @@ public class LogRotator extends BuildDiscarder { /** @deprecated Replaced by more generic {@link CompositeIOException}. */ @Deprecated - public static class CollatedLogRotatorException extends IOException { + public class CollatedLogRotatorException extends IOException { private static final long serialVersionUID = 5944233808072651101L; public final Collection collated; diff --git a/core/src/main/java/hudson/tools/DownloadFromUrlInstaller.java b/core/src/main/java/hudson/tools/DownloadFromUrlInstaller.java index adbc0ea500..6fa7dd8371 100644 --- a/core/src/main/java/hudson/tools/DownloadFromUrlInstaller.java +++ b/core/src/main/java/hudson/tools/DownloadFromUrlInstaller.java @@ -248,7 +248,7 @@ public abstract class DownloadFromUrlInstaller extends ToolInstaller { * Convenient abstract class to implement a NodeSpecificInstallable based on an existing Installable * @since 1.626 */ - public static abstract class NodeSpecificInstallable extends Installable implements NodeSpecific { + public abstract class NodeSpecificInstallable extends Installable implements NodeSpecific { public NodeSpecificInstallable(Installable inst) { this.id = inst.id; diff --git a/core/src/main/java/hudson/triggers/SlowTriggerAdminMonitor.java b/core/src/main/java/hudson/triggers/SlowTriggerAdminMonitor.java index 15bcdc9fe2..131181c0be 100644 --- a/core/src/main/java/hudson/triggers/SlowTriggerAdminMonitor.java +++ b/core/src/main/java/hudson/triggers/SlowTriggerAdminMonitor.java @@ -92,7 +92,7 @@ public class SlowTriggerAdminMonitor extends AdministrativeMonitor { return HttpResponses.redirectViaContextPath("/manage"); } - public static class Value { + public class Value { private final LocalDateTime time; private final String msg; From 330b7f9121871515a31e1d53ae72c4fd6cf94f99 Mon Sep 17 00:00:00 2001 From: NextTurn <45985406+NextTurn@users.noreply.github.com> Date: Sun, 8 Mar 2020 00:00:00 +0800 Subject: [PATCH 10/19] Update .NET version checks to be more correct --- .../lifecycle/WindowsInstallerLink.java | 2 +- .../src/main/java/hudson/util/jna/DotNet.java | 241 +++++++++++++++--- .../java/hudson/util/jna/RegistryKey.java | 10 +- 3 files changed, 207 insertions(+), 46 deletions(-) diff --git a/core/src/main/java/hudson/lifecycle/WindowsInstallerLink.java b/core/src/main/java/hudson/lifecycle/WindowsInstallerLink.java index a8f412539a..e95504206d 100644 --- a/core/src/main/java/hudson/lifecycle/WindowsInstallerLink.java +++ b/core/src/main/java/hudson/lifecycle/WindowsInstallerLink.java @@ -119,7 +119,7 @@ public class WindowsInstallerLink extends ManagementLink { sendError("Installation is already complete",req,rsp); return; } - if(!DotNet.isInstalled(2,0)) { + if(!DotNet.isInstalled(4,0) && !DotNet.isInstalled(2,0)) { sendError(".NET Framework 2.0 or later is required for this feature",req,rsp); return; } diff --git a/core/src/main/java/hudson/util/jna/DotNet.java b/core/src/main/java/hudson/util/jna/DotNet.java index 4d820149ca..d3a453a46c 100644 --- a/core/src/main/java/hudson/util/jna/DotNet.java +++ b/core/src/main/java/hudson/util/jna/DotNet.java @@ -30,8 +30,6 @@ import org.jinterop.winreg.JIPolicyHandle; import org.jinterop.winreg.JIWinRegFactory; import java.net.UnknownHostException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; /** * .NET related code. @@ -39,74 +37,233 @@ import java.util.regex.Pattern; * @author Kohsuke Kawaguchi */ public class DotNet { + private static final String PATH10 = "SOFTWARE\\Microsoft\\.NETFramework\\Policy\\v1.0\\3705"; + private static final String PATH11 = "SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v1.1.4322"; + private static final String PATH20 = "SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v2.0.50727"; + private static final String PATH30 = "SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v3.0\\Setup"; + private static final String PATH35 = "SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v3.5"; + private static final String PATH4 = "SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4\\Full"; + + private static final String VALUE_INSTALL = "Install"; + private static final String VALUE_INSTALL_SUCCESS = "InstallSuccess"; + private static final String VALUE_RELEASE = "Release"; + /** - * Returns true if the .NET framework of the given version (or greater) is installed. + * Returns true if the .NET framework of a compatible version is installed. */ public static boolean isInstalled(int major, int minor) { try { - // see http://support.microsoft.com/?scid=kb;en-us;315291 for the basic algorithm - // observation in my registry shows that the actual key name can be things like "v2.0 SP1" - // or "v2.0.50727", so the regexp is written to accommodate this. - RegistryKey key = RegistryKey.LOCAL_MACHINE.openReadonly("SOFTWARE\\Microsoft\\.NETFramework"); - try { - for( String keyName : key.getSubKeys() ) { - if (matches(keyName, major, minor)) - return true; - } + if (major == 4 && minor >= 5) { + return isV45PlusInstalled(minor); + } else if (major == 4 && minor == 0) { + return isV40Installed(); + } else if (major == 3 && minor == 5) { + return isV35Installed(); + } else if (major == 3 && minor == 0) { + return isV35Installed() || isV30Installed(); + } else if (major == 2 && minor == 0) { + return isV35Installed() || isV30Installed() || isV20Installed(); + } else if (major == 1 && minor == 1) { + return isV11Installed(); + } else if (major == 1 && minor == 0) { + return isV11Installed() || isV10Installed(); + } else { return false; - } finally { - key.dispose(); } } catch (JnaException e) { - if(e.getErrorCode()==2) // thrown when openReadonly fails because the key doesn't exist. + if (e.getErrorCode() == 2) { + // thrown when openReadonly fails because the key doesn't exist. return false; + } throw e; } } + private static boolean isV45PlusInstalled(int minor) { + try (RegistryKey key = RegistryKey.LOCAL_MACHINE.openReadonly(PATH4)) { + return key.getIntValue(VALUE_RELEASE) >= GetV45PlusMinRelease(minor); + } + } + + private static boolean isV40Installed() { + try (RegistryKey key = RegistryKey.LOCAL_MACHINE.openReadonly(PATH4)) { + return key.getIntValue(VALUE_INSTALL) == 1; + } + } + + private static boolean isV35Installed() { + try (RegistryKey key = RegistryKey.LOCAL_MACHINE.openReadonly(PATH35)) { + return key.getIntValue(VALUE_INSTALL) == 1; + } + } + + private static boolean isV30Installed() { + try (RegistryKey key = RegistryKey.LOCAL_MACHINE.openReadonly(PATH30)) { + return key.getIntValue(VALUE_INSTALL_SUCCESS) == 1; + } + } + + private static boolean isV20Installed() { + try (RegistryKey key = RegistryKey.LOCAL_MACHINE.openReadonly(PATH20)) { + return key.getIntValue(VALUE_INSTALL) == 1; + } + } + + private static boolean isV11Installed() { + try (RegistryKey key = RegistryKey.LOCAL_MACHINE.openReadonly(PATH11)) { + return key.getIntValue(VALUE_INSTALL) == 1; + } + } + + private static boolean isV10Installed() { + try (RegistryKey key = RegistryKey.LOCAL_MACHINE.openReadonly(PATH10)) { + return key.getStringValue(VALUE_INSTALL) == "1"; + } + } + /** - * Returns true if the .NET framework of the given version (or grater) is installed - * on a remote machine. + * Returns true if the .NET framework of a compatible version is installed on a remote machine. */ public static boolean isInstalled(int major, int minor, String targetMachine, IJIAuthInfo session) throws JIException, UnknownHostException { - IJIWinReg registry = JIWinRegFactory.getSingleTon().getWinreg(session,targetMachine,true); - JIPolicyHandle hklm=null; - JIPolicyHandle key=null; - + IJIWinReg registry = JIWinRegFactory.getSingleTon().getWinreg(session, targetMachine, true); + JIPolicyHandle hklm = null; try { hklm = registry.winreg_OpenHKLM(); - key = registry.winreg_OpenKey(hklm,"SOFTWARE\\Microsoft\\.NETFramework", IJIWinReg.KEY_READ ); - - for( int i=0; ; i++ ) { - String keyName = registry.winreg_EnumKey(key,i)[0]; - if(matches(keyName,major,minor)) - return true; + if (major == 4 && minor >= 5) { + return isV45PlusInstalled(minor, registry, hklm); + } else if (major == 4 && minor == 0) { + return isV40Installed(registry, hklm); + } else if (major == 3 && minor == 5) { + return isV35Installed(registry, hklm); + } else if (major == 3 && minor == 0) { + return isV35Installed(registry, hklm) || isV30Installed(registry, hklm); + } else if (major == 2 && minor == 0) { + return isV35Installed(registry, hklm) || isV30Installed(registry, hklm) || isV20Installed(registry, hklm); + } else if (major == 1 && minor == 1) { + return isV11Installed(registry, hklm); + } else if (major == 1 && minor == 0) { + return isV11Installed(registry, hklm) || isV10Installed(registry, hklm); + } else { + return false; } } catch (JIException e) { - if(e.getErrorCode()==2) - return false; // not found + if (e.getErrorCode() == 2) { + // not found + return false; + } throw e; } finally { - if(hklm!=null) + if (hklm != null) { registry.winreg_CloseKey(hklm); - if(key!=null) - registry.winreg_CloseKey(key); + } registry.closeConnection(); } } - private static boolean matches(String keyName, int major, int minor) { - Matcher m = VERSION_PATTERN.matcher(keyName); - if(m.matches()) { - int mj = Integer.parseInt(m.group(1)); - if(mj>=major) { - int mn = Integer.parseInt(m.group(2)); - if(mn>=minor) - return true; + private static boolean isV45PlusInstalled(int minor, IJIWinReg registry, JIPolicyHandle hklm) throws JIException { + JIPolicyHandle key = null; + try { + key = registry.winreg_OpenKey(hklm, PATH4, IJIWinReg.KEY_READ); + return GetIntValue(registry, key, VALUE_RELEASE) >= GetV45PlusMinRelease(minor); + } finally { + if (key != null) { + registry.winreg_CloseKey(key); } } - return false; } - private static final Pattern VERSION_PATTERN = Pattern.compile("v(\\d+)\\.(\\d+).*"); + private static boolean isV40Installed(IJIWinReg registry, JIPolicyHandle hklm) throws JIException { + JIPolicyHandle key = null; + try { + key = registry.winreg_OpenKey(hklm, PATH4, IJIWinReg.KEY_READ); + return GetIntValue(registry, key, VALUE_INSTALL) == 1; + } finally { + if (key != null) { + registry.winreg_CloseKey(key); + } + } + } + + private static boolean isV35Installed(IJIWinReg registry, JIPolicyHandle hklm) throws JIException { + JIPolicyHandle key = null; + try { + key = registry.winreg_OpenKey(hklm, PATH35, IJIWinReg.KEY_READ); + return GetIntValue(registry, key, VALUE_INSTALL) == 1; + } finally { + if (key != null) { + registry.winreg_CloseKey(key); + } + } + } + + private static boolean isV30Installed(IJIWinReg registry, JIPolicyHandle hklm) throws JIException { + JIPolicyHandle key = null; + try { + key = registry.winreg_OpenKey(hklm, PATH30, IJIWinReg.KEY_READ); + return GetIntValue(registry, key, VALUE_INSTALL_SUCCESS) == 1; + } finally { + if (key != null) { + registry.winreg_CloseKey(key); + } + } + } + + private static boolean isV20Installed(IJIWinReg registry, JIPolicyHandle hklm) throws JIException { + JIPolicyHandle key = null; + try { + key = registry.winreg_OpenKey(hklm, PATH20, IJIWinReg.KEY_READ); + return GetIntValue(registry, key, VALUE_INSTALL) == 1; + } finally { + if (key != null) { + registry.winreg_CloseKey(key); + } + } + } + + private static boolean isV11Installed(IJIWinReg registry, JIPolicyHandle hklm) throws JIException { + JIPolicyHandle key = null; + try { + key = registry.winreg_OpenKey(hklm, PATH11, IJIWinReg.KEY_READ); + return GetIntValue(registry, key, VALUE_INSTALL) == 1; + } finally { + if (key != null) { + registry.winreg_CloseKey(key); + } + } + } + + private static boolean isV10Installed(IJIWinReg registry, JIPolicyHandle hklm) throws JIException { + JIPolicyHandle key = null; + try { + key = registry.winreg_OpenKey(hklm, PATH10, IJIWinReg.KEY_READ); + return GetStringValue(registry, key, VALUE_INSTALL) == "1"; + } finally { + if (key != null) { + registry.winreg_CloseKey(key); + } + } + } + + private static int GetIntValue(IJIWinReg registry, JIPolicyHandle key, String name) throws JIException { + return RegistryKey.convertBufferToInt((byte[])registry.winreg_QueryValue(key, name, Integer.BYTES)[1]); + } + + private static String GetStringValue(IJIWinReg registry, JIPolicyHandle key, String name) throws JIException { + return RegistryKey.convertBufferToString((byte[])registry.winreg_QueryValue(key, name, Character.BYTES * 2)[1]); + } + + private static int GetV45PlusMinRelease(int minor) { + switch (minor) { + case 5: + return 378389; + case 6: + return 393295; + case 7: + return 460798; + case 8: + return 528040; + default: + return Integer.MAX_VALUE; + } + } } diff --git a/core/src/main/java/hudson/util/jna/RegistryKey.java b/core/src/main/java/hudson/util/jna/RegistryKey.java index 81ed3f06b3..554a1263ec 100644 --- a/core/src/main/java/hudson/util/jna/RegistryKey.java +++ b/core/src/main/java/hudson/util/jna/RegistryKey.java @@ -27,7 +27,7 @@ import java.util.TreeSet; * * @author Kohsuke Kawaguchi */ -public class RegistryKey { +public class RegistryKey implements AutoCloseable { /** * 32bit Windows key value. */ @@ -64,7 +64,7 @@ public class RegistryKey { * @throws java.io.UnsupportedEncodingException on error * @return String */ - private static String convertBufferToString(byte[] buf) { + static String convertBufferToString(byte[] buf) { return new String(buf, 0, buf.length - 2, StandardCharsets.UTF_16LE); } @@ -74,7 +74,7 @@ public class RegistryKey { * @param buf buffer * @return int */ - private static int convertBufferToInt(byte[] buf) { + static int convertBufferToInt(byte[] buf) { return ((buf[0] & 0xff) + ((buf[1] & 0xff) << 8) + ((buf[2] & 0xff) << 16) + ((buf[3] & 0xff) << 24)); } @@ -287,6 +287,10 @@ public class RegistryKey { handle = 0; } + public void close() { + dispose(); + } + // // Root keys // From 75d36b7e89aae168d2c14de81714b1e4424881fc Mon Sep 17 00:00:00 2001 From: zbynek Date: Mon, 16 Mar 2020 01:09:11 +0100 Subject: [PATCH 11/19] Remove unused CSS file --- war/src/main/webapp/css/style.css | 2102 ----------------------------- 1 file changed, 2102 deletions(-) delete mode 100644 war/src/main/webapp/css/style.css diff --git a/war/src/main/webapp/css/style.css b/war/src/main/webapp/css/style.css deleted file mode 100644 index 4afc61c044..0000000000 --- a/war/src/main/webapp/css/style.css +++ /dev/null @@ -1,2102 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi, Daniel Dyer, Stephen Connolly - * - * 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. - */ - -/* task */ - -#tasks { - margin-bottom: 20px; - display: inline-block; -} - -#tasks .task { - margin-bottom: 4px; - font-size: 14px; - max-width: 325px; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; -} - -/* buildQueue */ - -#buildQueue { - margin-bottom: 20px; -} - -/* Fonts etc */ - -body, table, form, input, td, th, p, textarea, select -{ - font-family: Helvetica, Arial, sans-serif; - font-size: 13px; - line-height:1.4em; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -@media (min-width:1600px){ - body#jenkins.j-hide-left #main-panel{max-width:75%} - body, table, form, input, td, th, p, textarea, select, #tasks .task - {font-size:14px;} -} -@media (min-width:2000px){ - body#jenkins.j-hide-left #main-panel{max-width:85%} - body, table, form, input, td, th, p, textarea, select, #tasks .task - {font-size:15px;} -} -body, table, form, td, th, p -{ - color: #333; -} - -form { - margin: 0; -} - -td { - vertical-align: top; -} - -dt { - font-weight: bold; -} - -dt > .plugin_version { - font-weight: normal; -} - -.fixed-width { - font-family: monospace; -} - -.center { - text-align: center; -} - -.middle-align td, td.middle-align { - vertical-align: middle; -} - -.center-align td, td.center-align { - text-align: center; -} - -.no-wrap td, td.no-wrap { - white-space: nowrap; -} - -#main-table { - padding: 0; - border-collapse: collapse; -} - -#top-panel { - margin-bottom: 3pt; - height: 34px; - background: url(../images/topbar.png) repeat-x; -} -#top-panel a { - text-decoration: none; -} - -.skiplink { - position: absolute; - left: -20000px; -} - -#shutdown-msg { - font-weight: bold; - color: white; - background-color: #ef2929; - text-align: center; - margin-left: 2em; - margin-right: 2em; - margin-bottom: 0.5em; - padding: 0.5em; - -moz-border-radius: 0.5em; -} - -#header .login { - position: relative; - top: 6px; - color: white; - margin-right: 10px; -} - -#header .login a, #header .login a:visited { - color: white; - text-decoration: none; -} - -#header .login a:hover { - text-decoration: underline; - color: #ccc; -} - -a:link { - text-decoration: underline; - color: #204A87; -} - -a:visited { - text-decoration: underline; - color: #5c3566; -} - -a.lowkey:link { - text-decoration: none; - color: inherit; -} - -a.lowkey:hover { - text-decoration: underline; - color: inherit; -} - -a.lowkey:visited { - text-decoration: none; - color: inherit; -} - -/* tip - anchors of class info */ -a.tip { - position:relative; - z-index:24; - text-decoration: underline; -} - -a.tip:hover { - z-index:25; -} - -a.tip span { - display: none -} - -a.tip:hover span { - display:block; - position:absolute; - top:2em; - left:2em; - width:400px; - border:1px solid #bbbbbb; - background-color:#fffff0; - color:#000; - text-align: left -} - -.call-to-action { - display: inline-block; - padding: 10px; - background-color: #cdf0f2; - border-left: 4px solid #577baa; -} - -.call-to-action a, .call-to-action a:visited { - color: #000000; - font-weight: bold; - font-size: larger; -} - -img { - vertical-align: middle; - border: 0; -} - -div.disabled { - opacity : 0.4; - filter: alpha(opacity=40); /* msie */ - background-color: #000; -} - -table.tab { - border-collapse: collapse; -} - -td.selected_tab { - vertical-align: middle; - border: 1px #090 solid; - background: #ffffff; -} - -td.tab_filler { - background: #ffffff; - border-bottom: 1px #090 solid; -} - -td.tab { - vertical-align: middle; - border: 1px #090 solid; - background: #f0f0f0; -} - -.dashboard td { - padding: 4px 4px 4px 4px; -} - -pre {/* see http://users.tkk.fi/tkarvine/pre-wrap-css3-mozilla-opera-ie.html */ - white-space: pre-wrap; /* css-3 */ - white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ - white-space: -pre-wrap; /* Opera 4-6 */ - white-space: -o-pre-wrap; /* Opera 7 */ - word-wrap: break-word; /* Internet Explorer 5.5+ */ - margin: 0; -} - -pre a { - word-wrap: break-word; -} - -pre.console { - overflow: auto; -} - -.setting-leftspace { - width: 2em; -} - -.setting-name { - white-space: nowrap; -} - -.setting-main { - width: 100%; /* try to make this column as big as possible. */ -} -.setting-help { - width: 16px; - vertical-align: middle; -} -.setting-no-help { - width: 16px; -} - -.setting-input { - width: 100%; - border-radius: 3px; - border: 1px solid #ccc; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - padding: 6px; - box-sizing: border-box; - -webkit-box-sizing:border-box; - -moz-box-sizing: border-box; -} - -.setting-description { - font-size: 0.8em; - margin-top: 0; - padding-top: 0; -} - -/* tabBar */ - -.tabBar { - overflow: auto; - position: relative; - top: 1px; -} -.tabBar .tab { - float: left; - margin-left: 3px; -} -.tabBar .tab:first-child { - margin-left: 15px; -} -.tabBar .tab a { - position: relative; - display: block; - border: solid 1px #f0f0f0; - border-bottom: none; - text-decoration: none; - color: #999; - padding: 7px 10px; - border-top-left-radius: 5px; - border-top-right-radius: 5px; -} -.tabBar .tab a:hover { - background: #eee; -} -.tabBar .tab a.addTab { - font-weight: bold; - color: #0460d1; - border: 1px solid #d8efff; - border-bottom: none; -} -.tabBar .tab a.addTab:hover { - background: #d8efff; -} -.tabBar .tab [type=radio] { - display: none; -} -.tabBar [type=radio]:checked ~ a { - border: solid 1px #f0f0f0; - border-bottom: none; - background: #eee; - color: #000; - font-weight: bold; - z-index: 2; -} -.tabBarBaseline { - border-top: 1px solid #f0f0f0; - z-index: 1; -} - -.tabBarBaseline { - display: none; /* Baseline is hidden by default. See next rule fo adding visibility. */ -} -.tabBarFrame.showBaseline .tabBarBaseline { - display: block; -} - -/* div that looks like a hyperlink */ -.pseudoLink { - cursor: pointer; -} - -.advancedLink { - text-align: right; -} - -.advancedBody { - display: none; -} - -.scm_info { - width: 480px; -} - -.build-keep { - font-weight: bold; -} - -.task-header { - display: block; - border-bottom: 1px #090 solid; - font-weight: bold; - font-size: 12pt; -} - -.jenkins-not-applicable { - color: darkgrey; - font-style: italic; -} - -#jenkins .task-link { - text-decoration: none; -} - -#jenkins .task-link:hover { - text-decoration: underline; -} - -.task { - white-space: nowrap; -} - -.subtasks { - padding-left: 1em; -} - -.main-table { -} - -/* pane */ - -.pane-frame { - border: solid 1px #f0f0f0; - border-radius: 4px; -} - -#side-panel .pane-frame:hover, #side-panel .pane-frame.mouseover { - border: solid 1px #cecece; -} - -.pane-header, .pane-footer { - padding: 8px 0px; - background-color: #eee; - border: solid 1px #f3f3f3; - border-left: none; - border-right: none; - color: #3b3b3b; -} -.pane-header { - border-top: none; - border-top-left-radius: 4px; - border-top-right-radius: 4px; -} -.pane-footer { - border-bottom: none; - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; -} - -.pane td { - padding: 4px 4px 3px 4px; - vertical-align: middle; -} - -table.pane { - width: 100%; - border-collapse: collapse; - border: 1px #bbb solid; -} - -td.pane { - padding: 3px 4px 3px 4px; - vertical-align: middle; -} - -table.stripped tr:nth-of-type(even) { - background: #fbfbfb; -} - -table.stripped-even tr:nth-child(even) { - background: #fbfbfb; -} -table.stripped-odd tr:nth-child(odd) { - background: #fbfbfb; -} -table.stripped tr:hover, table.stripped-even tr:hover, table.stripped-odd tr:hover { - background: #e8e8e8 !important; -} - -div.pane-header { - font-weight: bold; - padding-right: 24px; -} - -div.pane-header .collapse { - float: right; - margin-left: 3px; -} - -th.pane { - font-weight: bold; -} - -/* executors */ - -#executors th.pane { - text-align: left; - padding: 12px 5px 5px 5px; -} - -#executors th.pane a { - text-decoration: none; -} - -/* bigtable */ - -.bigtable tr { - border: 1px solid #bbb; - padding: 3px 4px 3px 4px; -} -table.bigtable.pane > tbody > tr > td:last-child { - border-right: none; -} - -.pane-frame table, .pane-frame .bigtable tr { - border: none; /* Border will be provided by the pane-frame */ -} - -.bigtable th { - font-weight: bold; - border: none; - background-color: #f0f0f0; - padding: 6px 4px; - white-space: nowrap; -} - -.bigtable th.minimum-width { - width: 1px; -} - -.bigtable td { - vertical-align: middle; - padding: 3px 4px 3px 4px; -} - -.smallfont { - font-size: 9px; -} - -#foldertab { - padding: 4px 0; - margin-left: 0; - border-bottom: 1px solid #090; - font: bold 12px Helvetica, Arial, sans-serif; -} - -#foldertab li { - list-style: none; - margin: 0; - display: inline; -} - -#foldertab li a { - padding: 4px 0.5em; - margin-left: 3px; - border: 1px solid #090; - border-bottom: none; - background: #090; - text-decoration: none; -} - -#foldertab li a:link { color: white; } -#foldertab li a:visited { color: white; } - -#foldertab li a:hover { - color: white; - background: #6c0; - border-color: #6c0; -} - -#foldertab li a#current { - background: white; - border-bottom: 1px solid white; - color: black; -} - -.changeset-message { - border: 1px solid #ccb; - background: #eed; - padding: 4px; - white-space: normal; -} - -.disabledJob { - color: gray; -} - -.spinner { - padding-left: 32px; - padding-top: 0.5em; - padding-bottom: 0.5em; - background-image: url("../images/spinner.gif"); - background-repeat: no-repeat; - background-position: left; -} - -.spinner-right { - padding-right: 32px; - padding-top: 0.5em; - padding-bottom: 0.5em; - background-image: url("../images/spinner.gif"); - background-repeat: no-repeat; - background-position: right; -} - -#login-field { - vertical-align: middle; - padding-right: 1em; - width:1px; -} - -#login-field SPAN { - white-space:nowrap; - color: white; -} - -#login-field A { - /* - link inside login field should be always white. - If I set this to inherit, it won't work in IE7 - */ - color: white; -} - -div.behavior-loading { - position: absolute; - left:0; right:0; - width:100%; - height:100%; - background-color: #e4e4e4; text-align: center; font-size: 300%; - opacity: 0.5; - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; - filter: alpha(opacity=50); -} - -.config div.behavior-loading{ - background: rgba(255,255,255,.85); - left: 0; - width:100%; - right: 0; - top: 15px; - bottom: 0px; - height: 100%; - width: auto; - min-height: 100%; - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; - filter: alpha(opacity=100); - font-size: 1.5em; - z-index: 99; - opacity: 1; - color: #999; - text-shadow:#fff 0 0 5px,#fff 0 0 5px,#fff 0 0 5px,#fff 0 0 5px,#fff 0 0 5px -} - -.behavior-loading:before { - content:' '; - display:block; - width: 100px; - height: 100px; - margin: 200px auto 25px; - background-color: rgba(0,0,0,.15); - border:5px solid rgba(0,0,0,.33); - position:relative; - z-index:2; - border-radius: 100%; - -webkit-animation: sk-scaleout 1.0s infinite ease-in-out; - animation: sk-scaleout 1.0s infinite ease-in-out; - box-shadow:#fff 0 0 0 10px -} - -@-webkit-keyframes sk-scaleout { - 0% { -webkit-transform: scale(0) } - 100% { - -webkit-transform: scale(1.0); - opacity: 0; - } -} - -@keyframes sk-scaleout { - 0% { - -webkit-transform: scale(0); - transform: scale(0); - } 100% { - -webkit-transform: scale(1.0); - transform: scale(1.0); - opacity: 0; - } -} - -LABEL.attach-previous { - margin-left: 0.5em; -} - -.bottom-sticker, #bottom-sticker { - width: 100%; /* it needs to occupy the entire width or else the underlying content will see through */ -} -.bottom-sticker-inner { - background: white; - padding: 1em 0; -} -.bottom-sticker-edge { - height:16px; - background-image: url("../images/bottom-sticker-top-edge.png"); - background-repeat: repeat-x; -} - -.top-sticker, #top-sticker { - width: 100%; /* it needs to occupy the entire width or else the underlying content will see through */ - z-index: 999; -} -.top-sticker-inner { - padding:1em; -} -.top-sticker-edge { - height:16px; - background-image: url("../images/top-sticker-bottom-edge.png"); - background-repeat: repeat-x; -} -.top-sticker.noedge > .top-sticker-edge { - display: none; -} - -.top-sticker.noedge > .top-sticker-inner { - padding:0; -} - - -/* ======================== error/warning message (mainly in the form.) Use them on block elements ======================== */ -.error { - color: #CC0000; - font-weight: bold; - padding-left: 20px; - min-height: 16px; - line-height: 16px; - background-image: url("../images/16x16/error.png"); - background-position: left center; - background-repeat: no-repeat; -} - -.error-inline { - color: #CC0000; - font-weight: bold; -} - -.warning { - color: #C4A000; - font-weight: bold; - padding-left: 20px; - min-height: 16px; - line-height: 16px; - background-image: url("../images/16x16/warning.png"); - background-size: 16px 16px; - background-position: left center; - background-repeat: no-repeat; -} - -.warning-inline { - color: #C4A000; - font-weight: bold; -} - -.info { - color: black; - font-weight: bold; - padding-left: 20px; - min-height: 16px; - background-image: url("../images/16x16/go-next.png"); /* TODO: get a better icon */ - background-position: left center; - background-repeat: no-repeat; -} - -.icon16x16 { - width: 16px; - height: 16px; -} - -.icon24x24 { - width: 24px; - height: 24px; -} - -.icon32x32 { - width: 32px; - height: 32px; -} - -/* ====================== help ===================================== */ - -.help { - display: none; /* hidden until loaded */ - border: solid #bbb 1px; - background-color: #f0f0f0; - padding: 1em; - margin-bottom: 1em; - word-break: break-word; -} - -.help .from-plugin { - text-align: right; - color: #666; -} - -.help-area { - /* this marker class is used by JavaScript to locate the area to display help text. */ -} - - -/* ====================== project view tab bar ===================================== */ -#viewList { - border: none; - margin-bottom: 0px; - width: 100%; - white-space: nowrap; -} -#viewList td { - padding: 0px; -} -#viewList td.inactive { - border: solid 1px #ccc; - border-bottom-color: #bbb; -} -#viewList td.inactive:hover { - background-color: #777; -} -#viewList td.inactive a { - text-decoration: none; - color: #444; -} -#viewList td.inactive a:hover { - color: #f1f1f1; -} -#viewList td.noleft { - border-left: none; -} -#viewList td.noright { - border-right: none; -} -#viewList td.active { - border: solid 1px #bbb; - padding: 0.5em; - border-bottom: none; - vertical-align:middle; - background-color: rgb(240,240,240); - font-weight: bold; - white-space: nowrap; -} -#viewList td.filler { - border: none; - border-bottom: solid 1px #bbb; - width: 100%; - text-align: right; -} -#viewList a { - display: block; - padding: 0.5em; - white-space: nowrap; -} - -#projectstatus .header { - border-bottom: 1px solid #ddd; -} -#projectstatus th { - text-align: left; - padding: 7px 0px; -} -#projectstatus tbody tr:first-child th:first-child { - border-top-left-radius: 4px; -} -#projectstatus tbody tr:first-child th:last-child { - border-top-right-radius: 4px; -} -#projectstatus tbody tr:last-child td:first-child { - border-bottom-left-radius: 4px; -} -#projectstatus tr:last-child td:last-child { - border-bottom-right-radius: 4px; -} - -/* ============================ list view entries ======================== */ -div.listview-jobs { - max-height:300px; - overflow:auto; -} - -/* ============================ parameters form ========================== */ - -table.parameters { - border-collapse: collapse; -} - -table.parameters > tbody > tr:first-child > td { - padding-top: 4px; -} - -table.parameters .setting-description { - padding-bottom: 4px; -} - -table.parameters > tbody:hover { - background-color: #f0f0f0; -} - -/* ============================ health report hover ========================== */ - -.healthReport a { - text-decoration: none; -} - -.healthReport div.healthReportDetails { - display: none; -} - -.healthReport:hover, .healthReport.hover { /* fix IE6 bug with :hover */ - background: transparent; -} - -.healthReport:hover div.healthReportDetails, .healthReport.hover div.healthReportDetails { - display: block; - position: absolute; - background-color: #ffe; - border: 1px solid #bbb; - margin-left: 32px; /* move it across a bit */ - z-index: 26; -} - -.healthReport div.healthReportDetails table { - border-collapse: collapse; - width: 450px; /* fix IE bug with width */ -} - -/* ========================= build history ========================= */ -#buildHistory .healthReport { - display: inline; - margin-right: 1em; -} - -#buildHistory tr.no-wrap td.middle-align { - padding: 0; -} - -#buildHistory .desc { - position: relative; - padding: 0; - margin-top: 5px; - white-space: normal; - opacity: 0.6; -} - -#buildHistory .build-row-cell { - position: relative; -} - -#buildHistory .build-rss-links { - float: right; -} - -#buildHistoryPage { - position: relative; -} - -#buildHistoryPage .build-search-row td { - position: relative; -} -#buildHistoryPage .build-search-row .clear { - position: absolute; - padding: 0px 4px; - cursor: pointer; - top: 7px; - right: 6px; - opacity: 0.5; -} -#buildHistoryPage .build-search-row .clear:hover { - background-color: #d3d3d3; - border: 1px solid #bbb; - top: 6px; - padding-right: 3px; - border-top-right-radius: 2px; -} -#buildHistoryPage .build-search-row input { - width: 100%; - padding-left: 5px; - padding-right: 20px; -} -#buildHistoryPage .build-search-row table { - width: 100%; -} -#buildHistoryPage .build-search-row .build-search-help { - width: 20px; -} -#buildHistoryPage .build-search-row .icon-help { - position: absolute; - cursor: pointer; - top: 6px; -} - -#buildHistoryPageNav { - position: absolute; - right: -21px; - top: 41px; - border: 1px solid; - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; - visibility: hidden; -} - -#buildHistoryPageNav .nav { - width: 16px; - height: 16px; - background: no-repeat; - background-position: center; - background-size: 16px 16px; - margin: 4px 2px; - cursor: pointer; -} - -#buildHistoryPageNav.mouseOverSidePanel { - visibility: visible; - border-color: #f0f0f0 !important; -} -#buildHistoryPageNav.mouseOverSidePanelBuildHistory { - border-color: #bbb !important; -} -#buildHistoryPageNav:hover { - border-color: #bbb !important; -} - -#buildHistoryPageNav .pageOne { - background-image: url(../images/16x16/go_top.png); -} -#buildHistoryPageNav .pageUp { - background-image: url(../images/16x16/go_up.png); -} -#buildHistoryPageNav .pageDown { - background-image: url(../images/16x16/go_down.png); -} -#buildHistoryPageNav .find { - background-image: url(../images/16x16/find.png); -} - -.build-row { - padding: 3px 4px 3px 4px; -} - -.build-row.model-link-active { - background: #e8e8e8 !important; -} - -.build-row-cell { - font-size: 12px; -} - -.build-row-cell .pane.build-name { - width: 25%; - font-weight: bold; - vertical-align: top; -} -.build-row-cell .pane.build-details { - width: 50%; -} -.build-row-cell .pane.build-controls { - width: 25%; - text-align: right; -} -.build-row-cell .pane.build-details.block { - width: 100%; -} - -.pane.build-name a, -.pane.build-name a:visited { - color: black; - text-decoration: underline; -} - -.pane.build-details a, -.pane.build-details a:visited { - color: black; - opacity: 0.6; - text-decoration: none; -} -.pane.build-details a:hover { - opacity: 1.0; - text-decoration: underline; -} - -.build-row.multi-line .build-row-cell .pane.build-name.block { - width: 100%; -} -.build-row-cell .pane.build-controls.block { - width: 100%; -} - -.build-row-cell .pane.build-name .build-icon, -.build-row-cell .pane.build-name .display-name { - display: inline-block; -} -.build-row-cell .pane.build-name .build-icon { - position: absolute; -} - -.build-row-cell .build-stop { - display: inline-block; - width: 30%; -} -.build-row-cell .build-badge { - display: inline-block; - text-align: right; - width: 70%; - padding: 2px 0; -} -.build-row-cell .build-badge > span { - display: inline-block; - max-width: 256px; - padding: 0 1px; - overflow: hidden; -} -.build-row-cell .build-badge > span + span { - margin: 0 0 0 2px !important; -} -@media (min-width: 1170px) { - .build-row-cell .build-badge > span { - max-width: 296px; - } -} - -.build-row .build-name-controls .pane.build-name, -.build-row .build-details-controls .pane.build-details { - width: 70%; -} - -.build-row .build-row-cell .pane { - padding: 0px 2px; /* Sync changes with func expandControlsTo50Percent in hudson-behavior.js */ - display: inline-block; - overflow: hidden; -} - -.build-row.multi-line .build-row-cell .block { - display: block; - overflow: auto; -} - -.build-row.multi-line .build-row-cell .indent-multiline { - margin-top: 5px; -} - -.build-row.multi-line .build-row-cell .left-bar { - position: absolute; - top: 19px; - bottom: 3px; - left: 14px; - border-left: 1px solid #cdcdcd; -} - -.build-row-cell .pane.build-name .display-name { - margin-left: 20px; -} - -.build-row-cell .indent-multiline { - padding-left: 20px !important; /* Sync changes with func expandControlsTo50Percent in hudson-behavior.js */ -} - -.build-row .build-row-cell { - /*visibility: hidden;*/ -} -.build-row.overflow-checked .build-row-cell { - visibility: visible; -} - -/* ================ Element overflow calculation helper styles ================ */ - -.force-wrap, .force-wrap a { - white-space: normal !important; - overflow: visible !important; -} -.force-nowrap, .force-nowrap a { - white-space: nowrap !important; - overflow: hidden !important; -} - -/* ========================= editable combobox style ========================= */ -.comboBoxList { - border: 1px solid #000; - overflow: visible; - background-color: white; - color: black; -} -.comboBoxSelectedItem { - background-color: Highlight; - color: HighlightText; -} -.combobox-values { - display: none; -} - - -/* ========================= directory tree ========================= */ -.parentPath { - font-size: 1.2em; - font-weight: bold; -} - -.dirTree li { - list-style: none; -} - -.dirTree .rootIcon { - margin-right: 1em; -} - -TABLE.fileList { - margin-left: 2em; - padding: 0; -} - -TABLE.fileList TD { - padding: 0; -} - -TABLE.fileList TD.fileSize { - padding-left: 2em; - text-align: right; - color: #888; -} - - - -/* ========================= test result ========================= */ -.result-passed { - color: #3465a4; -} - -.result-skipped { - color: #ddbb00; -} - -.result-fixed { - color: #3465a4; - font-weight: bold; -} - -.result-failed { - color: #ef2929; -} - -.result-regression { - color: #ef2929; - font-weight: bold; -} -.test-trend-caption { - text-align: center; - font-size: 1.2em; - font-weight: bold; -} - - - -/* ========================= sortable table ========================= */ -table.sortable a.sortheader { - text-decoration: none; - color: black; - display: block; -} -table.sortable span.sortarrow { - color: black; - text-decoration: none; -} - - - - -/* ========================= fingerprint ========================= */ -.md5sum { - text-align: right; -} - -.fingerprint-summary-header { - font-size: 1.2em; - vertical-align: middle; -} - -TABLE.fingerprint-in-build TD { - padding-left: 1em; - padding-right: 1em; -} - - - - -/* ========================= plugin manager ========================= */ -#plugins { - margin-top: 0; - border-top: none; -} - -#pluginsAdv tr:hover { - background-color: inherit; -} - -#plugins tr.already-upgraded { - background-color: #e8eeee; -} - -#plugins input.uninstall { - font-size: 11px; - padding: 3px 5px; - border-radius: 1px; - font-family: Helvetica, Arial, sans-serif; - font-weight: bold; - background-color: #4b758b; - color: #eee; - border: 1px solid #5788a1; -} - -#plugins tr.has-dependents input.uninstall { - background-color: #e0e0e0; - border: 1px solid #cccccc; - color: #505050; - background-color: white; -} - -#plugins tr.all-dependents-disabled .enable input { - pointer-events: auto; - opacity: 1.0; - visibility: visible; -} - -#plugins tr.has-dependents input, #plugins tr.has-disabled-dependency .enable input { - pointer-events: none; - opacity: 0.2; -} - -#plugins tr.has-dependents-but-disabled .enable input { - pointer-events: auto; - opacity: 1.0; - visibility: visible; -} - -#plugins tr.has-disabled-dependency .enable input { - opacity: 0.4; -} - -#plugins tr.deleted input { - visibility: hidden !important; -} - -#plugins .dependent-list, #plugins .dependency-list { - display: none; -} - -#plugins .enable-state-info, #plugins .uninstall-state-info { - padding: 5px 20px; - max-width: 70%; - border: solid 1px #f0ad4e; - border-radius: 3px; - text-align: center; -} - -#plugins .enable-state-info { - float: left; -} - -#plugins .uninstall-state-info { - float: right; -} - -#plugins .plugin-dependency-info .title, #plugins .plugin-dependency-info .subtitle { - opacity: 0.7; -} -#plugins .plugin-dependency-info .title { - margin-bottom: 5px; - font-size: larger; - font-weight: bolder; -} - -#plugins .plugin-dependency-info span { - margin: 5px 5px 0px 0px; - background-color: #337ab7; - display: inline-block; - padding: .2em .6em .3em; - font-size: 75%; - font-weight: 700; - line-height: 1; - color: #fff; - text-align: center; - white-space: nowrap; - vertical-align: baseline; - border-radius: .25em; -} - -/* ========================= repeatable elements ========================= */ - -.repeated-chunk { - border: 2px dashed transparent; -} - -.repeated-chunk.hover { - border-color: #CCC; -} - -.repeated-chunk .show-if-last { visibility: hidden; } -.repeated-chunk.last .show-if-last { visibility: visible; } - -.repeated-chunk .show-if-not-last { visibility: visible; } -.repeated-chunk.last .show-if-not-last { visibility: hidden; } - -.repeated-chunk .show-if-not-only { visibility: visible; } -.repeated-chunk.only .show-if-not-only { visibility: hidden; } - -/* == nested repeatable elements / 2 deep == */ -.repeated-chunk .repeated-chunk .show-if-last { visibility: hidden; } -.repeated-chunk .repeated-chunk.last .show-if-last { visibility: visible; } - -.repeated-chunk .repeated-chunk .show-if-not-last { visibility: visible; } -.repeated-chunk .repeated-chunk.last .show-if-not-last { visibility: hidden; } - -.repeated-chunk .repeated-chunk .show-if-not-only { visibility: visible; } -.repeated-chunk .repeated-chunk.only .show-if-not-only { visibility: hidden; } - -/* == nested repeatable elements / 3 deep == */ -.repeated-chunk .repeated-chunk .repeated-chunk .show-if-last { visibility: hidden; } -.repeated-chunk .repeated-chunk .repeated-chunk.last .show-if-last { visibility: visible; } - -.repeated-chunk .repeated-chunk .repeated-chunk .show-if-not-last { visibility: visible; } -.repeated-chunk .repeated-chunk .repeated-chunk.last .show-if-not-last { visibility: hidden; } - -.repeated-chunk .repeated-chunk .repeated-chunk .show-if-not-only { visibility: visible; } -.repeated-chunk .repeated-chunk .repeated-chunk.only .show-if-not-only { visibility: hidden; } - -/* -

s marked with to-be-removed is used in conjunction with repeatable.jelly and hetero-list.jelly - and represents a master copy that gets pulled out from HTML, then inserted later upon demand multiple times - when the user does "Add". -*/ -DIV.to-be-removed { display: none; } - -/* ========================= Other form related CSS ========================= */ - -.row-set-end { display: none; } - -/* ========================= Yahoo UI style adjustments ========================= */ -.ygtvlabel, .ygtvlabel:link, .ygtvlabel:visited, .ygtvlabel:hover { - color: inherit; -} - -DIV.yahooTree td { - vertical-align: middle; -} - -.yui-tt { - border: 1px solid black !important; - background-color: #FFFFFF !important; - padding: 2px !important; - font-family: inherit !important; - color: inherit !important; -} - -.yui-skin-sam .yui-tt .bd { - border: none !important; - background-color: #FFF !important; -} - -.yuimenu LI .yui-menu-tooltip { - color: #A6A6A6; -} - -.yuimenu LI.yui-menuitem-tooltip { - border-bottom: 1px solid #808080; - padding-bottom: 3px; - margin-bottom: 1em; -} - -#jenkins .yuimenuitem { - font-size: 12px; - padding: 3px; -} - -#jenkins .yuimenuitem, -#jenkins .yuimenuitemlabel { - cursor: pointer; -} - -#jenkins .yuimenu .bd { - border: 1px solid #ccc; - box-shadow: 0px 3px 10px #bbb; -} - -#jenkins .yui-menu-shadow { - display: none; -} - -/* ========================= search box at the top-right of the page ========================= */ -#search-box { - background: white url(../images/16x16/search.png) no-repeat 2px center; - height: 24px; - padding-left: 20px; - width: 15em; - position: static; - border: 1px solid #DDD; - border-radius: 4px; - box-shadow: 0 0 5px #DDD inset; - outline: none; -} - -#search-box-completion { - text-align: left; - width:25em; - position: absolute; - z-index: 1000; -} - -#search-box-completion .yui-ac-content { - border: 1px solid black; - width:35em; - background-color: white; - overflow: hidden; -} - -#search-box-completion UL { - padding: 0 0; - width: 100%; - margin: 0 0; - list-style: none; -} -#search-box-completion LI { - padding-left:20px; - white-space:nowrap; -} -#search-box-completion LI.yui-ac-highlight { - background: #729FCF; -} -#search-box-minWidth { - position:absolute; - visibility: hidden; - width:15em; -} - -#search-box-sizer { - position:absolute; - visibility: hidden; -} - - -/* ========================= resizable text area ========================= */ - -TEXTAREA { - margin-bottom: 0; -} -DIV.textarea-handle { - height: 5px; - font-size: 0; - background: #EEE url(../images/textarea-handle.gif) no-repeat 50% 1px; - border: 1px solid #BABDB6; - border-top: none; - cursor: s-resize; -} -TEXTAREA.rich-editor { - visibility: hidden; -} - -/* ========================= hover notification ========================= */ - -#hoverNotification { - visibility:hidden; - background-color: white; - border: 1px solid black; - padding: 0.5em; -} - -/* ========================= D&D support in heterogenous/repeatable lists = */ - -.hetero-list-container .dd-handle, .repeated-container .dd-handle { - cursor: move; - background-image: url(../images/grip.png); - background-repeat: repeat-y; - padding-left: 20px; -} - -.hetero-list-container.with-drag-drop .repeated-chunk { - padding: 0.5em; - margin-top: 0.5em; - margin-bottom: 0.5em; -} - - -/* ========================= plugin update center ========================= */ - -#plugins .excerpt { - white-space: normal; - margin-top: 0.5em; - padding-left: 2em; - color: #888a85; -} - -/* - * TODO(andipabst): Not used after https://github.com/jenkinsci/jenkins/pull/4299, - * remove once there are no more dependencies - */ -#plugins .compatWarning { - white-space: normal; - margin-top: 0.5em; - padding-left: 2em; - color: #FF0000; -} - -#plugins .securityWarning { - white-space: normal; - margin-top: 0.5em; - padding-left: 2em; - color: #FF0000; -} - -/* ========================= progress bar ========================= */ - -table.progress-bar { - border-collapse: collapse; - border: 1px solid #3465a4; - height: 6px; - width: 100px; - clear: none; -} - -table.progress-bar tr.unknown { - background-image:url(../images/progress-unknown.gif); -} - -td.progress-bar-done { - background-color: #3465a4; -} - -td.progress-bar-left { - background-color: #ffffff; -} - -table.progress-bar.red { - border: 1px solid #cc0000; -} - -table.progress-bar.red tr.unknown { - background-image:url(../images/progress-unknown-red.gif); -} -table.progress-bar.red td.progress-bar-done { - background-color: #cc0000; -} - -/* ========================= notification bar ========================= */ -#notification-bar { - width:100%; - position:fixed; - text-align:center; - left:0px; - font-size: 2em; - z-index:1000; - border-bottom: 1px solid black; -} - -/* ========================= YUI dialog ========================= */ - -.dialog .hd { - font-size: 12px !important; -} -/* discovered this margin fix by a trial and error. This can very well be a totally wrong fix, or perhaps updating - to the latest YUI will fix this? */ -.dialog .hd { - margin: 0 !important; -} - -.dialog .bd { - margin: 0 !important; -} - -.dialog .ft { - margin: 0 !important; -} - -/* ========================= tags/labels ================== */ -/* tag0 is the least important tag in a tag cloud */ -.tag0 { font-size: 1.00em; } -.tag1 { font-size: 1.10em; } -.tag2 { font-size: 1.20em; } -.tag3 { font-size: 1.30em; } -.tag4 { font-size: 1.40em; } -.tag5 { font-size: 1.50em; } -.tag6 { font-size: 1.60em; } -.tag7 { font-size: 1.70em; } -.tag8 { font-size: 1.80em; } -.tag9 { font-size: 1.90em; } - -/* ========================= textarea.jelly ================== */ - -.textarea-preview-container { - text-align:left; -} -.textarea-preview { - background-color: #F0F0F0; - padding: 0.5em; -} - -/* ========================= logRecords.jelly ================== */ - -.logrecord-metadata { - font-size: 70%; -} - -.logrecord-metadata-new { - color: #8A8; -} - -.logrecord-metadata-old { - color: #AAA; -} - -/* ========================= matrix configuration table ================== */ -table#configuration-matrix { - border: 1px #BBBBBB solid; - border-collapse: collapse; -} -tr.matrix-row { - background: #f0f0f0; - font-weight: bold; -} -td.matrix-header { - border: 1px #BBBBBB solid; - padding: 3px; -} -td#matrix-title { - padding: 3px; -} -td.matrix-leftcolumn { - border: 1px #BBBBBB solid; - font-weight: bold; - background: #f0f0f0; - padding: 3px; -} -td.matrix-cell { - border: 1px #BBBBBB solid; - text-align: center; -} - -/* ========================= legend.jelly ================== */ -table#legend-table td { - vertical-align: middle; -} - -/* ========================= select.jelly ================== */ -select.select-ajax-pending { - padding-left: 1.5em; - padding-top: 0.5em; - padding-bottom: 0.5em; - color: transparent; - background-image: url(../images/spinner.gif); /* this is why here and not in an adjunct */ - background-repeat: no-repeat; - background-position: 2px; -} - -/* ========================= Button styles ================= */ -.btn-box { - display:block; - margin-top:2em -} -#disable-project { - margin-top: 6px; -} - -#bottom-sticker .yui-button { - margin-right: 20px; -} - -/* This level of nesting is technically unnecessary, but to override the - * default Yahoo styles. */ -#jenkins .yui-button { - border-width: 0; - border-style: none; - border-color: transparent; - background: none; - text-decoration: none; -} - -#jenkins .yui-button .first-child { - border-width: 0; - border-style: none; - border-color: transparent; -} -#jenkins .yui-button .btn, -#jenkins .yui-button button { - font-size: 12px; - padding: 3px 20px; - *border: 0; - border-radius: 1px; - - border: 1px solid #cccccc; - background-color: #e0e0e0; - font-family: Helvetica, Arial, sans-serif; - - color: #505050; - font-weight: bold; -} - -#jenkins .yui-button .hetero-list-add { - padding-right: 35px; -} - -#jenkins .yui-button .btn:hover, -#jenkins .yui-button button:hover, -#jenkins .yui-button .hover -{ - background-color: #d0d0d0; - border: 1px solid #c0c0c0; -} - -#jenkins .yui-button .btn:active, -#jenkins .yui-button button:active, -#jenkins .yui-button .active { - background-color: #bbb; - box-shadow: inset 0px 1px 6px 2px #929292; - border: 1px solid #bbb; -} - -#jenkins .yui-button .btn.disabled, -#jenkins .yui-button button[disabled], -#jenkins .yui-button button[disabled]:hover, -#jenkins .yui-button button[disabled]:active, -#jenkins .yui-button .disabled { - background-color: #e5e5e5; - color: #999; - border: 1px solid #d2d2d2; -} - -/* Color overrides */ -#jenkins .yui-button.primary .btn, -#jenkins .yui-button.primary button { - background-color: #4b758b; - color: #eee; - border: 1px solid #5788a1; -} -#jenkins .yui-button.primary .btn:hover, -#jenkins .yui-button.primary button:hover, -#jenkins .yui-button.primary .hover -{ - background-color: #3f6275; - border: 1px solid #5788a1; -} -#jenkins .yui-button.primary .btn:active, -#jenkins .yui-button.primary button:active, -#jenkins .yui-button.primary .active -{ - background-color: #33505f; - border: 1px solid #3f6275; - box-shadow: inset 0px 1px 6px 2px #1b2b33; -} - -#jenkins .yui-button.primary button:disabled, -#jenkins .yui-button.primary .disabled -{ - background-color: #adc6d3; - color: #fff; - border: 1px solid #a2becd; -} - -#jenkins .yui-button.danger button { - background-color: #d24939; - color: #eee; - border: 1px solid #be3a2b; -} - -#jenkins .yui-button.danger button:hover, -#jenkins .yui-button.danger .hover -{ - background-color: #a23225; - border: 1px solid #942e22; -} - -#jenkins .yui-button.danger button:active, -#jenkins .yui-button.danger .active -{ - background-color: #942e22; - border: 1px solid #6b2118; - box-shadow: inset 0px 1px 6px 2px #79251b; -} - -#jenkins .yui-button.danger button:disabled, -#jenkins .yui-button.danger .disabled -{ - background-color: #e5958c; - color: #f8f8f8; - border: 1px solid #e39280; -} - -.i18n { - display: none; -} - -.alert { - padding: 15px; - margin-bottom: 20px; - border: 1px solid transparent; - border-radius: 4px; -} - -.alert-info { - color: #31708f; - background-color: #d9edf7; - border-color: #bce8f1; -} - -.alert-info p { - color: #31708f; -} - -.alert-warning { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #faebcc; -} - -.alert-warning p { - color: #8a6d3b; -} - -.alert-danger { - color: #a94442; - background-color: #f2dede; - border-color: #ebccd1; -} - -.alert-danger p { - color: #a94442; -} - -body.no-decoration #main-panel { - margin: 0 auto !important; -} -body.no-decoration #page-head, -body.no-decoration #side-panel, -body.no-decoration footer { - display: none; -} -body.no-sticker #bottom-sticker { - display: none; -} - -/* see the Icon class for the definition of these CSS classes */ -.icon-sm { - width: 16px; - height: 16px; -} - -.icon-md { - width: 24px; - height: 24px; -} - -.icon-lg { - width: 32px; - height: 32px; -} - -.icon-xlg { - width: 48px; - height: 48px; -} - -/* ----------- Manage Jenkins ----------- */ - -.manage-messages { - margin-bottom: 20px; -} - -.manage-messages a, .manage-messages a:visited { - color: inherit !important; - text-decoration: none; -} - -.manage-messages a:hover, .manage-messages a:focus { - text-decoration: underline; -} - -.manage-messages dl:first-child { - margin-top: 0; -} - -.manage-messages dl dt:first-child { - margin-top: 0; -} - -.manage-messages dl dt { - margin-top: 10px; - font-weight: normal; -} - -.manage-messages dl dd { - margin-left: 15px; -} - -.manage-messages dl:last-child { - margin-bottom: 0; -} - -.manage-messages dl dt::after { - content: ": "; -} - -.manage-messages .alert a { - text-decoration: underline; -} - -.manage-messages .alert a:hover { - text-decoration: none; -} - -.manage-messages .alert form { - position: relative; - float: right; - margin: -6px 0 0 0 !important; -} - -.manage-messages .alert form span { - margin: 0 0 0 4px !important; -} - -.manage-option { - border: 1px solid transparent; - margin-bottom: 4px; -} - -.manage-option:hover { - border: 1px solid #ccc; - border-radius: 3px; - background-color: #fafafa; -} - -.manage-option dl { - padding: 0; - margin: 0 0 0 62px; - min-height: 48px; -} - -.manage-option dl dt { - font-size: 16px; - line-height: 24px; -} - -.manage-option dl dd { - margin-left: 0; - line-height: 20px; - color: #333; - font-size: 14px; -} - -.manage-option a { - display: block; - padding: 8px; - margin: 0; - text-decoration: none !important; - color: #000; - outline: 0; -} - -.manage-option .icon { - display: block; - float: left; - position: relative; - width: 48px; - height: 48px; -} - -/* -------------------------------------- */ - -/* --------------- RSS bar -------------- */ - -#rss-bar { - margin: 1em; - text-align: right; -} - -#rss-bar .icon-rss { - border: 0; -} - -#rss-bar .rss-bar-item { - padding-left: 1em; -} - -/* -------------------------------------- */ - -/* -------------- SVG icons ------------- */ - -.svg-icon { - display: inline-block; - vertical-align: middle; - - /* default dimensions */ - height: 24px; - width: 24px; - - /* default fill fallback */ - fill: #111; - fill: currentColor; -} - -/* -------------------------------------- */ From 45dd8e470c12de7c4583dae743e1785ed3dde8e7 Mon Sep 17 00:00:00 2001 From: zbynek Date: Sat, 21 Mar 2020 11:17:46 +0100 Subject: [PATCH 12/19] Remove layout-common.css --- war/src/main/webapp/css/layout-common.css | 152 ---------------------- 1 file changed, 152 deletions(-) delete mode 100644 war/src/main/webapp/css/layout-common.css diff --git a/war/src/main/webapp/css/layout-common.css b/war/src/main/webapp/css/layout-common.css deleted file mode 100644 index 2f6d41c2a6..0000000000 --- a/war/src/main/webapp/css/layout-common.css +++ /dev/null @@ -1,152 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2016, CloudBees, Inc. - * - * 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. - */ - -html { - position: relative; - min-height: 100%; -} - -body { - margin: 0; - padding: 0 0 40px 0; -} - -/* --------------- header --------------- */ - -#header { - background-color: #000000; - height: 40px; -} - -#header div { - display: inline-block; - height: inherit; -} - -#header .logo { - margin-left: 16px; -} - -#jenkins-home-link { - position: absolute; - height: 40px; -} - -#jenkins-head-icon { - position: absolute; - bottom: 0px; -} - -#jenkins-name-icon { - position: absolute; - bottom: 3px; - left: 32px; -} - -#header .searchbox, #header .login { - float: right; - padding: 6px 11px; -} - -#breadcrumbBar, #footer-container, .top-sticker-inner { - background-color: #f6faf2; -} - -/* -------------------------------------- */ - -#page-body.clear:after { - clear: both; - content: ""; - display: table; -} - -#side-panel { - padding: 15px 15px 40px 15px; - float: left; - width: 320px; -} - -#main-panel { - padding: 15px 15px 40px 15px; -} - -body.two-column #main-panel { - margin-left: 320px; - display:block; -} - -body.full-screen { - padding: 0; -} - -body.full-screen #main-panel { - padding: 0; -} - -@media (max-width: 970px) { - body.two-column #side-panel { - width: 100%; - float: none; - padding-bottom: 20px; - } - - body.two-column #main-panel { - margin-left: 0; - display:block; - } -} - -@media (min-width: 1170px) { - body.two-column #side-panel { - width: 360px; - } - - body.two-column #main-panel { - margin-left: 360px; - display:block; - } -} - -/* --------------- footer --------------- */ - -footer { - padding: 11px 0; - background-color: #f6faf2; - border-top: 1px solid #d3d7cf; - border-bottom: 1px solid #f6faf2; - width: 100%; - position: absolute; - bottom: 0; - left: 0; - clear: both; - font-size: 12px; - text-align: right; -} - -footer span { - margin-left: 15px; - line-height: 14px; -} - -/* -------------------------------------- */ From 2c37b1d1ba5fbfcba6c82e58dcff14b382fc80cb Mon Sep 17 00:00:00 2001 From: Daniel Beck Date: Thu, 26 Mar 2020 01:26:37 +0100 Subject: [PATCH 13/19] [JENKINS-61688] Load build discarder configuration after restart --- .../GlobalBuildDiscarderConfiguration.java | 4 ++++ .../model/GlobalBuildDiscarderTest.java | 20 +++++++++++++++++++ ...odel.GlobalBuildDiscarderConfiguration.xml | 4 ++++ ...odel.GlobalBuildDiscarderConfiguration.xml | 14 +++++++++++++ 4 files changed, 42 insertions(+) create mode 100644 test/src/test/resources/jenkins/model/GlobalBuildDiscarderTest/testLoading/jenkins.model.GlobalBuildDiscarderConfiguration.xml create mode 100644 test/src/test/resources/jenkins/model/GlobalBuildDiscarderTest/testLoadingWithDiscarders/jenkins.model.GlobalBuildDiscarderConfiguration.xml diff --git a/core/src/main/java/jenkins/model/GlobalBuildDiscarderConfiguration.java b/core/src/main/java/jenkins/model/GlobalBuildDiscarderConfiguration.java index 9b0ca5aaf8..8a1d5cf8c8 100644 --- a/core/src/main/java/jenkins/model/GlobalBuildDiscarderConfiguration.java +++ b/core/src/main/java/jenkins/model/GlobalBuildDiscarderConfiguration.java @@ -48,6 +48,10 @@ public class GlobalBuildDiscarderConfiguration extends GlobalConfiguration { return ExtensionList.lookupSingleton(GlobalBuildDiscarderConfiguration.class); } + public GlobalBuildDiscarderConfiguration() { + load(); + } + private final DescribableList configuredBuildDiscarders = new DescribableList<>(this, Collections.singletonList(new JobGlobalBuildDiscarderStrategy())); diff --git a/test/src/test/java/jenkins/model/GlobalBuildDiscarderTest.java b/test/src/test/java/jenkins/model/GlobalBuildDiscarderTest.java index 4544e5c881..8c3be81c0d 100644 --- a/test/src/test/java/jenkins/model/GlobalBuildDiscarderTest.java +++ b/test/src/test/java/jenkins/model/GlobalBuildDiscarderTest.java @@ -5,15 +5,35 @@ import hudson.model.FreeStyleProject; import hudson.model.Run; import hudson.model.TaskListener; import hudson.tasks.LogRotator; +import hudson.util.DescribableList; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; +import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.recipes.LocalData; public class GlobalBuildDiscarderTest { @Rule public JenkinsRule j = new JenkinsRule(); + @Test + @LocalData + @Issue("JENKINS-61688") + public void testLoading() throws Exception { + Assert.assertEquals(0, GlobalBuildDiscarderConfiguration.get().getConfiguredBuildDiscarders().size()); + } + + @Test + @LocalData + @Issue("JENKINS-61688") + public void testLoadingWithDiscarders() throws Exception { + final DescribableList configuredBuildDiscarders = GlobalBuildDiscarderConfiguration.get().getConfiguredBuildDiscarders(); + Assert.assertEquals(2, configuredBuildDiscarders.size()); + Assert.assertNotNull(configuredBuildDiscarders.get(JobGlobalBuildDiscarderStrategy.class)); + Assert.assertEquals(5, ((LogRotator)configuredBuildDiscarders.get(SimpleGlobalBuildDiscarderStrategy.class).getDiscarder()).getNumToKeep()); + } + @Test public void testJobBuildDiscarder() throws Exception { FreeStyleProject p = j.createFreeStyleProject(); diff --git a/test/src/test/resources/jenkins/model/GlobalBuildDiscarderTest/testLoading/jenkins.model.GlobalBuildDiscarderConfiguration.xml b/test/src/test/resources/jenkins/model/GlobalBuildDiscarderTest/testLoading/jenkins.model.GlobalBuildDiscarderConfiguration.xml new file mode 100644 index 0000000000..08970809ad --- /dev/null +++ b/test/src/test/resources/jenkins/model/GlobalBuildDiscarderTest/testLoading/jenkins.model.GlobalBuildDiscarderConfiguration.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/test/src/test/resources/jenkins/model/GlobalBuildDiscarderTest/testLoadingWithDiscarders/jenkins.model.GlobalBuildDiscarderConfiguration.xml b/test/src/test/resources/jenkins/model/GlobalBuildDiscarderTest/testLoadingWithDiscarders/jenkins.model.GlobalBuildDiscarderConfiguration.xml new file mode 100644 index 0000000000..c40e61245d --- /dev/null +++ b/test/src/test/resources/jenkins/model/GlobalBuildDiscarderTest/testLoadingWithDiscarders/jenkins.model.GlobalBuildDiscarderConfiguration.xml @@ -0,0 +1,14 @@ + + + + + + + -1 + 5 + -1 + -1 + + + + \ No newline at end of file From 03659a1f1bdc61813e6153db1a2e990562724637 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Thu, 26 Mar 2020 17:55:22 -0400 Subject: [PATCH 14/19] Do not print random binary data during WebSocketAgentsTest --- test/src/test/java/jenkins/agents/WebSocketAgentsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/src/test/java/jenkins/agents/WebSocketAgentsTest.java b/test/src/test/java/jenkins/agents/WebSocketAgentsTest.java index 009a99963e..66d04f7f69 100644 --- a/test/src/test/java/jenkins/agents/WebSocketAgentsTest.java +++ b/test/src/test/java/jenkins/agents/WebSocketAgentsTest.java @@ -103,7 +103,7 @@ public class WebSocketAgentsTest { assertNotNull(s.getChannel().call(new FatTask())); FreeStyleProject p = r.createFreeStyleProject(); p.setAssignedNode(s); - p.getBuildersList().add(Functions.isWindows() ? new BatchFile("echo hello") : new Shell("dd if=/dev/random count=1024 bs=200")); + p.getBuildersList().add(Functions.isWindows() ? new BatchFile("echo hello") : new Shell("echo hello")); r.buildAndAssertSuccess(p); s.toComputer().getLogText().writeLogTo(0, System.out); } finally { From 8dcf818f654d391646b75fa700b77b9e5c623086 Mon Sep 17 00:00:00 2001 From: Daniel Beck Date: Fri, 27 Mar 2020 17:51:07 +0100 Subject: [PATCH 15/19] [JENKINS-61692] Fix proxy form validation --- core/src/main/resources/hudson/ProxyConfiguration/config.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/hudson/ProxyConfiguration/config.groovy b/core/src/main/resources/hudson/ProxyConfiguration/config.groovy index 0be2a539b6..c6f2603e94 100644 --- a/core/src/main/resources/hudson/ProxyConfiguration/config.groovy +++ b/core/src/main/resources/hudson/ProxyConfiguration/config.groovy @@ -22,5 +22,5 @@ f.advanced(){ f.textbox() } f.validateButton(title:_("Validate Proxy"), - method:"validateProxy", with:"testUrl,name,port,userName,password,noProxyHost") + method:"validateProxy", with:"testUrl,name,port,userName,secretPassword,noProxyHost") } From c2cbd5e984eae01986f4a05ab44bcc1d47ec2cd5 Mon Sep 17 00:00:00 2001 From: Daniel Beck Date: Sat, 28 Mar 2020 01:49:32 +0100 Subject: [PATCH 16/19] Support null Secrets when converting form submissions --- core/src/main/java/hudson/util/Secret.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/main/java/hudson/util/Secret.java b/core/src/main/java/hudson/util/Secret.java index d53054cec9..a063c0fb4b 100644 --- a/core/src/main/java/hudson/util/Secret.java +++ b/core/src/main/java/hudson/util/Secret.java @@ -303,6 +303,9 @@ public final class Secret implements Serializable { static { Stapler.CONVERT_UTILS.register(new org.apache.commons.beanutils.Converter() { public Secret convert(Class type, Object value) { + if (value == null) { + return null; + } if (value instanceof Secret) { return (Secret) value; } From e76f6a293aa26eeddd6887d43a9087e38f9e103a Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Sat, 28 Mar 2020 15:13:00 -0400 Subject: [PATCH 17/19] Let ConfidentialKey & Secret do something from unit tests (#4603) * Delete ConfidentialStoreRule * Also delete MockSecretRule so Secret.fromString works * Secret.SECRET still referenced by SecretHelper in jenkins-test-harness, used by RekeySecretAdminMonitorTest, also copied in publish-over-*-plugin * RSAConfidentialKey ought to use the same SecureRandom as the ConfidentialStore; this also lets the mock use a fixed seed * For now ConfidentialStore.secureRandom can be abstract * Some core unit tests can be rather verbose; set maven.test.redirectTestOutputToFile * Looks like we need a ConfidentialStoreRule after all * Restored another couple of usages --- core/pom.xml | 11 ++++ .../java/hudson/util/HistoricalSecrets.java | 12 +++- core/src/main/java/hudson/util/Secret.java | 5 +- .../jenkins/security/ConfidentialStore.java | 66 ++++++++++++++++++- .../security/DefaultConfidentialStore.java | 5 ++ .../jenkins/security/RSAConfidentialKey.java | 3 +- .../test/java/hudson/util/MockSecretRule.java | 33 ---------- .../java/hudson/util/SecretRewriterTest.java | 3 - .../src/test/java/hudson/util/SecretTest.java | 3 - .../security/ConfidentialStoreRule.java | 17 +---- .../security/RekeySecretAdminMonitorTest.java | 5 +- 11 files changed, 98 insertions(+), 65 deletions(-) delete mode 100644 core/src/test/java/hudson/util/MockSecretRule.java diff --git a/core/pom.xml b/core/pom.xml index b5913b102f..d9ffd0294c 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -895,5 +895,16 @@ THE SOFTWARE. true + + all-tests + + + !test + + + + true + + diff --git a/core/src/main/java/hudson/util/HistoricalSecrets.java b/core/src/main/java/hudson/util/HistoricalSecrets.java index 7b1d0ee3bb..ed0bbf9d4d 100644 --- a/core/src/main/java/hudson/util/HistoricalSecrets.java +++ b/core/src/main/java/hudson/util/HistoricalSecrets.java @@ -81,9 +81,15 @@ public class HistoricalSecrets { */ @Deprecated /*package*/ static SecretKey getLegacyKey() throws GeneralSecurityException { - String secret = Secret.SECRET; - if(secret==null) return Jenkins.get().getSecretKeyAsAES128(); - return Util.toAes128Key(secret); + if (Secret.SECRET != null) { + return Util.toAes128Key(Secret.SECRET); + } + Jenkins j = Jenkins.getInstanceOrNull(); + if (j != null) { + return j.getSecretKeyAsAES128(); + } else { + return Util.toAes128Key("mock"); + } } static final String MAGIC = "::::MAGIC::::"; diff --git a/core/src/main/java/hudson/util/Secret.java b/core/src/main/java/hudson/util/Secret.java index d53054cec9..af17928d21 100644 --- a/core/src/main/java/hudson/util/Secret.java +++ b/core/src/main/java/hudson/util/Secret.java @@ -31,7 +31,6 @@ import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import jenkins.util.SystemProperties; import java.util.Arrays; -import jenkins.model.Jenkins; import hudson.Util; import jenkins.security.CryptoConfidentialKey; import org.kohsuke.stapler.Stapler; @@ -289,8 +288,10 @@ public final class Secret implements Serializable { private static final String PROVIDER = SystemProperties.getString(Secret.class.getName()+".provider"); /** - * For testing only. Override the secret key so that we can test this class without {@link Jenkins}. + * For testing only. + * @deprecated Normally unnecessary. */ + @Deprecated /*package*/ static String SECRET = null; /** diff --git a/core/src/main/java/jenkins/security/ConfidentialStore.java b/core/src/main/java/jenkins/security/ConfidentialStore.java index e9b79e0581..1062486e08 100644 --- a/core/src/main/java/jenkins/security/ConfidentialStore.java +++ b/core/src/main/java/jenkins/security/ConfidentialStore.java @@ -10,10 +10,14 @@ import org.kohsuke.MetaInfServices; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; import java.io.IOException; +import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Iterator; +import java.util.Map; +import java.util.Random; import java.util.ServiceConfigurationError; import java.util.ServiceLoader; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; @@ -51,6 +55,9 @@ public abstract class ConfidentialStore { */ protected abstract @CheckForNull byte[] load(ConfidentialKey key) throws IOException; + // TODO consider promoting to public, and offering a default implementation of randomBytes (via the usual Util.isOverridden binary compat trick) + abstract SecureRandom secureRandom(); + /** * Works like {@link SecureRandom#nextBytes(byte[])}. * @@ -64,7 +71,10 @@ public abstract class ConfidentialStore { public static @Nonnull ConfidentialStore get() { if (TEST!=null) return TEST.get(); - Jenkins j = Jenkins.get(); + Jenkins j = Jenkins.getInstanceOrNull(); + if (j == null) { + return Mock.INSTANCE; + } Lookup lookup = j.lookup; ConfidentialStore cs = lookup.get(ConfidentialStore.class); if (cs==null) { @@ -91,10 +101,60 @@ public abstract class ConfidentialStore { return cs; } - /** - * Testing only. Used for testing {@link ConfidentialKey} without {@link Jenkins} + /** + * @deprecated No longer needed. */ + @Deprecated /*package*/ static ThreadLocal TEST = null; + static final class Mock extends ConfidentialStore { + + static final Mock INSTANCE = new Mock(); + + private final SecureRandom rand; + + private final Map data = new ConcurrentHashMap<>(); + + Mock() { + // Use a predictable seed to make tests more reproducible. + try { + rand = SecureRandom.getInstance("SHA1PRNG"); + } catch (NoSuchAlgorithmException x) { + throw new AssertionError("https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SecureRandom", x); + } + rand.setSeed(new byte[] {1, 2, 3, 4}); + } + + void clear() { + data.clear(); + } + + @Override + protected void store(ConfidentialKey key, byte[] payload) throws IOException { + LOGGER.fine(() -> "storing " + key.getId() + " " + hudson.Util.getDigestOf(hudson.Util.toHexString(payload))); + data.put(key.getId(), payload); + } + + @Override + protected byte[] load(ConfidentialKey key) throws IOException { + byte[] payload = data.get(key.getId()); + LOGGER.fine(() -> "loading " + key.getId() + " " + (payload != null ? hudson.Util.getDigestOf(hudson.Util.toHexString(payload)) : "null")); + return payload; + } + + @Override + SecureRandom secureRandom() { + return rand; + } + + @Override + public byte[] randomBytes(int size) { + byte[] random = new byte[size]; + rand.nextBytes(random); + return random; + } + + } + private static final Logger LOGGER = Logger.getLogger(ConfidentialStore.class.getName()); } diff --git a/core/src/main/java/jenkins/security/DefaultConfidentialStore.java b/core/src/main/java/jenkins/security/DefaultConfidentialStore.java index 5a16cefc70..4d18357b32 100644 --- a/core/src/main/java/jenkins/security/DefaultConfidentialStore.java +++ b/core/src/main/java/jenkins/security/DefaultConfidentialStore.java @@ -140,6 +140,11 @@ public class DefaultConfidentialStore extends ConfidentialStore { return new File(rootDir, key.getId()); } + @Override + SecureRandom secureRandom() { + return sr; + } + public byte[] randomBytes(int size) { byte[] random = new byte[size]; sr.nextBytes(random); diff --git a/core/src/main/java/jenkins/security/RSAConfidentialKey.java b/core/src/main/java/jenkins/security/RSAConfidentialKey.java index fe30c622de..8c5f64b317 100644 --- a/core/src/main/java/jenkins/security/RSAConfidentialKey.java +++ b/core/src/main/java/jenkins/security/RSAConfidentialKey.java @@ -30,7 +30,6 @@ import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PrivateKey; -import java.security.SecureRandom; import java.security.interfaces.RSAPrivateCrtKey; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; @@ -79,7 +78,7 @@ public abstract class RSAConfidentialKey extends ConfidentialKey { byte[] payload = load(); if (payload == null) { KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA"); - gen.initialize(2048, new SecureRandom()); // going beyond 2048 requires crypto extension + gen.initialize(2048, cs.secureRandom()); // going beyond 2048 requires crypto extension KeyPair keys = gen.generateKeyPair(); priv = (RSAPrivateKey) keys.getPrivate(); pub = (RSAPublicKey) keys.getPublic(); diff --git a/core/src/test/java/hudson/util/MockSecretRule.java b/core/src/test/java/hudson/util/MockSecretRule.java deleted file mode 100644 index f3c11ea381..0000000000 --- a/core/src/test/java/hudson/util/MockSecretRule.java +++ /dev/null @@ -1,33 +0,0 @@ -package hudson.util; - -import hudson.Util; -import org.junit.rules.ExternalResource; - -import java.security.SecureRandom; - -/** - * JUnit rule that cleans that sets a temporary {@link Secret#SECRET} value. - * - * @author Kohsuke Kawaguchi - */ -public class MockSecretRule extends ExternalResource { - - private String value; - - @Override - protected void before() throws Throwable { - byte[] random = new byte[32]; - sr.nextBytes(random); - value = Util.toHexString(random); - Secret.SECRET = value; - } - - @Override - protected void after() { - if (!Secret.SECRET.equals(value)) - throw new IllegalStateException("Someone tinkered with Secret.SECRET"); - Secret.SECRET = null; - } - - private static final SecureRandom sr = new SecureRandom(); -} diff --git a/core/src/test/java/hudson/util/SecretRewriterTest.java b/core/src/test/java/hudson/util/SecretRewriterTest.java index 881f278518..bb7e6adf68 100644 --- a/core/src/test/java/hudson/util/SecretRewriterTest.java +++ b/core/src/test/java/hudson/util/SecretRewriterTest.java @@ -22,9 +22,6 @@ import org.junit.rules.TemporaryFolder; public class SecretRewriterTest { - @Rule - public MockSecretRule mockSecretRule = new MockSecretRule(); - @Rule public ConfidentialStoreRule confidentialStoreRule = new ConfidentialStoreRule(); diff --git a/core/src/test/java/hudson/util/SecretTest.java b/core/src/test/java/hudson/util/SecretTest.java index 32b8d86ceb..19fec48e70 100644 --- a/core/src/test/java/hudson/util/SecretTest.java +++ b/core/src/test/java/hudson/util/SecretTest.java @@ -43,9 +43,6 @@ public class SecretTest { @Rule public ConfidentialStoreRule confidentialStore = new ConfidentialStoreRule(); - @Rule - public MockSecretRule mockSecretRule = new MockSecretRule(); - private static final Pattern ENCRYPTED_VALUE_PATTERN = Pattern.compile("\\{?[A-Za-z0-9+/]+={0,2}}?"); @Test diff --git a/core/src/test/java/jenkins/security/ConfidentialStoreRule.java b/core/src/test/java/jenkins/security/ConfidentialStoreRule.java index e060cfb3f6..5fbc242a7f 100644 --- a/core/src/test/java/jenkins/security/ConfidentialStoreRule.java +++ b/core/src/test/java/jenkins/security/ConfidentialStoreRule.java @@ -2,28 +2,15 @@ package jenkins.security; import org.junit.rules.ExternalResource; -import org.junit.rules.TemporaryFolder; /** - * Test rule that injects a temporary {@link DefaultConfidentialStore} - * @author Kohsuke Kawaguchi + * Test rule that makes {@link ConfidentialStore#get} be reset for each test. */ public class ConfidentialStoreRule extends ExternalResource { - private final TemporaryFolder tmp = new TemporaryFolder(); @Override protected void before() throws Throwable { - tmp.create(); - ConfidentialStore.TEST.set(new DefaultConfidentialStore(tmp.getRoot())); + ConfidentialStore.Mock.INSTANCE.clear(); } - @Override - protected void after() { - ConfidentialStore.TEST.set(null); - tmp.delete(); - } - - static { - ConfidentialStore.TEST = new ThreadLocal<>(); - } } diff --git a/test/src/test/java/jenkins/security/RekeySecretAdminMonitorTest.java b/test/src/test/java/jenkins/security/RekeySecretAdminMonitorTest.java index 4a84630e2d..1b744c2995 100644 --- a/test/src/test/java/jenkins/security/RekeySecretAdminMonitorTest.java +++ b/test/src/test/java/jenkins/security/RekeySecretAdminMonitorTest.java @@ -19,9 +19,12 @@ import javax.inject.Inject; import java.io.File; import java.io.IOException; import java.lang.annotation.Annotation; +import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.regex.Pattern; import java.util.stream.Stream; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; /** * @author Kohsuke Kawaguchi @@ -81,7 +84,7 @@ public class RekeySecretAdminMonitorTest extends HudsonTestCase { private void verifyRewrite(File dir) throws Exception { File xml = new File(dir, "foo.xml"); Pattern pattern = Pattern.compile(""+plain_regex_match+""); - assertTrue(pattern.matcher(FileUtils.readFileToString(xml).trim()).matches()); + MatcherAssert.assertThat(FileUtils.readFileToString(xml, StandardCharsets.UTF_8).trim(), Matchers.matchesRegex(pattern)); } // TODO sometimes fails: "Invalid request submission: {json=[Ljava.lang.String;@2c46358e, .crumb=[Ljava.lang.String;@35661457}" From 0f56520c5be2fd2809cf6e397da407f435c12d24 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Sun, 29 Mar 2020 22:59:03 -0700 Subject: [PATCH 18/19] [maven-release-plugin] prepare release jenkins-2.229 --- bom/pom.xml | 2 +- cli/pom.xml | 2 +- core/pom.xml | 2 +- pom.xml | 4 ++-- test/pom.xml | 2 +- war/pom.xml | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/bom/pom.xml b/bom/pom.xml index 6b93b53647..54f0260ca3 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.229 org.jenkins-ci.main diff --git a/cli/pom.xml b/cli/pom.xml index 77375bbe7a..a2cf04e7bb 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.229 cli diff --git a/core/pom.xml b/core/pom.xml index d9ffd0294c..44d9154593 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.229 jenkins-core diff --git a/pom.xml b/pom.xml index 15ba6b6795..ecf3fc0419 100755 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.229 pom Jenkins main module @@ -60,7 +60,7 @@ THE SOFTWARE. scm:git:git://github.com/jenkinsci/jenkins.git scm:git:ssh://git@github.com/jenkinsci/jenkins.git https://github.com/jenkinsci/jenkins - ${scmTag} + jenkins-2.229 diff --git a/test/pom.xml b/test/pom.xml index 658151f0b4..daf796dad7 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.229 jenkins-test diff --git a/war/pom.xml b/war/pom.xml index 2b5e49dc3c..0743ee136e 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.229 jenkins-war From 653243a07a0568c919779d03e52b1e1df9f7dcae Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Sun, 29 Mar 2020 22:59:09 -0700 Subject: [PATCH 19/19] [maven-release-plugin] prepare for next development iteration --- bom/pom.xml | 2 +- cli/pom.xml | 2 +- core/pom.xml | 2 +- pom.xml | 6 +++--- test/pom.xml | 2 +- war/pom.xml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/bom/pom.xml b/bom/pom.xml index 54f0260ca3..6b93b53647 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.229 + ${revision}${changelist} org.jenkins-ci.main diff --git a/cli/pom.xml b/cli/pom.xml index a2cf04e7bb..77375bbe7a 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main jenkins-parent - 2.229 + ${revision}${changelist} cli diff --git a/core/pom.xml b/core/pom.xml index 44d9154593..d9ffd0294c 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.229 + ${revision}${changelist} jenkins-core diff --git a/pom.xml b/pom.xml index ecf3fc0419..7c77316013 100755 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.229 + ${revision}${changelist} pom Jenkins main module @@ -60,7 +60,7 @@ THE SOFTWARE. scm:git:git://github.com/jenkinsci/jenkins.git scm:git:ssh://git@github.com/jenkinsci/jenkins.git https://github.com/jenkinsci/jenkins - jenkins-2.229 + ${scmTag} @@ -76,7 +76,7 @@ THE SOFTWARE. - 2.229 + 2.230 -SNAPSHOT