From f0c6cf62a7bed1a9a35e8865079e332cdb8f795b Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Thu, 10 Jun 2021 09:29:34 -0700 Subject: [PATCH 01/98] Restricting `SubClassGenerator` --- core/src/main/java/hudson/util/SubClassGenerator.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/main/java/hudson/util/SubClassGenerator.java b/core/src/main/java/hudson/util/SubClassGenerator.java index 1f15e3ba8f..bf80b40bb0 100644 --- a/core/src/main/java/hudson/util/SubClassGenerator.java +++ b/core/src/main/java/hudson/util/SubClassGenerator.java @@ -28,6 +28,8 @@ import jenkins.model.Jenkins; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Type; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.DoNotUse; import java.lang.reflect.Constructor; @@ -43,6 +45,7 @@ import static org.objectweb.asm.Opcodes.RETURN; * @author Kohsuke Kawaguchi */ @Deprecated +@Restricted(DoNotUse.class) public class SubClassGenerator extends ClassLoader { public SubClassGenerator(ClassLoader parent) { super(parent); From ccc3f6cb939deae3f473e8e30444f4971158d0ce Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Fri, 11 Jun 2021 08:18:42 -0700 Subject: [PATCH 02/98] Apply suggestions from code review Co-authored-by: Daniel Beck <1831569+daniel-beck@users.noreply.github.com> --- core/src/main/java/hudson/util/SubClassGenerator.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/hudson/util/SubClassGenerator.java b/core/src/main/java/hudson/util/SubClassGenerator.java index bf80b40bb0..5822e59b10 100644 --- a/core/src/main/java/hudson/util/SubClassGenerator.java +++ b/core/src/main/java/hudson/util/SubClassGenerator.java @@ -46,6 +46,7 @@ import static org.objectweb.asm.Opcodes.RETURN; */ @Deprecated @Restricted(DoNotUse.class) +@RestrictedSince("TODO") public class SubClassGenerator extends ClassLoader { public SubClassGenerator(ClassLoader parent) { super(parent); From 5b30d743067cf3cc6f820a4f48d2af6f37d59225 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Fri, 11 Jun 2021 08:20:38 -0700 Subject: [PATCH 03/98] Imports --- core/src/main/java/hudson/util/SubClassGenerator.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/hudson/util/SubClassGenerator.java b/core/src/main/java/hudson/util/SubClassGenerator.java index 5822e59b10..21add80119 100644 --- a/core/src/main/java/hudson/util/SubClassGenerator.java +++ b/core/src/main/java/hudson/util/SubClassGenerator.java @@ -24,12 +24,13 @@ package hudson.util; import hudson.PluginManager.UberClassLoader; +import hudson.RestrictedSince; import jenkins.model.Jenkins; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.DoNotUse; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Type; -import org.kohsuke.accmod.Restricted; -import org.kohsuke.accmod.restrictions.DoNotUse; import java.lang.reflect.Constructor; From 2e8b3a92b05d6e7142380e2d9defb47130892a58 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Sun, 20 Jun 2021 08:59:24 -0700 Subject: [PATCH 04/98] Removing `SubClassGenerator` --- .../java/hudson/util/SubClassGenerator.java | 105 ------------------ .../hudson/util/SubClassGeneratorTest.java | 61 ---------- 2 files changed, 166 deletions(-) delete mode 100644 core/src/main/java/hudson/util/SubClassGenerator.java delete mode 100644 core/src/test/java/hudson/util/SubClassGeneratorTest.java diff --git a/core/src/main/java/hudson/util/SubClassGenerator.java b/core/src/main/java/hudson/util/SubClassGenerator.java deleted file mode 100644 index 21add80119..0000000000 --- a/core/src/main/java/hudson/util/SubClassGenerator.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2010, InfraDNA, 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. - */ -package hudson.util; - -import hudson.PluginManager.UberClassLoader; -import hudson.RestrictedSince; -import jenkins.model.Jenkins; -import org.kohsuke.accmod.Restricted; -import org.kohsuke.accmod.restrictions.DoNotUse; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Type; - -import java.lang.reflect.Constructor; - -import static org.objectweb.asm.Opcodes.ACC_PUBLIC; -import static org.objectweb.asm.Opcodes.ALOAD; -import static org.objectweb.asm.Opcodes.ILOAD; -import static org.objectweb.asm.Opcodes.INVOKESPECIAL; -import static org.objectweb.asm.Opcodes.RETURN; - -/** - * Generates a new class that just defines constructors into the super types. - * - * @author Kohsuke Kawaguchi - */ -@Deprecated -@Restricted(DoNotUse.class) -@RestrictedSince("TODO") -public class SubClassGenerator extends ClassLoader { - public SubClassGenerator(ClassLoader parent) { - super(parent); - } - - public Class generate(Class base, String name) { - ClassWriter cw = new ClassWriter(0);//? - cw.visit(49, ACC_PUBLIC, name.replace('.', '/'), null, - Type.getInternalName(base),null); - - for (Constructor c : base.getDeclaredConstructors()) { - Class[] et = c.getExceptionTypes(); - String[] exceptions = new String[et.length]; - for (int i = 0; i < et.length; i++) - exceptions[i] = Type.getInternalName(et[i]); - - String methodDescriptor = getMethodDescriptor(c); - MethodVisitor m = cw.visitMethod(c.getModifiers(), "", methodDescriptor, null, exceptions); - m.visitCode(); - - int index=1; - m.visitVarInsn(ALOAD,0); - for (Class param : c.getParameterTypes()) { - Type t = Type.getType(param); - m.visitVarInsn(t.getOpcode(ILOAD), index); - index += t.getSize(); - } - m.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(base), "", methodDescriptor); - m.visitInsn(RETURN); - m.visitMaxs(index,index); - m.visitEnd(); - } - - cw.visitEnd(); - byte[] image = cw.toByteArray(); - - Class c = defineClass(name, image, 0, image.length).asSubclass(base); - - Jenkins h = Jenkins.getInstanceOrNull(); - if (h!=null) // null only during tests. - ((UberClassLoader)h.pluginManager.uberClassLoader).addNamedClass(name,c); // can't change the field type as it breaks binary compatibility - - return c; - } - - private String getMethodDescriptor(Constructor c) { - StringBuilder buf = new StringBuilder(); - buf.append('('); - for (Class p : c.getParameterTypes()) - buf.append(Type.getDescriptor(p)); - - buf.append(")V"); - return buf.toString(); - } -} diff --git a/core/src/test/java/hudson/util/SubClassGeneratorTest.java b/core/src/test/java/hudson/util/SubClassGeneratorTest.java deleted file mode 100644 index 1cc590bb6d..0000000000 --- a/core/src/test/java/hudson/util/SubClassGeneratorTest.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2010, InfraDNA, 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. - */ -package hudson.util; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; - -/** - * @author Kohsuke Kawaguchi - */ -public class SubClassGeneratorTest { - - public static class Foo { - String s; - double x; - int y; - public Foo() {} - public Foo(String s) {this.s=s;} - public Foo(double x, int y) { - this.x = x; - this.y = y; - } - } - - @Test - public void foo() throws Exception { - Class c = new SubClassGenerator(getClass().getClassLoader()).generate(Foo.class, "12345"); - assertEquals("12345",c.getName()); - - c.getDeclaredConstructor().newInstance(); - - Foo f = c.getConstructor(String.class).newInstance("aaa"); - assertEquals("aaa",f.s); - - f = c.getConstructor(double.class,int.class).newInstance(1.0,3); - assertEquals(1.0,f.x,0); - assertEquals(3,f.y); - } -} From 7ff32dcf3b3c03663ef7d6d625ea835623c99289 Mon Sep 17 00:00:00 2001 From: offa Date: Thu, 1 Jul 2021 17:14:43 +0200 Subject: [PATCH 05/98] Exception tests modernized and some code cleanup. --- core/src/test/java/hudson/FilePathTest.java | 81 +++++++++---------- .../test/java/hudson/PluginManagerTest.java | 15 ++-- .../test/java/hudson/PluginWrapperTest.java | 59 +++++--------- .../src/test/java/hudson/model/ItemsTest.java | 33 +++----- .../java/hudson/model/UpdateCenterTest.java | 67 ++++++--------- .../java/hudson/scheduler/CronTabTest.java | 24 ++---- 6 files changed, 99 insertions(+), 180 deletions(-) diff --git a/core/src/test/java/hudson/FilePathTest.java b/core/src/test/java/hudson/FilePathTest.java index 146198acd1..af76be814c 100644 --- a/core/src/test/java/hudson/FilePathTest.java +++ b/core/src/test/java/hudson/FilePathTest.java @@ -164,49 +164,44 @@ public class FilePathTest { private List> whenFileIsCopied100TimesConcurrently(final File file) throws InterruptedException { List> r = new ArrayList<>(); for (int i=0; i<100; i++) { - r.add(new Callable() { - @Override - public Integer call() throws Exception { - class Sink extends OutputStream { - private Exception closed; - private final AtomicInteger count = new AtomicInteger(); + r.add(() -> { + class Sink extends OutputStream { + private Exception closed; + private final AtomicInteger count = new AtomicInteger(); - private void checkNotClosed() throws IOException { - if (closed != null) - throw new IOException(closed); - } - - @Override - public void write(int b) throws IOException { - count.incrementAndGet(); - checkNotClosed(); - } - - @Override - public void write(byte[] b) throws IOException { - count.addAndGet(b.length); - checkNotClosed(); - } - - @Override - public void write(byte[] b, int off, int len) throws IOException { - count.addAndGet(len); - checkNotClosed(); - } - - @Override - public void close() throws IOException { - closed = new Exception(); - //if (size!=count) - // fail(); - } + private void checkNotClosed() throws IOException { + if (closed != null) + throw new IOException(closed); } - FilePath f = new FilePath(channels.french, file.getPath()); - Sink sink = new Sink(); - f.copyTo(sink); - return sink.count.get(); + @Override + public void write(int b) throws IOException { + count.incrementAndGet(); + checkNotClosed(); + } + + @Override + public void write(byte[] b) throws IOException { + count.addAndGet(b.length); + checkNotClosed(); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + count.addAndGet(len); + checkNotClosed(); + } + + @Override + public void close() throws IOException { + closed = new Exception(); + } } + + FilePath f = new FilePath(channels.french, file.getPath()); + Sink sink = new Sink(); + f.copyTo(sink); + return sink.count.get(); }); } @@ -789,12 +784,8 @@ public class FilePathTest { File f = temp.newFolder("folder"); FilePath fp = new FilePath(f); int invalidMode = 01770; // Full permissions for owner and group plus sticky bit. - try { - chmodAndMode(fp, invalidMode); - fail("Setting sticky bit should fail"); - } catch (IOException e) { - assertEquals("Invalid mode: " + invalidMode, e.getMessage()); - } + final IOException e = assertThrows("Setting sticky bit should fail", IOException.class, () -> chmodAndMode(fp, invalidMode)); + assertEquals("Invalid mode: " + invalidMode, e.getMessage()); } private int chmodAndMode(FilePath path, int mode) throws Exception { diff --git a/core/src/test/java/hudson/PluginManagerTest.java b/core/src/test/java/hudson/PluginManagerTest.java index d4080da9af..5d460ae7f0 100644 --- a/core/src/test/java/hudson/PluginManagerTest.java +++ b/core/src/test/java/hudson/PluginManagerTest.java @@ -48,7 +48,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.IsInstanceOf.instanceOf; import static org.hamcrest.core.StringContains.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * Tests of {@link PluginManager}. @@ -81,14 +81,11 @@ public class PluginManagerTest { "\n"; PluginManager pluginManager = new LocalPluginManager(Util.createTempDir()); - try { - pluginManager.parseRequestedPlugins(new StringInputStream(evilXML)); - fail("XML contains an external entity, but no exception was thrown."); - } - catch (IOException ex) { - assertThat(ex.getCause(), instanceOf(SAXException.class)); - assertThat(ex.getCause().getMessage(), containsString("Refusing to resolve entity with publicId(null) and systemId (file:///)")); - } + final IOException ex = assertThrows(IOException.class, + () -> pluginManager.parseRequestedPlugins(new StringInputStream(evilXML)), + "XML contains an external entity, but no exception was thrown."); + assertThat(ex.getCause(), instanceOf(SAXException.class)); + assertThat(ex.getCause().getMessage(), containsString("Refusing to resolve entity with publicId(null) and systemId (file:///)")); } @Test diff --git a/core/src/test/java/hudson/PluginWrapperTest.java b/core/src/test/java/hudson/PluginWrapperTest.java index 5fc7795de4..58e896cfcf 100644 --- a/core/src/test/java/hudson/PluginWrapperTest.java +++ b/core/src/test/java/hudson/PluginWrapperTest.java @@ -13,7 +13,6 @@ import jenkins.model.Jenkins; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.jvnet.hudson.test.Issue; @@ -22,8 +21,8 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -65,47 +64,35 @@ public class PluginWrapperTest { @Test public void jenkinsCoreTooOld() { PluginWrapper pw = pluginWrapper("fake").requiredCoreVersion("3.0").buildLoaded(); - try { - pw.resolvePluginDependencies(); - fail(); - } catch (IOException ex) { - assertContains(ex, "Failed to load: fake (42)", "Jenkins (3.0) or higher required"); - } + + final IOException ex = assertThrows(IOException.class, pw::resolvePluginDependencies); + assertContains(ex, "Failed to load: fake (42)", "Jenkins (3.0) or higher required"); } @Test public void dependencyNotInstalled() { PluginWrapper pw = pluginWrapper("dependee").deps("dependency:42").buildLoaded(); - try { - pw.resolvePluginDependencies(); - fail(); - } catch (IOException ex) { - assertContains(ex, "Failed to load: dependee (42)", "Plugin is missing: dependency (42)"); - } + + final IOException ex = assertThrows(IOException.class, pw::resolvePluginDependencies); + assertContains(ex, "Failed to load: dependee (42)", "Plugin is missing: dependency (42)"); } @Test public void dependencyOutdated() { pluginWrapper("dependency").version("3").buildLoaded(); PluginWrapper pw = pluginWrapper("dependee").deps("dependency:5").buildLoaded(); - try { - pw.resolvePluginDependencies(); - fail(); - } catch (IOException ex) { - assertContains(ex, "Failed to load: dependee (42)", "Update required: dependency (3) to be updated to 5 or higher"); - } + + final IOException ex = assertThrows(IOException.class, pw::resolvePluginDependencies); + assertContains(ex, "Failed to load: dependee (42)", "Update required: dependency (3) to be updated to 5 or higher"); } @Test public void dependencyFailedToLoad() { pluginWrapper("dependency").version("5").buildFailed(); PluginWrapper pw = pluginWrapper("dependee").deps("dependency:3").buildLoaded(); - try { - pw.resolvePluginDependencies(); - fail(); - } catch (IOException ex) { - assertContains(ex, "Failed to load: dependee (42)", "Failed to load: dependency (5)"); - } + + final IOException ex = assertThrows(IOException.class, pw::resolvePluginDependencies); + assertContains(ex, "Failed to load: dependee (42)", "Failed to load: dependency (5)"); } private void assertContains(Throwable ex, String... patterns) { @@ -123,18 +110,15 @@ public class PluginWrapperTest { private final HashMap plugins = new HashMap<>(); private final PluginManager pm = mock(PluginManager.class); { - when(pm.getPlugin(any(String.class))).thenAnswer(new Answer() { - @Override public PluginWrapper answer(InvocationOnMock invocation) throws Throwable { - return plugins.get(invocation.getArguments()[0]); - } - }); + when(pm.getPlugin(any(String.class))).thenAnswer((Answer) invocation -> plugins.get(invocation.getArguments()[0])); } + private final class PluginWrapperBuilder { - private String name; + private final String name; private String version = "42"; private String requiredCoreVersion = "1.0"; - private List deps = new ArrayList<>(); - private List optDeps = new ArrayList<>(); + private final List deps = new ArrayList<>(); + private final List optDeps = new ArrayList<>(); private PluginWrapperBuilder(String name) { this.name = name; @@ -157,13 +141,6 @@ public class PluginWrapperTest { return this; } - public PluginWrapperBuilder optDeps(String... optDeps) { - for (String dep: optDeps) { - this.optDeps.add(new PluginWrapper.Dependency(dep)); - } - return this; - } - private PluginWrapper buildLoaded() { PluginWrapper pw = build(); plugins.put(name, pw); diff --git a/core/src/test/java/hudson/model/ItemsTest.java b/core/src/test/java/hudson/model/ItemsTest.java index 2a7cef9f5d..3bdedd259c 100644 --- a/core/src/test/java/hudson/model/ItemsTest.java +++ b/core/src/test/java/hudson/model/ItemsTest.java @@ -5,7 +5,7 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.Assert.assertThrows; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -14,9 +14,9 @@ import static org.mockito.Mockito.when; */ public class ItemsTest { - private static ItemGroup root; - private static ItemGroup foo; - private static ItemGroup foo_bar; + private static ItemGroup root; + private static ItemGroup foo; + private static ItemGroup foo_bar; @BeforeClass public static void itemGroups() { @@ -28,7 +28,6 @@ public class ItemsTest { foo_bar = mock(ItemGroup.class); when(foo_bar.getFullName()).thenReturn("foo/bar"); - } @Test @@ -43,26 +42,14 @@ public class ItemsTest { assertEquals("foo/bar/baz/qux", Items.getCanonicalName(foo_bar, "baz/qux")); assertEquals("foo/baz/qux", Items.getCanonicalName(foo_bar, "../baz/qux")); - try { - Items.getCanonicalName(root, ".."); - fail(); - } catch (IllegalArgumentException ex) { - assertEquals("Illegal relative path '..' within context ''", ex.getMessage()); - } + final IllegalArgumentException ex0 = assertThrows(IllegalArgumentException.class, () -> Items.getCanonicalName(root, "..")); + assertEquals("Illegal relative path '..' within context ''", ex0.getMessage()); - try { - Items.getCanonicalName(foo, "../.."); - fail(); - } catch (IllegalArgumentException ex) { - assertEquals("Illegal relative path '../..' within context 'foo'", ex.getMessage()); - } + final IllegalArgumentException ex1 = assertThrows(IllegalArgumentException.class, () -> Items.getCanonicalName(foo, "../..")); + assertEquals("Illegal relative path '../..' within context 'foo'", ex1.getMessage()); - try { - Items.getCanonicalName(root, "foo/../.."); - fail(); - } catch (IllegalArgumentException ex) { - assertEquals("Illegal relative path 'foo/../..' within context ''", ex.getMessage()); - } + final IllegalArgumentException ex2 = assertThrows(IllegalArgumentException.class, () -> Items.getCanonicalName(root, "foo/../..")); + assertEquals("Illegal relative path 'foo/../..' within context ''", ex2.getMessage()); } @Test diff --git a/core/src/test/java/hudson/model/UpdateCenterTest.java b/core/src/test/java/hudson/model/UpdateCenterTest.java index 2a84fab8d5..8d4ff97f67 100644 --- a/core/src/test/java/hudson/model/UpdateCenterTest.java +++ b/core/src/test/java/hudson/model/UpdateCenterTest.java @@ -10,8 +10,8 @@ import java.util.Locale; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertEquals; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; public class UpdateCenterTest { @@ -52,12 +52,10 @@ public class UpdateCenterTest { @Test public void noChecksums() { - try { - UpdateCenter.verifyChecksums(new MockDownloadJob(null, null, null), buildEntryWithExpectedChecksums(null, null, null), new File("example")); - fail(); - } catch (IOException ex) { - assertEquals("Unable to confirm integrity of downloaded file, refusing installation", ex.getMessage()); - } + final IOException ex = assertThrows(IOException.class, + () -> UpdateCenter.verifyChecksums(new MockDownloadJob(null, null, null), + buildEntryWithExpectedChecksums(null, null, null), new File("example"))); + assertEquals("Unable to confirm integrity of downloaded file, refusing installation", ex.getMessage()); } @Test @@ -69,14 +67,10 @@ public class UpdateCenterTest { @Test public void sha1Mismatch() { - try { - UpdateCenter.verifyChecksums( - new MockDownloadJob(EMPTY_SHA1.replace('k', 'f'), null, null), - buildEntryWithExpectedChecksums(EMPTY_SHA1, null, null), new File("example")); - fail(); - } catch (IOException ex) { - assertTrue(ex.getMessage().contains("does not match expected SHA-1, expected '2jmj7l5rSw0yVb/vlWAYkK/YBwk=', actual '2jmj7l5rSw0yVb/vlWAYfK/YBwf='")); - } + final IOException ex = assertThrows(IOException.class, () -> UpdateCenter.verifyChecksums( + new MockDownloadJob(EMPTY_SHA1.replace('k', 'f'), null, null), + buildEntryWithExpectedChecksums(EMPTY_SHA1, null, null), new File("example"))); + assertTrue(ex.getMessage().contains("does not match expected SHA-1, expected '2jmj7l5rSw0yVb/vlWAYkK/YBwk=', actual '2jmj7l5rSw0yVb/vlWAYfK/YBwf='")); } @Test @@ -95,45 +89,32 @@ public class UpdateCenterTest { @Test public void sha1DoesNotIgnoreCase() { - try { - UpdateCenter.verifyChecksums( - new MockDownloadJob(EMPTY_SHA1, EMPTY_SHA256, EMPTY_SHA512), - buildEntryWithExpectedChecksums(EMPTY_SHA1.toUpperCase(Locale.US), null, null), new File("example")); - fail(); - } catch (Exception e) { - assertTrue(e.getMessage().contains("does not match expected SHA-1, expected '2JMJ7L5RSW0YVB/VLWAYKK/YBWK=', actual '2jmj7l5rSw0yVb/vlWAYkK/YBwk='")); - } - + final Exception ex = assertThrows(Exception.class, () -> UpdateCenter.verifyChecksums( + new MockDownloadJob(EMPTY_SHA1, EMPTY_SHA256, EMPTY_SHA512), + buildEntryWithExpectedChecksums(EMPTY_SHA1.toUpperCase(Locale.US), null, null), new File("example"))); + assertTrue(ex.getMessage().contains("does not match expected SHA-1, expected '2JMJ7L5RSW0YVB/VLWAYKK/YBWK=', actual '2jmj7l5rSw0yVb/vlWAYkK/YBwk='")); } @Test public void noOverlapForComputedAndProvidedChecksums() { - try { - UpdateCenter.verifyChecksums( - new MockDownloadJob(EMPTY_SHA1, EMPTY_SHA256, null), - buildEntryWithExpectedChecksums(null, null, EMPTY_SHA512), new File("example")); - fail(); - } catch (Exception e) { - assertEquals("Unable to confirm integrity of downloaded file, refusing installation", e.getMessage()); - } + final Exception ex = assertThrows(Exception.class, () -> UpdateCenter.verifyChecksums( + new MockDownloadJob(EMPTY_SHA1, EMPTY_SHA256, null), + buildEntryWithExpectedChecksums(null, null, EMPTY_SHA512), new File("example"))); + assertEquals("Unable to confirm integrity of downloaded file, refusing installation", ex.getMessage()); } @Test public void noOverlapForComputedAndProvidedChecksumsForSpecIncompliantJVM() { - try { - UpdateCenter.verifyChecksums( - new MockDownloadJob(EMPTY_SHA1, null, null), - buildEntryWithExpectedChecksums(null, EMPTY_SHA256, EMPTY_SHA512), new File("example")); - fail(); - } catch (Exception e) { - assertEquals("Unable to confirm integrity of downloaded file, refusing installation", e.getMessage()); - } + final Exception ex = assertThrows(Exception.class, () -> UpdateCenter.verifyChecksums( + new MockDownloadJob(EMPTY_SHA1, null, null), + buildEntryWithExpectedChecksums(null, EMPTY_SHA256, EMPTY_SHA512), new File("example"))); + assertEquals("Unable to confirm integrity of downloaded file, refusing installation", ex.getMessage()); } - private static String EMPTY_SHA1 = "2jmj7l5rSw0yVb/vlWAYkK/YBwk="; - private static String EMPTY_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; - private static String EMPTY_SHA512 = "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"; + private static final String EMPTY_SHA1 = "2jmj7l5rSw0yVb/vlWAYkK/YBwk="; + private static final String EMPTY_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; + private static final String EMPTY_SHA512 = "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"; private static UpdateSite.Entry buildEntryWithExpectedChecksums(String expectedSHA1, String expectedSHA256, String expectedSHA512) { JSONObject o = new JSONObject(); diff --git a/core/src/test/java/hudson/scheduler/CronTabTest.java b/core/src/test/java/hudson/scheduler/CronTabTest.java index c812b4c651..958b9807ad 100644 --- a/core/src/test/java/hudson/scheduler/CronTabTest.java +++ b/core/src/test/java/hudson/scheduler/CronTabTest.java @@ -33,7 +33,7 @@ import java.util.TimeZone; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; -import static org.junit.Assert.fail; +import static org.junit.Assert.assertThrows; import org.junit.Test; import org.jvnet.hudson.test.Issue; @@ -265,12 +265,8 @@ public class CronTabTest { compare(new GregorianCalendar(2013, 2, 21, 0, 2), new CronTab("H(0-15)/3 * * * *", Hash.from("junk")).ceil(new GregorianCalendar(2013, 2, 21, 0, 0))); compare(new GregorianCalendar(2013, 2, 21, 0, 2), new CronTab("H(0-3)/4 * * * *", Hash.from("junk")).ceil(new GregorianCalendar(2013, 2, 21, 0, 0))); compare(new GregorianCalendar(2013, 2, 21, 1, 2), new CronTab("H(0-3)/4 * * * *", Hash.from("junk")).ceil(new GregorianCalendar(2013, 2, 21, 0, 5))); - try { - compare(new GregorianCalendar(2013, 2, 21, 0, 0), new CronTab("H(0-3)/15 * * * *", Hash.from("junk")).ceil(new GregorianCalendar(2013, 2, 21, 0, 0))); - fail(); - } catch (ANTLRException x) { - // good - } + + assertThrows(ANTLRException.class, () -> compare(new GregorianCalendar(2013, 2, 21, 0, 0), new CronTab("H(0-3)/15 * * * *", Hash.from("junk")).ceil(new GregorianCalendar(2013, 2, 21, 0, 0)))); } @Test public void repeatedHash() throws Exception { @@ -289,21 +285,11 @@ public class CronTabTest { } @Test public void rangeBoundsCheckFailHour() { - try { - new CronTab("H H(12-24) * * *"); - fail(); - } catch (ANTLRException e) { - // ok - } + assertThrows(ANTLRException.class, () -> new CronTab("H H(12-24) * * *")); } @Test public void rangeBoundsCheckFailMinute() { - try { - new CronTab("H(33-66) * * * *"); - fail(); - } catch (ANTLRException e) { - // ok - } + assertThrows(ANTLRException.class, () -> new CronTab("H(33-66) * * * *")); } @Issue("JENKINS-9283") From 7143bf7c240f9bd4d10f05449e7fcda8f1e9c406 Mon Sep 17 00:00:00 2001 From: offa Date: Thu, 1 Jul 2021 17:15:05 +0200 Subject: [PATCH 06/98] Commented out code removed. --- .../java/hudson/ClassicPluginStrategy.java | 9 -------- core/src/main/java/hudson/Proc.java | 11 +--------- .../hudson/diagnosis/MemoryUsageMonitor.java | 8 ------- .../hudson/model/BuildTimelineWidget.java | 5 +---- .../main/java/hudson/model/Fingerprint.java | 19 ---------------- core/src/main/java/hudson/model/Job.java | 2 -- .../main/java/hudson/model/PageDecorator.java | 3 --- core/src/main/java/hudson/model/Run.java | 22 ------------------- core/src/main/java/hudson/model/Slave.java | 3 --- core/src/main/java/hudson/model/TreeView.java | 8 ------- .../hudson/model/queue/MappingWorksheet.java | 6 ----- .../AbstractNodeMonitorDescriptor.java | 2 -- .../node_monitors/ResponseTimeMonitor.java | 6 ----- .../security/BasicAuthenticationFilter.java | 14 ------------ .../java/hudson/util/ChunkedInputStream.java | 19 ---------------- .../main/java/hudson/util/ProcessTree.java | 19 ---------------- .../LastGrantedAuthoritiesProperty.java | 22 ------------------- .../jenkins/RemotingJarSignatureTest.java | 7 ++---- 18 files changed, 4 insertions(+), 181 deletions(-) diff --git a/core/src/main/java/hudson/ClassicPluginStrategy.java b/core/src/main/java/hudson/ClassicPluginStrategy.java index 19f04f0168..fd15af1a76 100644 --- a/core/src/main/java/hudson/ClassicPluginStrategy.java +++ b/core/src/main/java/hudson/ClassicPluginStrategy.java @@ -621,15 +621,6 @@ public class ClassicPluginStrategy implements PluginStrategy { return transientDependencies; } -// public List getDependencyPluginWrappers() { -// List r = new ArrayList(); -// for (Dependency d : dependencies) { -// PluginWrapper w = pluginManager.getPlugin(d.shortName); -// if (w!=null) r.add(w); -// } -// return r; -// } - @Override protected Class findClass(String name) throws ClassNotFoundException { if (PluginManager.FAST_LOOKUP) { diff --git a/core/src/main/java/hudson/Proc.java b/core/src/main/java/hudson/Proc.java index e1d0766b80..8f3d0f8170 100644 --- a/core/src/main/java/hudson/Proc.java +++ b/core/src/main/java/hudson/Proc.java @@ -345,16 +345,7 @@ public abstract class Proc { // it could be either because another thread is blocking on read, or // it could be a bug in Windows JVM. Who knows. // so I'm abandoning the idea of closing the stream -// try { -// proc.getInputStream().close(); -// } catch (IOException x) { -// LOGGER.log(Level.FINE,"stdin termination failed",x); -// } -// try { -// proc.getErrorStream().close(); -// } catch (IOException x) { -// LOGGER.log(Level.FINE,"stderr termination failed",x); -// } + out.write(msg.getBytes()); out.write('\n'); } diff --git a/core/src/main/java/hudson/diagnosis/MemoryUsageMonitor.java b/core/src/main/java/hudson/diagnosis/MemoryUsageMonitor.java index c3499035a7..aa8d781032 100644 --- a/core/src/main/java/hudson/diagnosis/MemoryUsageMonitor.java +++ b/core/src/main/java/hudson/diagnosis/MemoryUsageMonitor.java @@ -76,27 +76,19 @@ public final class MemoryUsageMonitor extends PeriodicWork { private void update() { long used = 0; long max = 0; -// long cur = 0; for (MemoryPoolMXBean pool : pools) { MemoryUsage usage = pool.getCollectionUsage(); if(usage==null) continue; // not available used += usage.getUsed(); max += usage.getMax(); - -// usage = pool.getUsage(); -// if(usage==null) continue; // not available -// cur += usage.getUsed(); } // B -> KB used /= 1024; max /= 1024; -// cur /= 1024; this.used.update(used); this.max.update(max); -// -// return String.format("%d/%d/%d (%d%%)",used,cur,max,used*100/max); } /** diff --git a/core/src/main/java/hudson/model/BuildTimelineWidget.java b/core/src/main/java/hudson/model/BuildTimelineWidget.java index 717052e5bf..66b5f3c1d9 100644 --- a/core/src/main/java/hudson/model/BuildTimelineWidget.java +++ b/core/src/main/java/hudson/model/BuildTimelineWidget.java @@ -61,16 +61,13 @@ public class BuildTimelineWidget { public TimelineEventList doData(StaplerRequest req, @QueryParameter long min, @QueryParameter long max) throws IOException { TimelineEventList result = new TimelineEventList(); - for (Run r : builds.byTimestamp(min,max)) { + for (Run r : builds.byTimestamp(min,max)) { Event e = new Event(); e.start = new Date(r.getStartTimeInMillis()); e.end = new Date(r.getStartTimeInMillis()+r.getDuration()); // due to SimileAjax.HTML.deEntify (in simile-ajax-bundle.js), "<" are transformed back to "<", but not the "<"; // to protect against XSS e.title = Util.escape(r.getFullDisplayName()).replace("<", "<"); - // what to put in the description? - // e.description = "Longish description of event "+r.getFullDisplayName(); - // e.durationEvent = true; e.link = req.getContextPath()+'/'+r.getUrl(); BallColor c = r.getIconColor(); e.color = String.format("#%06X",c.getBaseColor().darker().getRGB()&0xFFFFFF); diff --git a/core/src/main/java/hudson/model/Fingerprint.java b/core/src/main/java/hudson/model/Fingerprint.java index e31483e070..357232edf2 100644 --- a/core/src/main/java/hudson/model/Fingerprint.java +++ b/core/src/main/java/hudson/model/Fingerprint.java @@ -374,25 +374,6 @@ public class Fingerprint implements ModelObject, Saveable { }; } -// /** -// * List up builds. -// */ -// public ,R extends Run> Iterable listBuilds(final J job) { -// return new Iterable() { -// public Iterator iterator() { -// return new Iterators.FilterIterator(new AdaptedIterator(listNumbers().iterator()) { -// protected R adapt(Integer n) { -// return job.getBuildByNumber(n); -// } -// }) { -// protected boolean filter(R r) { -// return r!=null; -// } -// }; -// } -// }; -// } - /** * List all numbers in this range set in the descending order. */ diff --git a/core/src/main/java/hudson/model/Job.java b/core/src/main/java/hudson/model/Job.java index 4c038af5cd..04efbb5f7d 100644 --- a/core/src/main/java/hudson/model/Job.java +++ b/core/src/main/java/hudson/model/Job.java @@ -1499,8 +1499,6 @@ public abstract class Job, RunT extends Run implements super(self()); } - // this will never work because Descriptor and Describable are the same thing. -// protected PageDecorator() { -// } @Override public final Descriptor getDescriptor() { diff --git a/core/src/main/java/hudson/model/Run.java b/core/src/main/java/hudson/model/Run.java index 8a1f743402..58c842dd8f 100644 --- a/core/src/main/java/hudson/model/Run.java +++ b/core/src/main/java/hudson/model/Run.java @@ -1331,28 +1331,6 @@ public abstract class Run ,RunT extends Run names = new HashSet(); -// for (String[] token : tokens) { -// if(!names.add(combineLast(token,n))) -// continue OUTER; // collision. Increase n and try again -// } -// -// // this n successfully disambiguates -// for (int i = 0; i < tokens.length; i++) { -// String[] token = tokens[i]; -// get(i).displayPath = combineLast(token,n); -// } -// return; -// } - -// // it's impossible to get here, as that means -// // we have the same artifacts archived twice, but be defensive -// for (Artifact a : this) -// a.displayPath = a.relativePath; } /** diff --git a/core/src/main/java/hudson/model/Slave.java b/core/src/main/java/hudson/model/Slave.java index 17079a9f2c..7818fa7af3 100644 --- a/core/src/main/java/hudson/model/Slave.java +++ b/core/src/main/java/hudson/model/Slave.java @@ -206,9 +206,6 @@ public abstract class Slave extends Node implements Serializable { if (name.equals("")) throw new FormException(Messages.Slave_InvalidConfig_NoName(), null); -// if (remoteFS.equals("")) -// throw new FormException(Messages.Slave_InvalidConfig_NoRemoteDir(name), null); - if (this.numExecutors<=0) throw new FormException(Messages.Slave_InvalidConfig_Executors(name), null); } diff --git a/core/src/main/java/hudson/model/TreeView.java b/core/src/main/java/hudson/model/TreeView.java index 00191fc97a..8c9543f15a 100644 --- a/core/src/main/java/hudson/model/TreeView.java +++ b/core/src/main/java/hudson/model/TreeView.java @@ -97,19 +97,11 @@ public class TreeView extends View implements ViewGroup { @Override public synchronized List getItems() { return Jenkins.get().getItems(); -// List items = new ArrayList(jobNames.size()); -// for (String name : jobNames) { -// TopLevelItem item = Hudson.getInstance().getItem(name); -// if(item!=null) -// items.add(item); -// } -// return items; } @Override public boolean contains(TopLevelItem item) { return true; -// return jobNames.contains(item.getName()); } @Override diff --git a/core/src/main/java/hudson/model/queue/MappingWorksheet.java b/core/src/main/java/hudson/model/queue/MappingWorksheet.java index cced8c0ac8..d60809b2d0 100644 --- a/core/src/main/java/hudson/model/queue/MappingWorksheet.java +++ b/core/src/main/java/hudson/model/queue/MappingWorksheet.java @@ -173,12 +173,6 @@ public class MappingWorksheet { public class WorkChunk extends ReadOnlyList { public final int index; - // the main should be always at position 0 -// /** -// * This chunk includes {@linkplain WorkUnit#isMainWork() the main work unit}. -// */ -// public final boolean isMain; - /** * If this task needs to be run on a node with a particular label, * return that {@link Label}. Otherwise null, indicating diff --git a/core/src/main/java/hudson/node_monitors/AbstractNodeMonitorDescriptor.java b/core/src/main/java/hudson/node_monitors/AbstractNodeMonitorDescriptor.java index 3861bfca21..e773688839 100644 --- a/core/src/main/java/hudson/node_monitors/AbstractNodeMonitorDescriptor.java +++ b/core/src/main/java/hudson/node_monitors/AbstractNodeMonitorDescriptor.java @@ -194,8 +194,6 @@ public abstract class AbstractNodeMonitorDescriptor extends Descriptor0) buf.append(','); -// buf.append(l); -// } -// return buf.toString(); int fc = failureCount(); if(fc>0) return Messages.ResponseTimeMonitor_TimeOut(fc); diff --git a/core/src/main/java/hudson/security/BasicAuthenticationFilter.java b/core/src/main/java/hudson/security/BasicAuthenticationFilter.java index 836ddd5f1d..d481260c3a 100644 --- a/core/src/main/java/hudson/security/BasicAuthenticationFilter.java +++ b/core/src/main/java/hudson/security/BasicAuthenticationFilter.java @@ -178,20 +178,6 @@ public class BasicAuthenticationFilter implements Filter { rsp.setHeader("Location",path); } - //public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { - // HttpServletRequest req = (HttpServletRequest) request; - // String authorization = req.getHeader("Authorization"); - // - // String path = req.getServletPath(); - // if(authorization==null || req.getUserPrincipal()!=null || path.startsWith("/secured/")) { - // chain.doFilter(request,response); - // } else { - // if(req.getQueryString()!=null) - // path += req.getQueryString(); - // ((HttpServletResponse)response).sendRedirect(req.getContextPath()+"/secured"+path); - // } - //} - @Override public void destroy() { } diff --git a/core/src/main/java/hudson/util/ChunkedInputStream.java b/core/src/main/java/hudson/util/ChunkedInputStream.java index 491e8885fa..b934fd7af9 100644 --- a/core/src/main/java/hudson/util/ChunkedInputStream.java +++ b/core/src/main/java/hudson/util/ChunkedInputStream.java @@ -284,25 +284,6 @@ public class ChunkedInputStream extends InputStream { private void parseTrailerHeaders() throws IOException { // I feel lazy. No trailing header support readCRLF(); - -// Header[] footers = null; -// try { -// String charset = "US-ASCII"; -// if (this.method != null) { -// charset = this.method.getParams().getHttpElementCharset(); -// } -// footers = HttpParser.parseHeaders(in, charset); -// } catch(HttpException e) { -// LOG.error("Error parsing trailer headers", e); -// IOException ioe = new IOException(e.getMessage()); -// ExceptionUtil.initCause(ioe, e); -// throw ioe; -// } -// if (this.method != null) { -// for (int i = 0; i < footers.length; i++) { -// this.method.addResponseFooter(footers[i]); -// } -// } } /** diff --git a/core/src/main/java/hudson/util/ProcessTree.java b/core/src/main/java/hudson/util/ProcessTree.java index 8bcaf49a5f..29435b931f 100644 --- a/core/src/main/java/hudson/util/ProcessTree.java +++ b/core/src/main/java/hudson/util/ProcessTree.java @@ -1691,9 +1691,6 @@ public abstract class ProcessTree implements Iterable, IProcessTree, for( int base=0; base, IProcessTree, return new Remote(this, getChannelForSerialization()); } -// public static void main(String[] args) { -// // dump everything -// LOGGER.setLevel(Level.ALL); -// ConsoleHandler h = new ConsoleHandler(); -// h.setLevel(Level.ALL); -// LOGGER.addHandler(h); -// -// Solaris killer = (Solaris)get(); -// Solaris.SolarisSystem s = killer.createSystem(); -// Solaris.SolarisProcess p = s.get(Integer.parseInt(args[0])); -// System.out.println(p.getEnvVars()); -// -// if(args.length==2) -// p.kill(); -// } - /* On MacOS X, there's no procfs instead you'd do it with the sysctl diff --git a/core/src/main/java/jenkins/security/LastGrantedAuthoritiesProperty.java b/core/src/main/java/jenkins/security/LastGrantedAuthoritiesProperty.java index da348b768f..cfe795746a 100644 --- a/core/src/main/java/jenkins/security/LastGrantedAuthoritiesProperty.java +++ b/core/src/main/java/jenkins/security/LastGrantedAuthoritiesProperty.java @@ -136,28 +136,6 @@ public class LastGrantedAuthoritiesProperty extends UserProperty { // while this initially seemed like a good idea to avoid allowing wrong impersonation for too long, // doing this means a malicious user can break the impersonation capability // just by failing to login. See ApiTokenFilter that does the following, which seems better: - /* - try { - Jenkins.get().getSecurityRealm().loadUserByUsername(username); - } catch (UserMayOrMayNotExistException x) { - // OK, give them the benefit of the doubt. - } catch (UsernameNotFoundException x) { - // Not/no longer a user; deny the API token. (But do not leak the information that this happened.) - chain.doFilter(request, response); - return; - } catch (DataAccessException x) { - throw new ServletException(x); - } - */ - -// try { -// User u = User.getById(username,false); -// LastGrantedAuthoritiesProperty o = u.getProperty(LastGrantedAuthoritiesProperty.class); -// if (o!=null) -// o.invalidate(); -// } catch (IOException e) { -// LOGGER.log(Level.WARNING, "Failed to record granted authorities",e); -// } } } diff --git a/core/src/test/java/jenkins/RemotingJarSignatureTest.java b/core/src/test/java/jenkins/RemotingJarSignatureTest.java index 7bf5ac7264..f054d6b170 100644 --- a/core/src/test/java/jenkins/RemotingJarSignatureTest.java +++ b/core/src/test/java/jenkins/RemotingJarSignatureTest.java @@ -1,5 +1,6 @@ package jenkins; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; import hudson.remoting.Channel; @@ -23,8 +24,6 @@ public class RemotingJarSignatureTest { @Test public void testSignature() throws Exception { File jar = Which.jarFile(Channel.class); -// File jar = new File("/home/kohsuke/.m2/repository/org/jenkins-ci/main/remoting/1.421/remoting-1.421.jar"); -// File jar = new File("/home/kohsuke/.m2/repository/org/jenkins-ci/main/remoting/2.0/remoting-2.0.jar"); System.out.println("Verifying "+jar); JarFile myJar = new JarFile(jar,true); @@ -43,9 +42,7 @@ public class RemotingJarSignatureTest { // make sure bits are signed IOUtils.copy(myJar.getInputStream(entry), NullOutputStream.NULL_OUTPUT_STREAM); - if (entry.getCodeSigners()==null) { - fail("No signature for " + name); - } + assertNotNull("No signature for " + name, entry.getCodeSigners()); } } } From aa1f5ccbf379e7f927f82d88c7455041fa500b6f Mon Sep 17 00:00:00 2001 From: offa Date: Thu, 1 Jul 2021 17:15:27 +0200 Subject: [PATCH 07/98] Code clean up. --- core/src/main/java/hudson/Functions.java | 2 +- core/src/main/java/hudson/Launcher.java | 2 +- core/src/main/java/hudson/WebAppMain.java | 2 +- core/src/main/java/hudson/diagnosis/OldDataMonitor.java | 2 +- core/src/main/java/hudson/init/TaskMethodFinder.java | 2 +- .../hudson/init/impl/InstallUncaughtExceptionHandler.java | 4 +--- core/src/main/java/hudson/model/Executor.java | 2 +- core/src/main/java/hudson/scm/ChangeLogSet.java | 2 +- core/src/main/java/hudson/util/FormFieldValidator.java | 4 +--- core/src/main/java/hudson/util/SecretRewriter.java | 2 +- .../main/java/jenkins/fingerprints/FingerprintStorage.java | 2 +- .../main/java/jenkins/model/identity/IdentityRootAction.java | 2 +- core/src/main/java/jenkins/util/JSONSignatureValidator.java | 4 ++-- core/src/main/java/jenkins/util/ResourceBundleUtil.java | 2 +- core/src/test/java/hudson/UtilTest.java | 4 +--- core/src/test/java/hudson/XmlFileTest.java | 2 +- core/src/test/java/jenkins/RemotingJarSignatureTest.java | 1 - core/src/test/java/jenkins/util/JenkinsJVMTest.java | 4 ++-- core/src/test/java/jenkins/util/VirtualFileTest.java | 2 +- 19 files changed, 20 insertions(+), 27 deletions(-) diff --git a/core/src/main/java/hudson/Functions.java b/core/src/main/java/hudson/Functions.java index 6756715726..d49e49a358 100644 --- a/core/src/main/java/hudson/Functions.java +++ b/core/src/main/java/hudson/Functions.java @@ -1685,7 +1685,7 @@ public class Functions { if (Util.isOverridden(Throwable.class, t.getClass(), "printStackTrace", PrintWriter.class)) { StringWriter sw = new StringWriter(); t.printStackTrace(new PrintWriter(sw)); - s.append(sw.toString()); + s.append(sw); return; } Throwable lower = t.getCause(); diff --git a/core/src/main/java/hudson/Launcher.java b/core/src/main/java/hudson/Launcher.java index 9baa3992fa..3bc5f5ca51 100644 --- a/core/src/main/java/hudson/Launcher.java +++ b/core/src/main/java/hudson/Launcher.java @@ -813,7 +813,7 @@ public abstract class Launcher { } else buf.append(c); } - listener.getLogger().println(buf.toString()); + listener.getLogger().println(buf); listener.getLogger().flush(); } diff --git a/core/src/main/java/hudson/WebAppMain.java b/core/src/main/java/hudson/WebAppMain.java index 6eab71e566..1f88bae7cb 100644 --- a/core/src/main/java/hudson/WebAppMain.java +++ b/core/src/main/java/hudson/WebAppMain.java @@ -341,7 +341,7 @@ public class WebAppMain implements ServletContextListener { */ private void recordBootAttempt(File home) { try (OutputStream o=Files.newOutputStream(BootFailure.getBootFailureFile(home).toPath(), StandardOpenOption.CREATE, StandardOpenOption.APPEND)) { - o.write((new Date().toString() + System.getProperty("line.separator", "\n")).getBytes()); + o.write((new Date() + System.getProperty("line.separator", "\n")).getBytes()); } catch (IOException | InvalidPathException e) { LOGGER.log(WARNING, "Failed to record boot attempts",e); } diff --git a/core/src/main/java/hudson/diagnosis/OldDataMonitor.java b/core/src/main/java/hudson/diagnosis/OldDataMonitor.java index 6345dff0c5..fa43d35890 100644 --- a/core/src/main/java/hudson/diagnosis/OldDataMonitor.java +++ b/core/src/main/java/hudson/diagnosis/OldDataMonitor.java @@ -274,7 +274,7 @@ public class OldDataMonitor extends AdministrativeMonitor { @Override public String toString() { - return min==null ? "" : min.toString() + (single ? "" : " - " + max.toString()); + return min==null ? "" : min + (single ? "" : " - " + max.toString()); } /** diff --git a/core/src/main/java/hudson/init/TaskMethodFinder.java b/core/src/main/java/hudson/init/TaskMethodFinder.java index c23aa88f76..06702e4ea8 100644 --- a/core/src/main/java/hudson/init/TaskMethodFinder.java +++ b/core/src/main/java/hudson/init/TaskMethodFinder.java @@ -84,7 +84,7 @@ abstract class TaskMethodFinder extends TaskBuilder { c.getClassLoader().loadClass(c.getPackage().getName() + ".Messages")); return rb.format(key); } catch (ClassNotFoundException x) { - LOGGER.log(WARNING, "Failed to load "+x.getMessage()+" for "+e.toString(),x); + LOGGER.log(WARNING, "Failed to load "+x.getMessage()+" for "+ e,x); return key; } catch (MissingResourceException x) { LOGGER.log(WARNING, "Could not find key '" + key + "' in " + c.getPackage().getName() + ".Messages", x); diff --git a/core/src/main/java/hudson/init/impl/InstallUncaughtExceptionHandler.java b/core/src/main/java/hudson/init/impl/InstallUncaughtExceptionHandler.java index 2eaec5326f..4241d5a5e8 100644 --- a/core/src/main/java/hudson/init/impl/InstallUncaughtExceptionHandler.java +++ b/core/src/main/java/hudson/init/impl/InstallUncaughtExceptionHandler.java @@ -32,9 +32,7 @@ public class InstallUncaughtExceptionHandler { @Initializer public static void init(final Jenkins j) throws IOException { - CompressionFilter.setUncaughtExceptionHandler(j.servletContext, (e, context, req, rsp) -> { - handleException(j, e, req, rsp, 500); - }); + CompressionFilter.setUncaughtExceptionHandler(j.servletContext, (e, context, req, rsp) -> handleException(j, e, req, rsp, 500)); try { Thread.setDefaultUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler()); LOGGER.log(Level.FINE, "Successfully installed a global UncaughtExceptionHandler."); diff --git a/core/src/main/java/hudson/model/Executor.java b/core/src/main/java/hudson/model/Executor.java index 3587ad904b..9e7cfc0499 100644 --- a/core/src/main/java/hudson/model/Executor.java +++ b/core/src/main/java/hudson/model/Executor.java @@ -417,7 +417,7 @@ public class Executor extends Thread implements ModelObject { } } - setName(getName() + " : executing " + executable.toString()); + setName(getName() + " : executing " + executable); Authentication auth = workUnit.context.item.authenticate2(); LOGGER.log(FINE, "{0} is now executing {1} as {2}", new Object[] {getName(), executable, auth}); if (LOGGER.isLoggable(FINE) && auth.equals(ACL.SYSTEM2)) { // i.e., unspecified diff --git a/core/src/main/java/hudson/scm/ChangeLogSet.java b/core/src/main/java/hudson/scm/ChangeLogSet.java index 135869989b..c78edcba8d 100644 --- a/core/src/main/java/hudson/scm/ChangeLogSet.java +++ b/core/src/main/java/hudson/scm/ChangeLogSet.java @@ -253,7 +253,7 @@ public abstract class ChangeLogSet implements Iter } catch(Exception e) { LOGGER.info("ChangeLogAnnotator " + a.toString() + " failed to annotate message '" + getMsg() + "'; " + e.getMessage()); } catch(Error e) { - LOGGER.severe("ChangeLogAnnotator " + a.toString() + " failed to annotate message '" + getMsg() + "'; " + e.getMessage()); + LOGGER.severe("ChangeLogAnnotator " + a + " failed to annotate message '" + getMsg() + "'; " + e.getMessage()); } return markup.toString(false); diff --git a/core/src/main/java/hudson/util/FormFieldValidator.java b/core/src/main/java/hudson/util/FormFieldValidator.java index f3e78b1292..eab40cb382 100644 --- a/core/src/main/java/hudson/util/FormFieldValidator.java +++ b/core/src/main/java/hudson/util/FormFieldValidator.java @@ -567,10 +567,8 @@ public abstract class FormFieldValidator { public void validate(File fexe) { try { self.checkExecutable(fexe); - } catch (IOException ioe) { + } catch (IOException | ServletException ioe) { exceptions[0] = ioe; - } catch (ServletException se) { - exceptions[0] = se; } } }); diff --git a/core/src/main/java/hudson/util/SecretRewriter.java b/core/src/main/java/hudson/util/SecretRewriter.java index ffb417a781..0122503366 100644 --- a/core/src/main/java/hudson/util/SecretRewriter.java +++ b/core/src/main/java/hudson/util/SecretRewriter.java @@ -110,7 +110,7 @@ public class SecretRewriter { copied = eidx; } buf.append(line.substring(copied)); - out.println(buf.toString()); + out.println(buf); } } diff --git a/core/src/main/java/jenkins/fingerprints/FingerprintStorage.java b/core/src/main/java/jenkins/fingerprints/FingerprintStorage.java index d92bbf4c22..f447ed378e 100644 --- a/core/src/main/java/jenkins/fingerprints/FingerprintStorage.java +++ b/core/src/main/java/jenkins/fingerprints/FingerprintStorage.java @@ -114,7 +114,7 @@ public abstract class FingerprintStorage extends AbstractDescribableImpl { - Util.ifOverridden(() -> true, BaseClass.class, DerivedClassFailure.class, "method"); - }); + AbstractMethodError error = Assert.assertThrows(AbstractMethodError.class, () -> Util.ifOverridden(() -> true, BaseClass.class, DerivedClassFailure.class, "method")); assertEquals("The class " + DerivedClassFailure.class.getName() + " must override at least one of the BaseClass.method methods", error.getMessage()); } diff --git a/core/src/test/java/hudson/XmlFileTest.java b/core/src/test/java/hudson/XmlFileTest.java index c8d8b9f3c1..e208c48440 100644 --- a/core/src/test/java/hudson/XmlFileTest.java +++ b/core/src/test/java/hudson/XmlFileTest.java @@ -42,7 +42,7 @@ public class XmlFileTest { XmlFile xmlFile = new XmlFile(xs, new File(configUrl.getFile())); if (xmlFile.exists()) { - assertThrows(SAXParseException.class, () -> xmlFile.read()); + assertThrows(SAXParseException.class, xmlFile::read); } } diff --git a/core/src/test/java/jenkins/RemotingJarSignatureTest.java b/core/src/test/java/jenkins/RemotingJarSignatureTest.java index f054d6b170..f6103ec165 100644 --- a/core/src/test/java/jenkins/RemotingJarSignatureTest.java +++ b/core/src/test/java/jenkins/RemotingJarSignatureTest.java @@ -1,7 +1,6 @@ package jenkins; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; import hudson.remoting.Channel; import hudson.remoting.Which; diff --git a/core/src/test/java/jenkins/util/JenkinsJVMTest.java b/core/src/test/java/jenkins/util/JenkinsJVMTest.java index 206ca39fe2..888c592739 100644 --- a/core/src/test/java/jenkins/util/JenkinsJVMTest.java +++ b/core/src/test/java/jenkins/util/JenkinsJVMTest.java @@ -13,14 +13,14 @@ public class JenkinsJVMTest { @Test public void checkJenkinsJVM_WhenNotInAJenkinsJVM() { - assertThrows(IllegalStateException.class, () -> JenkinsJVM.checkJenkinsJVM()); + assertThrows(IllegalStateException.class, JenkinsJVM::checkJenkinsJVM); } @Test public void checkNotJenkinsJVM_WhenInAJenkinsJVM() { JenkinsJVM.setJenkinsJVM(true); try { - assertThrows(IllegalStateException.class, () -> JenkinsJVM.checkNotJenkinsJVM()); + assertThrows(IllegalStateException.class, JenkinsJVM::checkNotJenkinsJVM); } finally { JenkinsJVM.setJenkinsJVM(false); } diff --git a/core/src/test/java/jenkins/util/VirtualFileTest.java b/core/src/test/java/jenkins/util/VirtualFileTest.java index e2f4d81a45..c28a213b1b 100644 --- a/core/src/test/java/jenkins/util/VirtualFileTest.java +++ b/core/src/test/java/jenkins/util/VirtualFileTest.java @@ -100,7 +100,7 @@ public class VirtualFileTest { VirtualFile hack = root.child("hack"); assertFalse(hack.isFile()); assertFalse(hack.exists()); - assertThrows(FileNotFoundException.class, () -> hack.open()); + assertThrows(FileNotFoundException.class, hack::open); } @Issue("JENKINS-26810") From 467e1463c442dcf6a4b20237b7545568c177f6d5 Mon Sep 17 00:00:00 2001 From: offa Date: Fri, 2 Jul 2021 17:13:44 +0200 Subject: [PATCH 08/98] Revert LastGrantedAuthoritiesProperty and keep the code for documentation. --- .../LastGrantedAuthoritiesProperty.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/core/src/main/java/jenkins/security/LastGrantedAuthoritiesProperty.java b/core/src/main/java/jenkins/security/LastGrantedAuthoritiesProperty.java index cfe795746a..da348b768f 100644 --- a/core/src/main/java/jenkins/security/LastGrantedAuthoritiesProperty.java +++ b/core/src/main/java/jenkins/security/LastGrantedAuthoritiesProperty.java @@ -136,6 +136,28 @@ public class LastGrantedAuthoritiesProperty extends UserProperty { // while this initially seemed like a good idea to avoid allowing wrong impersonation for too long, // doing this means a malicious user can break the impersonation capability // just by failing to login. See ApiTokenFilter that does the following, which seems better: + /* + try { + Jenkins.get().getSecurityRealm().loadUserByUsername(username); + } catch (UserMayOrMayNotExistException x) { + // OK, give them the benefit of the doubt. + } catch (UsernameNotFoundException x) { + // Not/no longer a user; deny the API token. (But do not leak the information that this happened.) + chain.doFilter(request, response); + return; + } catch (DataAccessException x) { + throw new ServletException(x); + } + */ + +// try { +// User u = User.getById(username,false); +// LastGrantedAuthoritiesProperty o = u.getProperty(LastGrantedAuthoritiesProperty.class); +// if (o!=null) +// o.invalidate(); +// } catch (IOException e) { +// LOGGER.log(Level.WARNING, "Failed to record granted authorities",e); +// } } } From a405d0a3a1a04e1e749dc4c59d4a0a7d4ef1e2c6 Mon Sep 17 00:00:00 2001 From: offa Date: Fri, 2 Jul 2021 18:16:25 +0200 Subject: [PATCH 09/98] Keep commented out code for documentation and naming fixed. --- core/src/main/java/hudson/Proc.java | 11 ++++++++++- .../src/main/java/hudson/util/FormFieldValidator.java | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/hudson/Proc.java b/core/src/main/java/hudson/Proc.java index 8f3d0f8170..e1d0766b80 100644 --- a/core/src/main/java/hudson/Proc.java +++ b/core/src/main/java/hudson/Proc.java @@ -345,7 +345,16 @@ public abstract class Proc { // it could be either because another thread is blocking on read, or // it could be a bug in Windows JVM. Who knows. // so I'm abandoning the idea of closing the stream - +// try { +// proc.getInputStream().close(); +// } catch (IOException x) { +// LOGGER.log(Level.FINE,"stdin termination failed",x); +// } +// try { +// proc.getErrorStream().close(); +// } catch (IOException x) { +// LOGGER.log(Level.FINE,"stderr termination failed",x); +// } out.write(msg.getBytes()); out.write('\n'); } diff --git a/core/src/main/java/hudson/util/FormFieldValidator.java b/core/src/main/java/hudson/util/FormFieldValidator.java index eab40cb382..29ae0eb8d4 100644 --- a/core/src/main/java/hudson/util/FormFieldValidator.java +++ b/core/src/main/java/hudson/util/FormFieldValidator.java @@ -567,8 +567,8 @@ public abstract class FormFieldValidator { public void validate(File fexe) { try { self.checkExecutable(fexe); - } catch (IOException | ServletException ioe) { - exceptions[0] = ioe; + } catch (IOException | ServletException ex) { + exceptions[0] = ex; } } }); From b34e1f03b933dcb86114b38ae2f8ef6efdbe0705 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jul 2021 20:14:00 +0100 Subject: [PATCH 10/98] Bump jenkins from 1.64 to 1.65 (#5594) Bumps [jenkins](https://github.com/jenkinsci/pom) from 1.64 to 1.65. - [Release notes](https://github.com/jenkinsci/pom/releases) - [Changelog](https://github.com/jenkinsci/pom/blob/master/CHANGELOG-old.md) - [Commits](https://github.com/jenkinsci/pom/compare/jenkins-1.64...jenkins-1.65) --- updated-dependencies: - dependency-name: org.jenkins-ci:jenkins dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6212db894d..3d976b16f2 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci jenkins - 1.64 + 1.65 From 617f4805d64a1748d48c1f06fce0120de042bc86 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jul 2021 20:14:15 +0100 Subject: [PATCH 11/98] Bump checkstyle from 8.43 to 8.44 (#5595) Bumps [checkstyle](https://github.com/checkstyle/checkstyle) from 8.43 to 8.44. - [Release notes](https://github.com/checkstyle/checkstyle/releases) - [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-8.43...checkstyle-8.44) --- updated-dependencies: - dependency-name: com.puppycrawl.tools:checkstyle dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3d976b16f2..9dc552e829 100644 --- a/pom.xml +++ b/pom.xml @@ -432,7 +432,7 @@ THE SOFTWARE. com.puppycrawl.tools checkstyle - 8.43 + 8.44 From 4450b0e4c0a82b22d6e0495961182c618396f310 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jul 2021 19:14:59 +0000 Subject: [PATCH 12/98] Bump junit from 1.50 to 1.51 Bumps [junit](https://github.com/jenkinsci/junit-plugin) from 1.50 to 1.51. - [Release notes](https://github.com/jenkinsci/junit-plugin/releases) - [Commits](https://github.com/jenkinsci/junit-plugin/compare/junit-1.50...junit-1.51) --- updated-dependencies: - dependency-name: org.jenkins-ci.plugins:junit dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- test/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/pom.xml b/test/pom.xml index f51c55c12b..7c18106126 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -129,7 +129,7 @@ THE SOFTWARE. org.jenkins-ci.plugins junit - 1.50 + 1.51 test From 2a151c28d079cc8235ace0b61ab1f5a448a77661 Mon Sep 17 00:00:00 2001 From: Daniel Beck <1831569+daniel-beck@users.noreply.github.com> Date: Mon, 5 Jul 2021 21:30:52 +0200 Subject: [PATCH 13/98] Fix up comments for SECURITY-2371 (#5600) Co-authored-by: Daniel Beck --- .../hudson/security/AuthenticationProcessingFilter2.java | 5 ++--- core/src/main/java/hudson/security/SecurityRealm.java | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/hudson/security/AuthenticationProcessingFilter2.java b/core/src/main/java/hudson/security/AuthenticationProcessingFilter2.java index 1f028b047f..55f3358adf 100644 --- a/core/src/main/java/hudson/security/AuthenticationProcessingFilter2.java +++ b/core/src/main/java/hudson/security/AuthenticationProcessingFilter2.java @@ -62,10 +62,9 @@ public final class AuthenticationProcessingFilter2 extends UsernamePasswordAuthe @Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { if (SystemProperties.getInteger(SecurityRealm.class.getName() + ".sessionFixationProtectionMode", 1) == 2) { - // This is the default session fixation prevention fix. - // While use of SessionFixationProtectionStrategy would be the canonical Spring Security approach, it seems less compatible with some security realms. + // While use of SessionFixationProtectionStrategy is the canonical Spring Security approach, it may not be compatible with some security realms, so offer this alternative request.getSession().invalidate(); - HttpSession newSession = request.getSession(true); + request.getSession(true); } super.successfulAuthentication(request, response, chain, authResult); HttpSession newSession = request.getSession(); diff --git a/core/src/main/java/hudson/security/SecurityRealm.java b/core/src/main/java/hudson/security/SecurityRealm.java index 0f9214da55..b31679911c 100644 --- a/core/src/main/java/hudson/security/SecurityRealm.java +++ b/core/src/main/java/hudson/security/SecurityRealm.java @@ -596,7 +596,7 @@ public abstract class SecurityRealm extends AbstractDescribableImpl Date: Mon, 5 Jul 2021 12:43:04 -0700 Subject: [PATCH 14/98] Remove explicit dependency on ASM --- bom/pom.xml | 5 ----- core/pom.xml | 4 ---- 2 files changed, 9 deletions(-) diff --git a/bom/pom.xml b/bom/pom.xml index beaca71fad..34b022b140 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -193,11 +193,6 @@ THE SOFTWARE. jnr-posix 3.1.7 - - org.ow2.asm - asm - 9.1 - org.kohsuke windows-package-checker diff --git a/core/pom.xml b/core/pom.xml index c5fcbb51b2..528f80b39f 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -107,10 +107,6 @@ THE SOFTWARE. com.github.jnr jnr-posix - - org.ow2.asm - asm - - 4.9 + 4.10 3.14 From dba7b535c1b42c415e461d2c46bfea1df03e4abd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Jul 2021 16:38:47 +0000 Subject: [PATCH 19/98] Bump git-changelist-maven-extension from 1.0-beta-4 to 1.2 Bumps [git-changelist-maven-extension](https://github.com/jenkinsci/incrementals-tools) from 1.0-beta-4 to 1.2. - [Release notes](https://github.com/jenkinsci/incrementals-tools/releases) - [Commits](https://github.com/jenkinsci/incrementals-tools/commits/parent-1.2) --- updated-dependencies: - dependency-name: io.jenkins.tools.incrementals:git-changelist-maven-extension dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- .mvn/extensions.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml index a2d496cc2b..43d6281619 100644 --- a/.mvn/extensions.xml +++ b/.mvn/extensions.xml @@ -2,6 +2,6 @@ io.jenkins.tools.incrementals git-changelist-maven-extension - 1.0-beta-4 + 1.2 From b7e9f6515ad0273175145ec0c85e85aab25276b6 Mon Sep 17 00:00:00 2001 From: vt Date: Sat, 10 Jul 2021 10:59:15 +0800 Subject: [PATCH 20/98] The appropriate HTTP code in buildWithParameters. When a new parameter build request is determined to be repeated, HTTP 201 should not be returned here. 409 is generally considered to be more appropriate. But since Jenkins actually merged some foldable actions, I think we should return 303 here. --- .../model/ParametersDefinitionProperty.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/hudson/model/ParametersDefinitionProperty.java b/core/src/main/java/hudson/model/ParametersDefinitionProperty.java index 716183e70c..dfc5575491 100644 --- a/core/src/main/java/hudson/model/ParametersDefinitionProperty.java +++ b/core/src/main/java/hudson/model/ParametersDefinitionProperty.java @@ -27,6 +27,7 @@ package hudson.model; import hudson.Extension; import hudson.Util; import hudson.model.Queue.WaitingItem; +import hudson.model.queue.ScheduleResult; import java.io.IOException; import java.util.AbstractList; import java.util.ArrayList; @@ -39,6 +40,7 @@ import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; import javax.servlet.ServletException; import static javax.servlet.http.HttpServletResponse.SC_CREATED; +import static javax.servlet.http.HttpServletResponse.SC_SEE_OTHER; import jenkins.model.Jenkins; import jenkins.model.OptionalJobProperty; import jenkins.model.ParameterizedJobMixIn; @@ -187,14 +189,19 @@ public class ParametersDefinitionProperty extends OptionalJobProperty> if (delay==null) delay=new TimeDuration(TimeUnit.MILLISECONDS.convert(getJob().getQuietPeriod(), TimeUnit.SECONDS)); - Queue.Item item = Jenkins.get().getQueue().schedule2( - getJob(), delay.getTimeInSeconds(), new ParametersAction(values), ParameterizedJobMixIn.getBuildCause(getJob(), req)).getItem(); - + ScheduleResult scheduleResult = Jenkins.get().getQueue().schedule2( + getJob(), delay.getTimeInSeconds(), new ParametersAction(values), ParameterizedJobMixIn.getBuildCause(getJob(), req)); + Queue.Item item = scheduleResult.getItem(); + + if (item != null && !scheduleResult.isCreated()) { + rsp.sendRedirect(SC_SEE_OTHER, req.getContextPath() + '/' + item.getUrl()); + return; + } if (item != null) { rsp.sendRedirect(SC_CREATED, req.getContextPath() + '/' + item.getUrl()); - } else { - rsp.sendRedirect("."); + return; } + rsp.sendRedirect("."); } /** From c6e2e98056d739a8a6d79f019f0c19dc669a237d Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Sat, 10 Jul 2021 08:33:23 -0700 Subject: [PATCH 21/98] Remove hudson.model.TreeView (#5603) --- core/src/main/java/hudson/model/TreeView.java | 205 ------------------ .../hudson/model/TreeView/ajaxRows.jelly | 44 ---- .../hudson/model/TreeView/main.jelly | 36 --- .../hudson/model/TreeView/newView.jelly | 26 --- .../hudson/model/TreeView/newViewDetail.jelly | 28 --- .../hudson/model/TreeView/sidepanel2.jelly | 31 --- .../model/TreeView/sidepanel2_bg.properties | 24 -- .../model/TreeView/sidepanel2_da.properties | 23 -- .../model/TreeView/sidepanel2_de.properties | 1 - .../model/TreeView/sidepanel2_es.properties | 23 -- .../model/TreeView/sidepanel2_fr.properties | 23 -- .../model/TreeView/sidepanel2_it.properties | 24 -- .../model/TreeView/sidepanel2_ja.properties | 23 -- .../TreeView/sidepanel2_pt_BR.properties | 23 -- .../model/TreeView/sidepanel2_sr.properties | 3 - .../TreeView/sidepanel2_zh_TW.properties | 23 -- .../hudson/cli/UpdateViewCommandTest.java | 8 +- 17 files changed, 4 insertions(+), 564 deletions(-) delete mode 100644 core/src/main/java/hudson/model/TreeView.java delete mode 100644 core/src/main/resources/hudson/model/TreeView/ajaxRows.jelly delete mode 100644 core/src/main/resources/hudson/model/TreeView/main.jelly delete mode 100644 core/src/main/resources/hudson/model/TreeView/newView.jelly delete mode 100644 core/src/main/resources/hudson/model/TreeView/newViewDetail.jelly delete mode 100644 core/src/main/resources/hudson/model/TreeView/sidepanel2.jelly delete mode 100644 core/src/main/resources/hudson/model/TreeView/sidepanel2_bg.properties delete mode 100644 core/src/main/resources/hudson/model/TreeView/sidepanel2_da.properties delete mode 100644 core/src/main/resources/hudson/model/TreeView/sidepanel2_de.properties delete mode 100644 core/src/main/resources/hudson/model/TreeView/sidepanel2_es.properties delete mode 100644 core/src/main/resources/hudson/model/TreeView/sidepanel2_fr.properties delete mode 100644 core/src/main/resources/hudson/model/TreeView/sidepanel2_it.properties delete mode 100644 core/src/main/resources/hudson/model/TreeView/sidepanel2_ja.properties delete mode 100644 core/src/main/resources/hudson/model/TreeView/sidepanel2_pt_BR.properties delete mode 100644 core/src/main/resources/hudson/model/TreeView/sidepanel2_sr.properties delete mode 100644 core/src/main/resources/hudson/model/TreeView/sidepanel2_zh_TW.properties diff --git a/core/src/main/java/hudson/model/TreeView.java b/core/src/main/java/hudson/model/TreeView.java deleted file mode 100644 index 8c9543f15a..0000000000 --- a/core/src/main/java/hudson/model/TreeView.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package hudson.model; - -import hudson.model.Descriptor.FormException; -import hudson.util.CaseInsensitiveComparator; -import hudson.Indenter; -import hudson.Extension; -import jenkins.util.SystemProperties; -import hudson.views.ViewsTabBar; -import jenkins.model.Jenkins; -import org.kohsuke.stapler.DataBoundConstructor; -import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.StaplerResponse; - -import javax.servlet.ServletException; -import java.io.IOException; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; -import java.util.concurrent.CopyOnWriteArrayList; -import org.kohsuke.stapler.interceptor.RequirePOST; -import org.kohsuke.stapler.verb.POST; - -/** - * - *

EXPERIMENTAL

- *

- * The intention is to eventually merge this with the {@link ListView}. - * This class is here for experimentation. - * - *

- * Until this class is sufficiently stable, there's no need for l10n. - * TODO: use ViewGroupMixIn - * - * @author Kohsuke Kawaguchi - */ -public class TreeView extends View implements ViewGroup { - /** - * List of job names. This is what gets serialized. - */ - private final Set jobNames - = new TreeSet<>(CaseInsensitiveComparator.INSTANCE); - - /** - * Nested views. - */ - private final CopyOnWriteArrayList views = new CopyOnWriteArrayList<>(); - - @DataBoundConstructor - public TreeView(String name) { - super(name); - } - - /** - * Returns {@link Indenter} that has the fixed indentation width. - * Used for assisting view rendering. - */ - public Indenter createFixedIndenter(String d) { - final int depth = Integer.parseInt(d); - return new Indenter() { - @Override - protected int getNestLevel(Job job) { return depth; } - }; - } - - /** - * Returns a read-only view of all {@link Job}s in this view. - * - *

- * This method returns a separate copy each time to avoid - * concurrent modification issue. - */ - @Override - public synchronized List getItems() { - return Jenkins.get().getItems(); - } - - @Override - public boolean contains(TopLevelItem item) { - return true; - } - - @Override - @POST - public TopLevelItem doCreateItem(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { - ItemGroup ig = getOwner().getItemGroup(); - if (ig instanceof ModifiableItemGroup) { - TopLevelItem item = ((ModifiableItemGroup)ig).doCreateItem(req, rsp); - if(item!=null) { - jobNames.add(item.getName()); - owner.save(); - } - return item; - } - return null; - } - - // TODO listen for changes that might affect jobNames - - @Override - protected void submit(StaplerRequest req) throws IOException, ServletException, FormException { - } - - @Override - public boolean canDelete(View view) { - return true; - } - - @Override - public void deleteView(View view) throws IOException { - views.remove(view); - } - - @Override - public Collection getViews() { - return Collections.unmodifiableList(views); - } - - @Override - public View getView(String name) { - //Top level views returned first if match - for (View v : views) { - if (v.getViewName().equals(name)) { - return v; - } - } - for (View v : views) { - if (v instanceof ViewGroup) { - View nestedView = ((ViewGroup) v).getView(name); - if (nestedView != null) { - return nestedView; - } - } - } - return null; - } - - @Override - public void onViewRenamed(View view, String oldName, String newName) { - // noop - } - - @RequirePOST - public void doCreateView( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException, FormException { - checkPermission(View.CREATE); - views.add(View.create(req,rsp,this)); - save(); - } - - // this feature is not public yet - @Extension - public static ViewDescriptor register() { - if(SystemProperties.getBoolean("hudson.TreeView")) - return new DescriptorImpl(); - else - return null; - } - - public static final class DescriptorImpl extends ViewDescriptor { - @Override - public String getDisplayName() { - return "Tree View"; - } - } - - @Override - public ViewsTabBar getViewsTabBar() { - return Jenkins.get().getViewsTabBar(); - } - - @Override - public ItemGroup getItemGroup() { - return getOwner().getItemGroup(); - } - - @Override - public List getViewActions() { - return owner.getViewActions(); - } - -} diff --git a/core/src/main/resources/hudson/model/TreeView/ajaxRows.jelly b/core/src/main/resources/hudson/model/TreeView/ajaxRows.jelly deleted file mode 100644 index a6812a20cd..0000000000 --- a/core/src/main/resources/hudson/model/TreeView/ajaxRows.jelly +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - -
-
-
diff --git a/core/src/main/resources/hudson/model/TreeView/main.jelly b/core/src/main/resources/hudson/model/TreeView/main.jelly deleted file mode 100644 index 0f14902005..0000000000 --- a/core/src/main/resources/hudson/model/TreeView/main.jelly +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/core/src/main/resources/hudson/model/TreeView/newView.jelly b/core/src/main/resources/hudson/model/TreeView/newView.jelly deleted file mode 100644 index 9f22b566ac..0000000000 --- a/core/src/main/resources/hudson/model/TreeView/newView.jelly +++ /dev/null @@ -1,26 +0,0 @@ - - - - \ No newline at end of file diff --git a/core/src/main/resources/hudson/model/TreeView/newViewDetail.jelly b/core/src/main/resources/hudson/model/TreeView/newViewDetail.jelly deleted file mode 100644 index b7c7ea1666..0000000000 --- a/core/src/main/resources/hudson/model/TreeView/newViewDetail.jelly +++ /dev/null @@ -1,28 +0,0 @@ - - - -

- Experimental: show hierarchical views -
\ No newline at end of file diff --git a/core/src/main/resources/hudson/model/TreeView/sidepanel2.jelly b/core/src/main/resources/hudson/model/TreeView/sidepanel2.jelly deleted file mode 100644 index dc3d0e7938..0000000000 --- a/core/src/main/resources/hudson/model/TreeView/sidepanel2.jelly +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - diff --git a/core/src/main/resources/hudson/model/TreeView/sidepanel2_bg.properties b/core/src/main/resources/hudson/model/TreeView/sidepanel2_bg.properties deleted file mode 100644 index 6c612eb9e6..0000000000 --- a/core/src/main/resources/hudson/model/TreeView/sidepanel2_bg.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Bulgarian translation: Copyright (c) 2016, Alexander Shopov -# -# 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. - -New\ View=\ - \u041d\u043e\u0432 \u0438\u0437\u0433\u043b\u0435\u0434 diff --git a/core/src/main/resources/hudson/model/TreeView/sidepanel2_da.properties b/core/src/main/resources/hudson/model/TreeView/sidepanel2_da.properties deleted file mode 100644 index 448476e662..0000000000 --- a/core/src/main/resources/hudson/model/TreeView/sidepanel2_da.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. Kohsuke Kawaguchi. Knud Poulsen. -# -# 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. - -New\ View=Ny visning diff --git a/core/src/main/resources/hudson/model/TreeView/sidepanel2_de.properties b/core/src/main/resources/hudson/model/TreeView/sidepanel2_de.properties deleted file mode 100644 index ce2c2658a4..0000000000 --- a/core/src/main/resources/hudson/model/TreeView/sidepanel2_de.properties +++ /dev/null @@ -1 +0,0 @@ -New\ View=Neue Ansicht diff --git a/core/src/main/resources/hudson/model/TreeView/sidepanel2_es.properties b/core/src/main/resources/hudson/model/TreeView/sidepanel2_es.properties deleted file mode 100644 index 4df3f2f531..0000000000 --- a/core/src/main/resources/hudson/model/TreeView/sidepanel2_es.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, 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. - -New\ View=Nueva vista diff --git a/core/src/main/resources/hudson/model/TreeView/sidepanel2_fr.properties b/core/src/main/resources/hudson/model/TreeView/sidepanel2_fr.properties deleted file mode 100644 index f4253389b6..0000000000 --- a/core/src/main/resources/hudson/model/TreeView/sidepanel2_fr.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Eric Lefevre-Ardant -# -# 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. - -New\ View=Nouvelle vue diff --git a/core/src/main/resources/hudson/model/TreeView/sidepanel2_it.properties b/core/src/main/resources/hudson/model/TreeView/sidepanel2_it.properties deleted file mode 100644 index 260b76d71e..0000000000 --- a/core/src/main/resources/hudson/model/TreeView/sidepanel2_it.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Italian localization plugin for Jenkins -# Copyright © 2020 Alessandro Menti -# -# 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. - -New\ View=Nuova vista diff --git a/core/src/main/resources/hudson/model/TreeView/sidepanel2_ja.properties b/core/src/main/resources/hudson/model/TreeView/sidepanel2_ja.properties deleted file mode 100644 index 78f5e6bb32..0000000000 --- a/core/src/main/resources/hudson/model/TreeView/sidepanel2_ja.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi, Seiji Sogabe -# -# 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. - -New\ View=\u65B0\u898F\u30D3\u30E5\u30FC diff --git a/core/src/main/resources/hudson/model/TreeView/sidepanel2_pt_BR.properties b/core/src/main/resources/hudson/model/TreeView/sidepanel2_pt_BR.properties deleted file mode 100644 index 6054ccefe2..0000000000 --- a/core/src/main/resources/hudson/model/TreeView/sidepanel2_pt_BR.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc., Cleiber Silva -# -# 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. - -New\ View=Nova view diff --git a/core/src/main/resources/hudson/model/TreeView/sidepanel2_sr.properties b/core/src/main/resources/hudson/model/TreeView/sidepanel2_sr.properties deleted file mode 100644 index f8ec299e02..0000000000 --- a/core/src/main/resources/hudson/model/TreeView/sidepanel2_sr.properties +++ /dev/null @@ -1,3 +0,0 @@ -# This file is under the MIT License by authors - -New\ View=\u041D\u043E\u0432\u0438 \u043F\u0440\u0435\u0433\u043B\u0435\u0434 diff --git a/core/src/main/resources/hudson/model/TreeView/sidepanel2_zh_TW.properties b/core/src/main/resources/hudson/model/TreeView/sidepanel2_zh_TW.properties deleted file mode 100644 index 4d5595a242..0000000000 --- a/core/src/main/resources/hudson/model/TreeView/sidepanel2_zh_TW.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2013, Chunghwa Telecom Co., Ltd., Pei-Tang Huang -# -# 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. - -New\ View=\u65b0\u589e\u8996\u666f diff --git a/test/src/test/java/hudson/cli/UpdateViewCommandTest.java b/test/src/test/java/hudson/cli/UpdateViewCommandTest.java index 857b67f352..1d9c72ff1a 100644 --- a/test/src/test/java/hudson/cli/UpdateViewCommandTest.java +++ b/test/src/test/java/hudson/cli/UpdateViewCommandTest.java @@ -33,7 +33,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.nullValue; import hudson.model.ListView; -import hudson.model.TreeView; +import hudson.model.MyView; import hudson.model.View; import jenkins.model.Jenkins; @@ -85,8 +85,8 @@ public class UpdateViewCommandTest { } @Test public void updateViewWithWrongViewTypeShouldFail() throws Exception { - TreeView tv = new TreeView("aView"); - j.jenkins.addView(tv); + MyView myView = new MyView("aView"); + j.jenkins.addView(myView); final CLICommandInvoker.Result result = command .authorizedTo(View.READ, View.CONFIGURE, Jenkins.READ) .withStdin(this.getClass().getResourceAsStream("/hudson/cli/view.xml")) @@ -94,7 +94,7 @@ public class UpdateViewCommandTest { ; assertThat(result, failedWith(1)); - assertThat(result.stderr(), containsString("Expecting view type: "+ tv.getClass() + assertThat(result.stderr(), containsString("Expecting view type: "+ myView.getClass() + " but got: class hudson.model.ListView instead.")); } From c6f842ebae521983c57365b8ea49466dd5d675c9 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Sat, 10 Jul 2021 11:33:53 -0400 Subject: [PATCH 22/98] `AccessControlled` calls on `ACL.SYSTEM2` can bypass `getACL` (#5586) * `AccessControlled` calls on `ACL.SYSTEM2` can bypass `getACL` * https://github.com/jenkinsci/jenkins/pull/5586#discussion_r662410216 --- core/src/main/java/hudson/model/AbstractItem.java | 4 ++-- core/src/main/java/hudson/model/Hudson.java | 2 +- core/src/main/java/hudson/security/ACL.java | 4 ++-- core/src/main/java/hudson/security/AccessControlled.java | 7 +++++++ test/src/test/java/hudson/model/MyViewsPropertyTest.java | 2 +- test/src/test/java/hudson/security/ACLTest.java | 6 ++++++ 6 files changed, 19 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/hudson/model/AbstractItem.java b/core/src/main/java/hudson/model/AbstractItem.java index b562f91ed8..2f0b64ef69 100644 --- a/core/src/main/java/hudson/model/AbstractItem.java +++ b/core/src/main/java/hudson/model/AbstractItem.java @@ -960,10 +960,10 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet @Restricted(NoExternalUse.class) public Object getTarget() { if (!SKIP_PERMISSION_CHECK) { - if (!getACL().hasPermission(Item.DISCOVER)) { + if (!hasPermission(Item.DISCOVER)) { return null; } - getACL().checkPermission(Item.READ); + checkPermission(Item.READ); } return this; } diff --git a/core/src/main/java/hudson/model/Hudson.java b/core/src/main/java/hudson/model/Hudson.java index adea4375cd..b07c8fd0ec 100644 --- a/core/src/main/java/hudson/model/Hudson.java +++ b/core/src/main/java/hudson/model/Hudson.java @@ -306,7 +306,7 @@ public class Hudson extends Jenkins { */ @Deprecated public static boolean isAdmin() { - return Jenkins.get().getACL().hasPermission(ADMINISTER); + return Jenkins.get().hasPermission(ADMINISTER); } /** diff --git a/core/src/main/java/hudson/security/ACL.java b/core/src/main/java/hudson/security/ACL.java index cf4260035c..47ff187077 100644 --- a/core/src/main/java/hudson/security/ACL.java +++ b/core/src/main/java/hudson/security/ACL.java @@ -69,7 +69,7 @@ public abstract class ACL { */ public final void checkPermission(@NonNull Permission p) { Authentication a = Jenkins.getAuthentication2(); - if (a.equals(SYSTEM2)) { + if (a.equals(SYSTEM2)) { // perhaps redundant given check in AccessControlled return; } if (!hasPermission2(a,p)) { @@ -131,7 +131,7 @@ public abstract class ACL { */ public final boolean hasPermission(@NonNull Permission p) { Authentication a = Jenkins.getAuthentication2(); - if (a.equals(SYSTEM2)) { + if (a.equals(SYSTEM2)) { // perhaps redundant given check in AccessControlled return true; } return hasPermission2(a, p); diff --git a/core/src/main/java/hudson/security/AccessControlled.java b/core/src/main/java/hudson/security/AccessControlled.java index 2de2ae904b..a8c8a93a8b 100644 --- a/core/src/main/java/hudson/security/AccessControlled.java +++ b/core/src/main/java/hudson/security/AccessControlled.java @@ -24,6 +24,7 @@ package hudson.security; import edu.umd.cs.findbugs.annotations.NonNull; +import jenkins.model.Jenkins; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.core.Authentication; @@ -44,6 +45,9 @@ public interface AccessControlled { * Convenient short-cut for {@code getACL().checkPermission(permission)} */ default void checkPermission(@NonNull Permission permission) throws AccessDeniedException { + if (Jenkins.getAuthentication2().equals(ACL.SYSTEM2)) { + return; + } getACL().checkPermission(permission); } @@ -61,6 +65,9 @@ public interface AccessControlled { * Convenient short-cut for {@code getACL().hasPermission(permission)} */ default boolean hasPermission(@NonNull Permission permission) { + if (Jenkins.getAuthentication2().equals(ACL.SYSTEM2)) { + return true; + } return getACL().hasPermission(permission); } diff --git a/test/src/test/java/hudson/model/MyViewsPropertyTest.java b/test/src/test/java/hudson/model/MyViewsPropertyTest.java index 7106f352de..e5970ff654 100644 --- a/test/src/test/java/hudson/model/MyViewsPropertyTest.java +++ b/test/src/test/java/hudson/model/MyViewsPropertyTest.java @@ -234,7 +234,7 @@ public class MyViewsPropertyTest { property.setUser(user); user.addProperty(property); for(Permission p : Permission.getAll()){ - assertEquals("Property should have the same ACL as its user", property.getACL().hasPermission(p), user.getACL().hasPermission(p)); + assertEquals("Property should have the same ACL as its user", property.hasPermission(p), user.hasPermission(p)); } } diff --git a/test/src/test/java/hudson/security/ACLTest.java b/test/src/test/java/hudson/security/ACLTest.java index c04cc134c3..554e59f0e3 100644 --- a/test/src/test/java/hudson/security/ACLTest.java +++ b/test/src/test/java/hudson/security/ACLTest.java @@ -46,6 +46,7 @@ import org.jvnet.hudson.test.MockAuthorizationStrategy; import org.jvnet.hudson.test.TestExtension; import edu.umd.cs.findbugs.annotations.CheckForNull; +import hudson.model.Job; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.core.Authentication; @@ -195,6 +196,11 @@ public class ACLTest { }; } + @Override + public ACL getACL(Job project) { + throw new AssertionError("should not have even needed to call getACL"); + } + @NonNull @Override public Collection getGroups() { From 5b4d081d94c7d791ac0d883756230d1a725216b4 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Sun, 11 Jul 2021 11:11:06 -0700 Subject: [PATCH 23/98] Remove unnecessary differences from upstream `AntClassLoader` --- .../java/hudson/PluginFirstClassLoader.java | 9 - .../java/jenkins/util/AntClassLoader.java | 467 ++++++++---------- .../util/AntWithFindResourceClassLoader.java | 14 +- 3 files changed, 221 insertions(+), 269 deletions(-) diff --git a/core/src/main/java/hudson/PluginFirstClassLoader.java b/core/src/main/java/hudson/PluginFirstClassLoader.java index ee398f7b9b..8fc8323160 100644 --- a/core/src/main/java/hudson/PluginFirstClassLoader.java +++ b/core/src/main/java/hudson/PluginFirstClassLoader.java @@ -23,7 +23,6 @@ */ package hudson; -import java.io.Closeable; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -43,7 +42,6 @@ import jenkins.util.AntWithFindResourceClassLoader; */ public class PluginFirstClassLoader extends AntWithFindResourceClassLoader - implements Closeable { public PluginFirstClassLoader() { @@ -70,13 +68,6 @@ public class PluginFirstClassLoader { return urls; } - - @Override - public void close() - throws IOException - { - cleanup(); - } @Override protected Enumeration findResources( String arg0, boolean arg1 ) diff --git a/core/src/main/java/jenkins/util/AntClassLoader.java b/core/src/main/java/jenkins/util/AntClassLoader.java index db8ef62436..17185152b1 100644 --- a/core/src/main/java/jenkins/util/AntClassLoader.java +++ b/core/src/main/java/jenkins/util/AntClassLoader.java @@ -6,7 +6,7 @@ * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -20,21 +20,21 @@ package jenkins.util; import jenkins.telemetry.impl.java11.MissingClassTelemetry; import org.apache.tools.ant.BuildEvent; import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.MagicNames; import org.apache.tools.ant.Project; import org.apache.tools.ant.SubBuildListener; import org.apache.tools.ant.launch.Locator; import org.apache.tools.ant.types.Path; -import org.apache.tools.ant.util.CollectionUtils; import org.apache.tools.ant.util.FileUtils; import org.apache.tools.ant.util.JavaEnvUtils; import org.apache.tools.ant.util.LoaderUtils; -import org.apache.tools.ant.util.ReflectUtil; +import org.apache.tools.ant.util.VectorSet; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; import edu.umd.cs.findbugs.annotations.CheckForNull; -import edu.umd.cs.findbugs.annotations.NonNull; import java.io.ByteArrayOutputStream; +import java.io.Closeable; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -45,13 +45,13 @@ import java.nio.file.Files; import java.security.CodeSource; import java.security.ProtectionDomain; import java.security.cert.Certificate; -import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Objects; import java.util.StringTokenizer; import java.util.Vector; import java.util.jar.Attributes; @@ -59,6 +59,8 @@ import java.util.jar.Attributes.Name; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.Manifest; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Used to load classes within ant with a different classpath from @@ -78,7 +80,7 @@ import java.util.jar.Manifest; * */ @Restricted(NoExternalUse.class) -public class AntClassLoader extends ClassLoader implements SubBuildListener { +public class AntClassLoader extends ClassLoader implements SubBuildListener, Closeable { private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); @@ -91,11 +93,11 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @see AntClassLoader#findResources(String) * @see java.lang.ClassLoader#getResources(String) */ - private class ResourceEnumeration implements Enumeration { + private class ResourceEnumeration implements Enumeration { /** * The name of the resource being searched for. */ - private String resourceName; + private final String resourceName; /** * The index of the next classpath element to search. @@ -115,7 +117,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * * @param name the name of the resource to search for. */ - ResourceEnumeration(String name) { + ResourceEnumeration(final String name) { this.resourceName = name; this.pathElementsIndex = 0; findNextResource(); @@ -139,8 +141,8 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @return the next resource in the enumeration */ @Override - public Object nextElement() { - URL ret = this.nextResource; + public URL nextElement() { + final URL ret = this.nextResource; if (ret == null) { throw new NoSuchElementException(); } @@ -158,10 +160,10 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { URL url = null; while (pathElementsIndex < pathComponents.size() && url == null) { try { - File pathComponent = pathComponents.get(pathElementsIndex); + final File pathComponent = pathComponents.elementAt(pathElementsIndex); url = getResourceURL(pathComponent, this.resourceName); pathElementsIndex++; - } catch (BuildException e) { + } catch (final BuildException e) { // ignore path elements which are not valid relative to the // project } @@ -184,7 +186,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * The components of the classpath that the classloader searches * for classes. */ - private ArrayList pathComponents = new ArrayList<>(); + private final Vector pathComponents = new VectorSet<>(); /** * The project to which this class loader belongs. @@ -202,14 +204,14 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * loader regardless of whether the parent class loader is being searched * first or not. */ - private Vector systemPackages = new Vector(); + private final Vector systemPackages = new Vector<>(); /** * These are the package roots that are to be loaded by this class loader * regardless of whether the parent class loader is being searched first * or not. */ - private Vector loaderPackages = new Vector(); + private final Vector loaderPackages = new Vector<>(); /** * Whether or not this classloader will ignore the base @@ -227,10 +229,11 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { /** * A hashtable of zip files opened by the classloader (File to JarFile). */ - private Hashtable jarFiles = new Hashtable(); + private Hashtable jarFiles = new Hashtable<>(); /** Static map of jar file/time to manifest class-path entries */ - private static Map/**/ pathMap = Collections.synchronizedMap(new HashMap()); + private static Map pathMap = + Collections.synchronizedMap(new HashMap<>()); /** * The context loader saved when setting the thread's current @@ -252,7 +255,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * belong. * @param classpath The classpath to use to load classes. */ - public AntClassLoader(ClassLoader parent, Project project, Path classpath) { + public AntClassLoader(final ClassLoader parent, final Project project, final Path classpath) { super(parent); // KK patch for JENKINS-21579 setParent(parent); setClassPath(classpath); @@ -277,7 +280,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * May be {@code null}, in which case no path * elements are set up to start with. */ - public AntClassLoader(Project project, Path classpath) { + public AntClassLoader(final Project project, final Path classpath) { setParent(null); setProject(project); setClassPath(classpath); @@ -299,8 +302,8 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * classloader should be consulted before trying to * load the a class through this loader. */ - public AntClassLoader( - ClassLoader parent, Project project, Path classpath, boolean parentFirst) { + public AntClassLoader(final ClassLoader parent, final Project project, + final Path classpath, final boolean parentFirst) { this(project, classpath); if (parent != null) { setParent(parent); @@ -321,7 +324,8 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * classloader should be consulted before trying to * load the a class through this loader. */ - public AntClassLoader(Project project, Path classpath, boolean parentFirst) { + public AntClassLoader(final Project project, final Path classpath, + final boolean parentFirst) { this(null, project, classpath, parentFirst); } @@ -338,7 +342,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * classloader should be consulted before trying to * load the a class through this loader. */ - public AntClassLoader(ClassLoader parent, boolean parentFirst) { + public AntClassLoader(final ClassLoader parent, final boolean parentFirst) { super(parent); // KK patch for JENKINS-21579 setParent(parent); project = null; @@ -350,7 +354,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * * @param project the project instance */ - public void setProject(Project project) { + public void setProject(final Project project) { this.project = project; if (project != null) { project.addBuildListener(this); @@ -364,15 +368,13 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @param classpath the search classpath consisting of directories and * jar/zip files. */ - public void setClassPath(Path classpath) { - pathComponents.clear(); + public void setClassPath(final Path classpath) { + pathComponents.removeAllElements(); if (classpath != null) { - Path actualClasspath = classpath.concatSystemClasspath("ignore"); - String[] pathElements = actualClasspath.list(); - for (String pathElement : pathElements) { + for (String pathElement : classpath.concatSystemClasspath("ignore").list()) { try { addPathElement(pathElement); - } catch (BuildException e) { + } catch (final BuildException e) { // ignore path elements which are invalid // relative to the project } @@ -386,7 +388,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * * @param parent the parent class loader. */ - public void setParent(ClassLoader parent) { + public void setParent(final ClassLoader parent) { this.parent = parent == null ? AntClassLoader.class.getClassLoader() : parent; } @@ -398,7 +400,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @param parentFirst if true, delegate initial class search to the parent * classloader. */ - public void setParentFirst(boolean parentFirst) { + public void setParentFirst(final boolean parentFirst) { this.parentFirst = parentFirst; } @@ -410,7 +412,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * * @param priority The logging priority of the message. */ - protected void log(String message, int priority) { + protected void log(final String message, final int priority) { if (project != null) { project.log(message, priority); } @@ -427,7 +429,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { if (LoaderUtils.isContextLoaderAvailable()) { savedContextLoader = LoaderUtils.getContextClassLoader(); ClassLoader loader = this; - if (project != null && "only".equals(project.getProperty("build.sysclasspath"))) { + if (project != null && "only".equals(project.getProperty(MagicNames.BUILD_SYSCLASSPATH))) { loader = this.getClass().getClassLoader(); } LoaderUtils.setContextClassLoader(loader); @@ -456,12 +458,12 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @exception BuildException if the given path element cannot be resolved * against the project. */ - public void addPathElement(String pathElement) throws BuildException { - File pathComponent = project != null ? project.resolveFile(pathElement) : new File( + public void addPathElement(final String pathElement) throws BuildException { + final File pathComponent = project != null ? project.resolveFile(pathElement) : new File( pathElement); try { addPathFile(pathComponent); - } catch (IOException e) { + } catch (final IOException e) { throw new BuildException(e); } } @@ -473,11 +475,11 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * their CLASSPATH entry in the manifest file. * @param file the jar file or directory to add. */ - public void addPathComponent(File file) { + public void addPathComponent(final File file) { if (pathComponents.contains(file)) { return; } - pathComponents.add(file); + pathComponents.addElement(file); } /** @@ -490,20 +492,20 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * * @throws IOException if data needed from the file cannot be read. */ - protected void addPathFile(File pathComponent) throws IOException { + protected void addPathFile(final File pathComponent) throws IOException { if (!pathComponents.contains(pathComponent)) { - pathComponents.add(pathComponent); + pathComponents.addElement(pathComponent); } if (pathComponent.isDirectory()) { return; } - String absPathPlusTimeAndLength = pathComponent.getAbsolutePath() + final String absPathPlusTimeAndLength = pathComponent.getAbsolutePath() + pathComponent.lastModified() + "-" + pathComponent.length(); - String classpath = (String) pathMap.get(absPathPlusTimeAndLength); + String classpath = pathMap.get(absPathPlusTimeAndLength); if (classpath == null) { try (JarFile jarFile = new JarFile(pathComponent)) { - Manifest manifest = jarFile.getManifest(); + final Manifest manifest = jarFile.getManifest(); if (manifest == null) { return; } @@ -516,20 +518,20 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { pathMap.put(absPathPlusTimeAndLength, classpath); } - if (!"".equals(classpath)) { - URL baseURL = FILE_UTILS.getFileURL(pathComponent); - StringTokenizer st = new StringTokenizer(classpath); + if (!classpath.isEmpty()) { + final URL baseURL = FILE_UTILS.getFileURL(pathComponent); + final StringTokenizer st = new StringTokenizer(classpath); while (st.hasMoreTokens()) { - String classpathElement = st.nextToken(); - URL libraryURL = new URL(baseURL, classpathElement); + final String classpathElement = st.nextToken(); + final URL libraryURL = new URL(baseURL, classpathElement); if (!libraryURL.getProtocol().equals("file")) { log("Skipping jar library " + classpathElement + " since only relative URLs are supported by this" + " loader", Project.MSG_VERBOSE); continue; } - String decodedPath = Locator.decodeUri(libraryURL.getFile()); - File libraryFile = new File(decodedPath); + final String decodedPath = Locator.decodeUri(libraryURL.getFile()); + final File libraryFile = new File(decodedPath); if (libraryFile.exists() && !isInPath(libraryFile)) { addPathFile(libraryFile); } @@ -544,15 +546,12 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * separated by the path separator for the system. */ public String getClasspath() { - StringBuilder sb = new StringBuilder(); - boolean firstPass = true; - for (File pathComponent : pathComponents) { - if (!firstPass) { - sb.append(System.getProperty("path.separator")); - } else { - firstPass = false; + final StringBuilder sb = new StringBuilder(); + for (final File component : pathComponents) { + if (sb.length() > 0) { + sb.append(File.pathSeparator); } - sb.append(pathComponent.getAbsolutePath()); + sb.append(component.getAbsolutePath()); } return sb.toString(); } @@ -566,7 +565,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @param isolated Whether or not this classloader should run in * isolated mode. */ - public synchronized void setIsolated(boolean isolated) { + public synchronized void setIsolated(final boolean isolated) { ignoreBase = isolated; } @@ -581,13 +580,13 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * Use Class.forName with initialize=true instead. */ @Deprecated - public static void initializeClass(Class theClass) { + public static void initializeClass(final Class theClass) { // ***HACK*** We ask the VM to create an instance // by voluntarily providing illegal arguments to force // the VM to run the class' static initializer, while // at the same time not running a valid constructor. - final Constructor[] cons = theClass.getDeclaredConstructors(); + final Constructor[] cons = theClass.getDeclaredConstructors(); //At least one constructor is guaranteed to be there, but check anyway. if (cons != null) { if (cons.length > 0 && cons[0] != null) { @@ -596,7 +595,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { cons[0].newInstance((Object[]) strs); // Expecting an exception to be thrown by this call: // IllegalArgumentException: wrong number of Arguments - } catch (Exception e) { + } catch (final Exception e) { // Ignore - we are interested only in the side // effect - that of getting the static initializers // invoked. As we do not want to call a valid @@ -623,7 +622,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @param packageRoot The root of all packages to be included. * Should not be {@code null}. */ - public void addSystemPackageRoot(String packageRoot) { + public void addSystemPackageRoot(final String packageRoot) { systemPackages.addElement(packageRoot + (packageRoot.endsWith(".") ? "" : ".")); } @@ -636,7 +635,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @param packageRoot The root of all packages to be included. * Should not be {@code null}. */ - public void addLoaderPackageRoot(String packageRoot) { + public void addLoaderPackageRoot(final String packageRoot) { loaderPackages.addElement(packageRoot + (packageRoot.endsWith(".") ? "" : ".")); } @@ -655,10 +654,10 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @exception ClassNotFoundException if the requested class does not exist * on this loader's classpath. */ - public Class forceLoadClass(String classname) throws ClassNotFoundException { + public Class forceLoadClass(final String classname) throws ClassNotFoundException { log("force loading " + classname, Project.MSG_DEBUG); - Class theClass = findLoadedClass(classname); + Class theClass = findLoadedClass(classname); if (theClass == null) { theClass = findClass(classname); @@ -682,10 +681,10 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @exception ClassNotFoundException if the requested class does not exist * on this loader's classpath. */ - public Class forceLoadSystemClass(String classname) throws ClassNotFoundException { + public Class forceLoadSystemClass(final String classname) throws ClassNotFoundException { log("force system loading " + classname, Project.MSG_DEBUG); - Class theClass = findLoadedClass(classname); + Class theClass = findLoadedClass(classname); if (theClass == null) { theClass = findBaseClass(classname); @@ -703,7 +702,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * resource cannot be found on the loader's classpath. */ @Override - public InputStream getResourceAsStream(String name) { + public InputStream getResourceAsStream(final String name) { InputStream resourceStream = null; if (isParentFirst(name)) { resourceStream = loadBaseResource(name); @@ -720,7 +719,9 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { } if (resourceStream == null && !isParentFirst(name)) { if (ignoreBase) { - resourceStream = getRootLoader() == null ? null : getRootLoader().getResourceAsStream(name); + resourceStream = getRootLoader() == null + ? null + : getRootLoader().getResourceAsStream(name); } else { resourceStream = loadBaseResource(name); } @@ -744,16 +745,11 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @return a stream to the required resource or {@code null} if * the resource cannot be found on the loader's classpath. */ - private InputStream loadResource(String name) { + private InputStream loadResource(final String name) { // we need to search the components of the path to see if we can // find the class we want. - for (File pathComponent : pathComponents) { - InputStream stream = getResourceStream(pathComponent, name); - if (stream != null) { - return stream; - } - } - return null; + return pathComponents.stream().map(path -> getResourceStream(path, name)) + .filter(Objects::nonNull).findFirst().orElse(null); } /** @@ -766,34 +762,10 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @return a stream to the named resource, or {@code null} if * the resource cannot be found. */ - private InputStream loadBaseResource(String name) { + private InputStream loadBaseResource(final String name) { return parent == null ? super.getResourceAsStream(name) : parent.getResourceAsStream(name); } - @FunctionalInterface - private interface Converter { - 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()) { - 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 converter.convert(jarFile, entry); - } - /** * Returns an inputstream to a given resource in the given file which may * either be a directory or a zip file. @@ -806,18 +778,32 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @return a stream to the required resource or {@code null} if * the resource cannot be found in the given file. */ - private InputStream getResourceStream(File file, String resourceName) { + private InputStream getResourceStream(final File file, final String resourceName) { try { - JarFile jarFile = (JarFile) jarFiles.get(file); + JarFile jarFile = jarFiles.get(file); if (jarFile == null && file.isDirectory()) { - File resource = new File(file, resourceName); + final File resource = new File(file, resourceName); if (resource.exists()) { return Files.newInputStream(resource.toPath()); } } else { - return storeAndConvert(jarFile, file, resourceName, JarFile::getInputStream); + 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 = jarFiles.get(file); + } + final JarEntry entry = jarFile.getJarEntry(resourceName); + if (entry != null) { + return jarFile.getInputStream(entry); + } } - } catch (Exception e) { + } catch (final Exception e) { log("Ignoring Exception " + e.getClass().getName() + ": " + e.getMessage() + " reading resource " + resourceName + " from " + file, Project.MSG_VERBOSE); } @@ -837,31 +823,16 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @return whether or not the parent classloader should be checked for a * resource before this one is. */ - private boolean isParentFirst(String resourceName) { + private boolean isParentFirst(final String resourceName) { // default to the global setting and then see // if this class belongs to a package which has been // designated to use a specific loader first // (this one or the parent one) - // TODO shouldn't this always return false in isolated mode? + // TODO - shouldn't this always return false in isolated mode? - boolean useParentFirst = parentFirst; - - for (Enumeration e = systemPackages.elements(); e.hasMoreElements();) { - String packageName = (String) e.nextElement(); - if (resourceName.startsWith(packageName)) { - useParentFirst = true; - break; - } - } - for (Enumeration e = loaderPackages.elements(); e.hasMoreElements();) { - String packageName = (String) e.nextElement(); - if (resourceName.startsWith(packageName)) { - useParentFirst = false; - break; - } - } - return useParentFirst; + return loaderPackages.stream().noneMatch(resourceName::startsWith) + && (systemPackages.stream().anyMatch(resourceName::startsWith) || parentFirst); } /** @@ -889,7 +860,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * adequate privileges to get the resource. */ @Override - public URL getResource(String name) { + public URL getResource(final String name) { // we need to search the components of the path to see if // we can find the class we want. URL url = null; @@ -930,7 +901,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { @CheckForNull protected URL getUrl(Iterable pathComponents, String name) { URL url = null; - for (File pathComponent : pathComponents) { + for (final File pathComponent : pathComponents) { url = getResourceURL(pathComponent, name); if (url != null) { log("Resource " + name + " loaded from ant loader", Project.MSG_DEBUG); @@ -948,9 +919,13 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { *

Would override getResources if that wasn't final in Java * 1.4.

* + * @param name name of the resource + * @return possible URLs as enumeration + * @throws IOException if something goes wrong + * @see #findResources(String, boolean) * @since Ant 1.8.0 */ - public Enumeration/**/ getNamedResources(String name) + public Enumeration getNamedResources(final String name) throws IOException { return findResources(name, false); } @@ -965,7 +940,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @exception IOException if I/O errors occurs (can't happen) */ @Override - public Enumeration/**/ findResources(String name) throws IOException { + public Enumeration findResources(final String name) throws IOException { return findResources(name, true); } @@ -981,11 +956,11 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @return an enumeration of URLs for the resources * @exception IOException if I/O errors occurs (can't happen) */ - protected Enumeration/**/ findResources(String name, - boolean parentHasBeenSearched) + protected Enumeration findResources(final String name, + final boolean parentHasBeenSearched) throws IOException { - Enumeration/**/ mine = new ResourceEnumeration(name); - Enumeration/**/ base; + final Enumeration mine = new ResourceEnumeration(name); + Enumeration base; if (parent != null && (!parentHasBeenSearched || parent != getParent())) { // Delegate to the parent: base = parent.getResources(name); @@ -995,18 +970,23 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { } else { // ClassLoader.this.parent is already delegated to for example from // ClassLoader.getResources, no need: - base = new CollectionUtils.EmptyEnumeration(); + base = Collections.emptyEnumeration(); } if (isParentFirst(name)) { // Normal case. - return CollectionUtils.append(base, mine); + return append(base, mine); } if (ignoreBase) { - return getRootLoader() == null ? mine : CollectionUtils.append(mine, getRootLoader() - .getResources(name)); + return getRootLoader() == null ? mine + : append(mine, getRootLoader().getResources(name)); } // parent last: - return CollectionUtils.append(mine, base); + return append(mine, base); + } + + private static Enumeration append(Enumeration one, Enumeration two) { + return Stream.concat(Collections.list(one).stream(), Collections.list(two).stream()) + .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::enumeration)); } /** @@ -1021,30 +1001,41 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @return a stream to the required resource or {@code null} if the * resource cannot be found in the given file object. */ - protected URL getResourceURL(File file, String resourceName) { + protected URL getResourceURL(final File file, final String resourceName) { try { - JarFile jarFile = (JarFile) jarFiles.get(file); + JarFile jarFile = jarFiles.get(file); if (jarFile == null && file.isDirectory()) { - File resource = new File(file, resourceName); + final File resource = new File(file, resourceName); if (resource.exists()) { try { return FILE_UTILS.getFileURL(resource); - } catch (MalformedURLException ex) { + } catch (final MalformedURLException ex) { return null; } } } else { - return storeAndConvert(jarFile, file, resourceName, (jar, entry) -> { - try { - return new URL("jar:" + FILE_UTILS.getFileURL(file) + "!/" + entry); - } catch (MalformedURLException ex) { + if (jarFile == null) { + if (file.exists()) { + jarFile = new JarFile(file); + jarFiles.put(file, jarFile); + } else { return null; } - }); + // potential race-condition + jarFile = jarFiles.get(file); + } + final JarEntry entry = jarFile.getJarEntry(resourceName); + if (entry != null) { + try { + return new URL("jar:" + FILE_UTILS.getFileURL(file) + "!/" + entry); + } catch (final MalformedURLException ex) { + return null; + } + } } - } catch (Exception e) { - String msg = "Unable to obtain resource from " + file + ": "; + } catch (final Exception e) { + final String msg = "Unable to obtain resource from " + file + ": "; log(msg + e, Project.MSG_WARN); System.err.println(msg); e.printStackTrace(); @@ -1073,13 +1064,13 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * classpath. */ @Override - protected synchronized Class loadClass(String classname, boolean resolve) - throws ClassNotFoundException { + protected synchronized Class loadClass(final String classname, final boolean resolve) + throws ClassNotFoundException { // 'sync' is needed - otherwise 2 threads can load the same class // twice, resulting in LinkageError: duplicated class definition. // findLoadedClass avoids that, but without sync it won't work. - Class theClass = findLoadedClass(classname); + Class theClass = findLoadedClass(classname); if (theClass != null) { return theClass; } @@ -1090,17 +1081,17 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { try { theClass = findBaseClass(classname); log("Class " + classname + " loaded from parent loader " + "(parentFirst)", - Project.MSG_DEBUG); - } catch (ClassNotFoundException cnfe) { + Project.MSG_DEBUG); + } catch (final ClassNotFoundException cnfe) { theClass = findClass(classname); log("Class " + classname + " loaded from ant loader " + "(parentFirst)", - Project.MSG_DEBUG); + Project.MSG_DEBUG); } } else { try { theClass = findClass(classname); log("Class " + classname + " loaded from ant loader", Project.MSG_DEBUG); - } catch (ClassNotFoundException cnfe) { + } catch (final ClassNotFoundException cnfe) { if (ignoreBase) { throw cnfe; } @@ -1129,7 +1120,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * * @return the classname in filesystem format (eg java/lang/Integer.class) */ - private String getClassFilename(String classname) { + private String getClassFilename(final String classname) { return classname.replace('.', '/') + ".class"; } @@ -1146,15 +1137,15 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * * @throws IOException if the class data cannot be read. */ - protected Class defineClassFromData(File container, byte[] classData, String classname) - throws IOException { + protected Class defineClassFromData(final File container, final byte[] classData, final String classname) + throws IOException { definePackage(container, classname); - ProtectionDomain currentPd = Project.class.getProtectionDomain(); - String classResource = getClassFilename(classname); - CodeSource src = new CodeSource(FILE_UTILS.getFileURL(container), - getCertificates(container, - classResource)); - ProtectionDomain classesPd = + final ProtectionDomain currentPd = Project.class.getProtectionDomain(); + final String classResource = getClassFilename(classname); + final CodeSource src = new CodeSource(FILE_UTILS.getFileURL(container), + getCertificates(container, + classResource)); + final ProtectionDomain classesPd = new ProtectionDomain(src, currentPd.getPermissions(), this, currentPd.getPrincipals()); @@ -1172,18 +1163,18 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @exception IOException if the package information cannot be read from the * container. */ - protected void definePackage(File container, String className) throws IOException { - int classIndex = className.lastIndexOf('.'); + protected void definePackage(final File container, final String className) throws IOException { + final int classIndex = className.lastIndexOf('.'); if (classIndex == -1) { return; } - String packageName = className.substring(0, classIndex); + final String packageName = className.substring(0, classIndex); if (getPackage(packageName) != null) { // already defined return; } // define the package now - Manifest manifest = getJarManifest(container); + final Manifest manifest = getJarManifest(container); if (manifest == null) { definePackage(packageName, null, null, null, null, null, null, null); @@ -1203,11 +1194,11 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * * @exception IOException if the manifest cannot be read. */ - private Manifest getJarManifest(File container) throws IOException { + private Manifest getJarManifest(final File container) throws IOException { if (container.isDirectory()) { return null; } - JarFile jarFile = (JarFile) jarFiles.get(container); + final JarFile jarFile = jarFiles.get(container); if (jarFile == null) { return null; } @@ -1222,19 +1213,16 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * * @return the entry's certificates or null is the container is * not a jar or it has no certificates. - * - * @exception IOException if the manifest cannot be read. */ - private Certificate[] getCertificates(File container, String entry) - throws IOException { + private Certificate[] getCertificates(final File container, final String entry) { if (container.isDirectory()) { return null; } - JarFile jarFile = (JarFile) jarFiles.get(container); + final JarFile jarFile = jarFiles.get(container); if (jarFile == null) { return null; } - JarEntry ent = jarFile.getJarEntry(entry); + final JarEntry ent = jarFile.getJarEntry(entry); return ent == null ? null : ent.getCertificates(); } @@ -1246,8 +1234,8 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @param packageName the name of the package being defined. * @param manifest the jar's manifest */ - protected void definePackage(File container, String packageName, Manifest manifest) { - String sectionName = packageName.replace('.', '/') + "/"; + protected void definePackage(final File container, final String packageName, final Manifest manifest) { + final String sectionName = packageName.replace('.', '/') + "/"; String specificationTitle = null; String specificationVendor = null; @@ -1258,7 +1246,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { String sealedString = null; URL sealBase = null; - Attributes sectionAttributes = manifest.getAttributes(sectionName); + final Attributes sectionAttributes = manifest.getAttributes(sectionName); if (sectionAttributes != null) { specificationTitle = sectionAttributes.getValue(Name.SPECIFICATION_TITLE); specificationVendor = sectionAttributes.getValue(Name.SPECIFICATION_VENDOR); @@ -1268,7 +1256,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { implementationVersion = sectionAttributes.getValue(Name.IMPLEMENTATION_VERSION); sealedString = sectionAttributes.getValue(Name.SEALED); } - Attributes mainAttributes = manifest.getMainAttributes(); + final Attributes mainAttributes = manifest.getMainAttributes(); if (mainAttributes != null) { if (specificationTitle == null) { specificationTitle = mainAttributes.getValue(Name.SPECIFICATION_TITLE); @@ -1295,12 +1283,12 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { if (sealedString != null && sealedString.equalsIgnoreCase("true")) { try { sealBase = new URL(FileUtils.getFileUtils().toURI(container.getAbsolutePath())); - } catch (MalformedURLException e) { + } catch (final MalformedURLException e) { // ignore } } definePackage(packageName, specificationTitle, specificationVersion, specificationVendor, - implementationTitle, implementationVersion, implementationVendor, sealBase); + implementationTitle, implementationVersion, implementationVendor, sealBase); } /** @@ -1319,16 +1307,16 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @exception SecurityException if there is a security problem while * reading the class from the stream. */ - private Class getClassFromStream(InputStream stream, String classname, File container) - throws IOException, SecurityException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); + private Class getClassFromStream(final InputStream stream, final String classname, final File container) + throws IOException, SecurityException { + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); int bytesRead = -1; - byte[] buffer = new byte[BUFFER_SIZE]; + final byte[] buffer = new byte[BUFFER_SIZE]; while ((bytesRead = stream.read(buffer, 0, BUFFER_SIZE)) != -1) { baos.write(buffer, 0, bytesRead); } - byte[] classData = baos.toByteArray(); + final byte[] classData = baos.toByteArray(); return defineClassFromData(container, classData, classname); } @@ -1344,7 +1332,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * on this loader's classpath. */ @Override - public Class findClass(String name) throws ClassNotFoundException { + public Class findClass(final String name) throws ClassNotFoundException { log("Finding class " + name, Project.MSG_DEBUG); return findClassInComponents(name); } @@ -1356,7 +1344,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * * @return true if the file is in the class path */ - protected boolean isInPath(File component) { + protected boolean isInPath(final File component) { return pathComponents.contains(component); } @@ -1371,24 +1359,24 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @exception ClassNotFoundException if the requested class does not exist * on this loader's classpath. */ - private Class findClassInComponents(String name) + private Class findClassInComponents(final String name) throws ClassNotFoundException { // we need to search the components of the path to see if // we can find the class we want. - String classFilename = getClassFilename(name); - for (File pathComponent : pathComponents) { + final String classFilename = getClassFilename(name); + for (final File pathComponent : pathComponents) { try (InputStream stream = getResourceStream(pathComponent, classFilename)) { if (stream != null) { log("Loaded from " + pathComponent + " " + classFilename, Project.MSG_DEBUG); return getClassFromStream(stream, name, pathComponent); } - } catch (SecurityException se) { + } catch (final SecurityException se) { throw se; - } catch (IOException ioe) { + } catch (final IOException ioe) { // ioe.printStackTrace(); log("Exception reading component " + pathComponent + " (reason: " - + ioe.getMessage() + ")", Project.MSG_VERBOSE); + + ioe.getMessage() + ")", Project.MSG_VERBOSE); } } throw new ClassNotFoundException(name); @@ -1409,7 +1397,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @exception ClassNotFoundException if the requested class does not exist * on this loader's classpath. */ - private Class findBaseClass(String name) throws ClassNotFoundException { + private Class findBaseClass(final String name) throws ClassNotFoundException { return parent == null ? findSystemClass(name) : parent.loadClass(name); } @@ -1418,15 +1406,10 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * files are closed. */ public synchronized void cleanup() { - for (Enumeration e = jarFiles.elements(); e.hasMoreElements();) { - JarFile jarFile = (JarFile) e.nextElement(); - try { - jarFile.close(); - } catch (IOException ioe) { - // ignore - } + for (final JarFile jarFile : jarFiles.values()) { + FileUtils.close(jarFile); } - jarFiles = new Hashtable(); + jarFiles = new Hashtable<>(); if (project != null) { project.removeBuildListener(this); } @@ -1437,6 +1420,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * Gets the parent as has been specified in the constructor or via * setParent. * + * @return classloader * @since Ant 1.8.0 */ public ClassLoader getConfiguredParent() { @@ -1449,7 +1433,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @param event the buildStarted event */ @Override - public void buildStarted(BuildEvent event) { + public void buildStarted(final BuildEvent event) { // Not significant for the class loader. } @@ -1460,7 +1444,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @param event the buildFinished event */ @Override - public void buildFinished(BuildEvent event) { + public void buildFinished(final BuildEvent event) { cleanup(); } @@ -1474,7 +1458,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @since Ant 1.6.2 */ @Override - public void subBuildFinished(BuildEvent event) { + public void subBuildFinished(final BuildEvent event) { if (event.getProject() == project) { cleanup(); } @@ -1488,7 +1472,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @since Ant 1.6.2 */ @Override - public void subBuildStarted(BuildEvent event) { + public void subBuildStarted(final BuildEvent event) { // Not significant for the class loader. } @@ -1498,7 +1482,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @param event the targetStarted event */ @Override - public void targetStarted(BuildEvent event) { + public void targetStarted(final BuildEvent event) { // Not significant for the class loader. } @@ -1508,7 +1492,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @param event the targetFinished event */ @Override - public void targetFinished(BuildEvent event) { + public void targetFinished(final BuildEvent event) { // Not significant for the class loader. } @@ -1518,7 +1502,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @param event the taskStarted event */ @Override - public void taskStarted(BuildEvent event) { + public void taskStarted(final BuildEvent event) { // Not significant for the class loader. } @@ -1528,7 +1512,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @param event the taskFinished event */ @Override - public void taskFinished(BuildEvent event) { + public void taskFinished(final BuildEvent event) { // Not significant for the class loader. } @@ -1538,7 +1522,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * @param event the messageLogged event */ @Override - public void messageLogged(BuildEvent event) { + public void messageLogged(final BuildEvent event) { // Not significant for the class loader. } @@ -1547,12 +1531,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { * here */ public void addJavaLibraries() { - Vector packages = JavaEnvUtils.getJrePackages(); - Enumeration e = packages.elements(); - while (e.hasMoreElements()) { - String packageName = (String) e.nextElement(); - addSystemPackageRoot(packageName); - } + JavaEnvUtils.getJrePackages().forEach(this::addSystemPackageRoot); } /** @@ -1564,37 +1543,25 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { return "AntClassLoader[" + getClasspath() + "]"; } - private static Class subClassToLoad = null; - private static final Class[] CONSTRUCTOR_ARGS = new Class[] { - ClassLoader.class, Project.class, Path.class, Boolean.TYPE - }; - - static { - if (JavaEnvUtils.isAtLeastJavaVersion(JavaEnvUtils.JAVA_1_5)) { - try { - subClassToLoad = - Class.forName("org.apache.tools.ant.loader.AntClassLoader5"); - } catch (ClassNotFoundException e) { - // this is Java5 but the installation is lacking our subclass - } - } + /** {@inheritDoc} */ + @Override + public void close() { + cleanup(); } /** * Factory method + * + * @param parent ClassLoader + * @param project Project + * @param path Path + * @param parentFirst boolean + * @return AntClassLoader */ - public static AntClassLoader newAntClassLoader(ClassLoader parent, - Project project, - Path path, - boolean parentFirst) { - if (subClassToLoad != null) { - return (AntClassLoader) - ReflectUtil.newInstance(subClassToLoad, - CONSTRUCTOR_ARGS, - new Object[] { - parent, project, path, parentFirst - }); - } + public static AntClassLoader newAntClassLoader(final ClassLoader parent, + final Project project, + final Path path, + final boolean parentFirst) { return new AntClassLoader(parent, project, path, parentFirst); } diff --git a/core/src/main/java/jenkins/util/AntWithFindResourceClassLoader.java b/core/src/main/java/jenkins/util/AntWithFindResourceClassLoader.java index 4c03d41242..5cc31c4856 100644 --- a/core/src/main/java/jenkins/util/AntWithFindResourceClassLoader.java +++ b/core/src/main/java/jenkins/util/AntWithFindResourceClassLoader.java @@ -1,19 +1,18 @@ package jenkins.util; -import java.io.Closeable; import java.io.File; import java.io.IOException; import java.lang.reflect.Field; import java.net.URL; -import java.util.ArrayList; import java.util.Collection; +import java.util.Vector; /** * As of 1.8.0, {@link org.apache.tools.ant.AntClassLoader} doesn't implement {@link #findResource(String)} * in any meaningful way, which breaks fast lookup. Implement it properly. */ -public class AntWithFindResourceClassLoader extends AntClassLoader implements Closeable { - private final ArrayList pathComponents; +public class AntWithFindResourceClassLoader extends AntClassLoader { + private final Vector pathComponents; public AntWithFindResourceClassLoader(ClassLoader parent, boolean parentFirst) { super(parent, parentFirst); @@ -21,7 +20,7 @@ public class AntWithFindResourceClassLoader extends AntClassLoader implements Cl try { Field $pathComponents = AntClassLoader.class.getDeclaredField("pathComponents"); $pathComponents.setAccessible(true); - pathComponents = (ArrayList)$pathComponents.get(this); + pathComponents = (Vector)$pathComponents.get(this); } catch (NoSuchFieldException | IllegalAccessException e) { throw new LinkageError(e.getMessage(), e); } @@ -32,11 +31,6 @@ public class AntWithFindResourceClassLoader extends AntClassLoader implements Cl addPathFile(f); } - @Override - public void close() throws IOException { - cleanup(); - } - @Override public URL findResource(String name) { // try and load from this loader if the parent either didn't find From 6ee7e2c21f3775854fab5b1c96565d2c1b7605af Mon Sep 17 00:00:00 2001 From: Jenkins Release Bot <66998184+jenkins-release-bot@users.noreply.github.com> Date: Tue, 13 Jul 2021 12:21:29 +0000 Subject: [PATCH 24/98] [maven-release-plugin] prepare release jenkins-2.302 --- 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 beaca71fad..b6b459878a 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.302 jenkins-bom diff --git a/cli/pom.xml b/cli/pom.xml index 259689191e..daed6487f9 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.302 cli diff --git a/core/pom.xml b/core/pom.xml index c5fcbb51b2..8247b22d95 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.302 jenkins-core diff --git a/pom.xml b/pom.xml index d2c6299321..d410adc910 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.302 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.302 diff --git a/test/pom.xml b/test/pom.xml index f51c55c12b..1eee35f908 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.302 jenkins-test diff --git a/war/pom.xml b/war/pom.xml index 2fbcbb61bf..341ae2e512 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.302 jenkins-war From 97d919c971fedf4ea5c23172359fec959c15829a Mon Sep 17 00:00:00 2001 From: Jenkins Release Bot <66998184+jenkins-release-bot@users.noreply.github.com> Date: Tue, 13 Jul 2021 12:21:47 +0000 Subject: [PATCH 25/98] [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 b6b459878a..beaca71fad 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.302 + ${revision}${changelist} jenkins-bom diff --git a/cli/pom.xml b/cli/pom.xml index daed6487f9..259689191e 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main jenkins-parent - 2.302 + ${revision}${changelist} cli diff --git a/core/pom.xml b/core/pom.xml index 8247b22d95..c5fcbb51b2 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.302 + ${revision}${changelist} jenkins-core diff --git a/pom.xml b/pom.xml index d410adc910..d05e1ba12b 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.302 + ${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.302 + ${scmTag} @@ -69,7 +69,7 @@ THE SOFTWARE. - 2.302 + 2.303 -SNAPSHOT org.springframework.security spring-security-bom - 5.5.0 + 5.5.1 pom import From 814cee0676fbb50a046ff1c992bdee209d38dd71 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 18 Jul 2021 17:19:46 +0100 Subject: [PATCH 28/98] Bump commons-compress from 1.20 to 1.21 (#5624) Bumps commons-compress from 1.20 to 1.21. --- updated-dependencies: - dependency-name: org.apache.commons:commons-compress dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- bom/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/pom.xml b/bom/pom.xml index 5f6fa33f01..35d8dc782b 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -236,7 +236,7 @@ THE SOFTWARE. org.apache.commons commons-compress - 1.20 + 1.21 commons-collections From 4446140d8812dd2892d08d857df7422ec9e51bb0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jul 2021 18:05:44 +0000 Subject: [PATCH 29/98] Bump access-modifier-checker from 1.21 to 1.22 Bumps [access-modifier-checker](https://github.com/jenkinsci/lib-access-modifier) from 1.21 to 1.22. - [Release notes](https://github.com/jenkinsci/lib-access-modifier/releases) - [Commits](https://github.com/jenkinsci/lib-access-modifier/compare/access-modifier-1.21...access-modifier-1.22) --- updated-dependencies: - dependency-name: org.kohsuke:access-modifier-checker dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d05e1ba12b..ca7bf111f3 100644 --- a/pom.xml +++ b/pom.xml @@ -101,7 +101,7 @@ THE SOFTWARE. 1.21 - 1.21 + 1.22 5.7.2 2.0.9 2.12.1 From 39278bdc73ce071413d5747d80d5f46b674587b2 Mon Sep 17 00:00:00 2001 From: Jenkins Release Bot <66998184+jenkins-release-bot@users.noreply.github.com> Date: Thu, 22 Jul 2021 12:46:35 +0000 Subject: [PATCH 30/98] [maven-release-plugin] prepare release jenkins-2.303 --- 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 35d8dc782b..e7473a0b38 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.303 jenkins-bom diff --git a/cli/pom.xml b/cli/pom.xml index 259689191e..3c3dc85a37 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.303 cli diff --git a/core/pom.xml b/core/pom.xml index c5fcbb51b2..0d81f0dcf3 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.303 jenkins-core diff --git a/pom.xml b/pom.xml index d05e1ba12b..61502fc759 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.303 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.303 diff --git a/test/pom.xml b/test/pom.xml index 7c18106126..fcb302a1d5 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.303 jenkins-test diff --git a/war/pom.xml b/war/pom.xml index 2fbcbb61bf..4dc6d1db2c 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.303 jenkins-war From a45d1a0a7618d0351e5ec12cc166194fae337694 Mon Sep 17 00:00:00 2001 From: Jenkins Release Bot <66998184+jenkins-release-bot@users.noreply.github.com> Date: Thu, 22 Jul 2021 12:46:53 +0000 Subject: [PATCH 31/98] [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 e7473a0b38..35d8dc782b 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.303 + ${revision}${changelist} jenkins-bom diff --git a/cli/pom.xml b/cli/pom.xml index 3c3dc85a37..259689191e 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main jenkins-parent - 2.303 + ${revision}${changelist} cli diff --git a/core/pom.xml b/core/pom.xml index 0d81f0dcf3..c5fcbb51b2 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.303 + ${revision}${changelist} jenkins-core diff --git a/pom.xml b/pom.xml index 61502fc759..36a8190b3a 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.303 + ${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.303 + ${scmTag} @@ -69,7 +69,7 @@ THE SOFTWARE. - 2.303 + 2.304 -SNAPSHOT - 1.21 + 1.22 1.22 5.7.2 2.0.9 From dce3baec903f48727ce030df51c6ab1d639a54e8 Mon Sep 17 00:00:00 2001 From: Jenkins Release Bot <66998184+jenkins-release-bot@users.noreply.github.com> Date: Tue, 27 Jul 2021 12:21:31 +0000 Subject: [PATCH 41/98] [maven-release-plugin] prepare release jenkins-2.304 --- 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 a46b5faa9e..0e8ec2edce 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.304 jenkins-bom diff --git a/cli/pom.xml b/cli/pom.xml index 259689191e..a4325d7488 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.304 cli diff --git a/core/pom.xml b/core/pom.xml index 528f80b39f..bccd9e5cc4 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.304 jenkins-core diff --git a/pom.xml b/pom.xml index 1ed1de4224..23cd9afd88 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.304 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.304 diff --git a/test/pom.xml b/test/pom.xml index c6a1472aaa..03eabe78e1 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.304 jenkins-test diff --git a/war/pom.xml b/war/pom.xml index 2fbcbb61bf..04c6f1466c 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.304 jenkins-war From 842b1ab4a84f976d498d36d12d8c89d93748c356 Mon Sep 17 00:00:00 2001 From: Jenkins Release Bot <66998184+jenkins-release-bot@users.noreply.github.com> Date: Tue, 27 Jul 2021 12:21:54 +0000 Subject: [PATCH 42/98] [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 0e8ec2edce..a46b5faa9e 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.304 + ${revision}${changelist} jenkins-bom diff --git a/cli/pom.xml b/cli/pom.xml index a4325d7488..259689191e 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main jenkins-parent - 2.304 + ${revision}${changelist} cli diff --git a/core/pom.xml b/core/pom.xml index bccd9e5cc4..528f80b39f 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.304 + ${revision}${changelist} jenkins-core diff --git a/pom.xml b/pom.xml index 23cd9afd88..957aa7013a 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.304 + ${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.304 + ${scmTag} @@ -69,7 +69,7 @@ THE SOFTWARE. - 2.304 + 2.305 -SNAPSHOT diff --git a/pom.xml b/pom.xml index bc0d98726b..6fd704b25a 100644 --- a/pom.xml +++ b/pom.xml @@ -99,9 +99,7 @@ THE SOFTWARE. High ${project.basedir}/../src/findbugs/findbugs-excludes.xml - - 1.22 - 1.22 + 1.24 5.7.2 2.0.9 2.12.2 @@ -329,7 +327,7 @@ THE SOFTWARE. org.kohsuke access-modifier-checker - ${access-modifier-checker.version} + ${access-modifier.version} com.cloudbees From e223c2cbeceed3f9ad48b6a042cae1d5a9732506 Mon Sep 17 00:00:00 2001 From: Zbynek Konecny Date: Sat, 31 Jul 2021 16:15:30 +0200 Subject: [PATCH 49/98] Shorter plugin label descriptions (#5611) --- core/src/main/resources/hudson/model/Messages.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/resources/hudson/model/Messages.properties b/core/src/main/resources/hudson/model/Messages.properties index 1b38799541..fdf54887f0 100644 --- a/core/src/main/resources/hudson/model/Messages.properties +++ b/core/src/main/resources/hudson/model/Messages.properties @@ -296,7 +296,7 @@ UpdateCenter.PluginCategory.builder=Build Tools UpdateCenter.PluginCategory.buildwrapper=Build Wrappers UpdateCenter.PluginCategory.cli=Command Line Interface UpdateCenter.PluginCategory.cloud=Cloud Providers -UpdateCenter.PluginCategory.cluster=Cluster Management and Distributed Build +UpdateCenter.PluginCategory.cluster=Cluster Management UpdateCenter.PluginCategory.database=Database UpdateCenter.PluginCategory.deployment=Deployment UpdateCenter.PluginCategory.devops=DevOps @@ -322,7 +322,7 @@ UpdateCenter.PluginCategory.scala=Scala Development UpdateCenter.PluginCategory.scm=Source Code Management UpdateCenter.PluginCategory.scm-related=Source Code Management related UpdateCenter.PluginCategory.security=Security -UpdateCenter.PluginCategory.slaves=Agent Launchers and Controllers +UpdateCenter.PluginCategory.slaves=Agent Management UpdateCenter.PluginCategory.test=Testing UpdateCenter.PluginCategory.theme=UI Themes UpdateCenter.PluginCategory.trigger=Build Triggers From 7489e5ff37928894d644c47cf7bab7b09f84c8e7 Mon Sep 17 00:00:00 2001 From: Tim Jacomb <21194782+timja@users.noreply.github.com> Date: Sun, 1 Aug 2021 17:54:40 +0100 Subject: [PATCH 50/98] Javadoc update required for guava upgrade (#5647) --- core/src/main/java/hudson/util/Iterators.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/hudson/util/Iterators.java b/core/src/main/java/hudson/util/Iterators.java index a596de1d66..a09c1dcae0 100644 --- a/core/src/main/java/hudson/util/Iterators.java +++ b/core/src/main/java/hudson/util/Iterators.java @@ -23,7 +23,6 @@ */ package hudson.util; -import com.google.common.annotations.Beta; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableList; @@ -419,7 +418,9 @@ public class Iterators { } /** - * Similar to {@link com.google.common.collect.Iterators#skip} except not {@link Beta}. + * Calls {@code next()} on {@code iterator}, either {@code count} times + * or until {@code hasNext()} returns {@code false}, whichever comes first. + * * @param iterator some iterator * @param count a nonnegative count */ From 5b0d9eb8afdda65f37cbc4565d188498c5d50ffe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Aug 2021 08:50:55 +0100 Subject: [PATCH 51/98] Bump checkstyle from 8.44 to 8.45 (#5648) Bumps [checkstyle](https://github.com/checkstyle/checkstyle) from 8.44 to 8.45. - [Release notes](https://github.com/checkstyle/checkstyle/releases) - [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-8.44...checkstyle-8.45) --- updated-dependencies: - dependency-name: com.puppycrawl.tools:checkstyle dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6fd704b25a..c251c90b4e 100644 --- a/pom.xml +++ b/pom.xml @@ -430,7 +430,7 @@ THE SOFTWARE. com.puppycrawl.tools checkstyle - 8.44 + 8.45 From d5e6312120d54fe26f966334ce3967365c24c98e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Aug 2021 16:05:38 +0100 Subject: [PATCH 52/98] Bump jetty-maven-plugin from 9.4.42.v20210604 to 9.4.43.v20210629 (#5618) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Tim Jacomb <21194782+timja@users.noreply.github.com> --- war/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/war/pom.xml b/war/pom.xml index 2fbcbb61bf..7f01618935 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -105,7 +105,7 @@ THE SOFTWARE. org.jenkins-ci winstone - 5.19 + 5.20 test @@ -416,7 +416,7 @@ THE SOFTWARE. org.eclipse.jetty jetty-maven-plugin - 9.4.42.v20210604 + 9.4.43.v20210629 org.jenkins-ci.plugins script-security - 1.77 + 1.78 From 91027e9b1f58929a2cd01ce0452d5e90a37970ff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Aug 2021 19:59:03 +0100 Subject: [PATCH 54/98] Bump annotation-indexer from 1.14 to 1.15 (#5639) Bumps [annotation-indexer](https://github.com/jenkinsci/lib-annotation-indexer) from 1.14 to 1.15. - [Release notes](https://github.com/jenkinsci/lib-annotation-indexer/releases) - [Commits](https://github.com/jenkinsci/lib-annotation-indexer/compare/annotation-indexer-1.14...annotation-indexer-1.15) --- updated-dependencies: - dependency-name: org.jenkins-ci:annotation-indexer dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- bom/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/pom.xml b/bom/pom.xml index 1cce90b81c..a2da81ddea 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -170,7 +170,7 @@ THE SOFTWARE. org.jenkins-ci annotation-indexer - 1.14 + 1.15 From 3f69b3632370bc170183889fa252966646efe2c2 Mon Sep 17 00:00:00 2001 From: Jenkins Release Bot <66998184+jenkins-release-bot@users.noreply.github.com> Date: Tue, 3 Aug 2021 12:16:29 +0000 Subject: [PATCH 55/98] [maven-release-plugin] prepare release jenkins-2.305 --- 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 a2da81ddea..7a1636350a 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.305 jenkins-bom diff --git a/cli/pom.xml b/cli/pom.xml index 259689191e..5c9fd3eae8 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.305 cli diff --git a/core/pom.xml b/core/pom.xml index 528f80b39f..886abba086 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.305 jenkins-core diff --git a/pom.xml b/pom.xml index c251c90b4e..4960c77b6b 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.305 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.305 diff --git a/test/pom.xml b/test/pom.xml index a0b2928aa3..ee7ed5e0a4 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.305 jenkins-test diff --git a/war/pom.xml b/war/pom.xml index 7f01618935..8ba71e7fcc 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.305 jenkins-war From 2f03ce4be28e30c54d29ba8983bec62f2f63aebc Mon Sep 17 00:00:00 2001 From: Jenkins Release Bot <66998184+jenkins-release-bot@users.noreply.github.com> Date: Tue, 3 Aug 2021 12:16:45 +0000 Subject: [PATCH 56/98] [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 7a1636350a..a2da81ddea 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.305 + ${revision}${changelist} jenkins-bom diff --git a/cli/pom.xml b/cli/pom.xml index 5c9fd3eae8..259689191e 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main jenkins-parent - 2.305 + ${revision}${changelist} cli diff --git a/core/pom.xml b/core/pom.xml index 886abba086..528f80b39f 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.305 + ${revision}${changelist} jenkins-core diff --git a/pom.xml b/pom.xml index 4960c77b6b..8d5284cbd3 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.305 + ${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.305 + ${scmTag} @@ -69,7 +69,7 @@ THE SOFTWARE. - 2.305 + 2.306 -SNAPSHOT diff --git a/core/pom.xml b/core/pom.xml index 528f80b39f..5dc235ccf3 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -36,6 +36,7 @@ THE SOFTWARE. Jenkins core Jenkins core code and view files to render HTML. + https://github.com/jenkinsci/jenkins true diff --git a/pom.xml b/pom.xml index 8d5284cbd3..7fc48c613e 100644 --- a/pom.xml +++ b/pom.xml @@ -39,6 +39,7 @@ THE SOFTWARE. Jenkins main module The module that constitutes the main jenkins.war + https://github.com/jenkinsci/jenkins diff --git a/war/pom.xml b/war/pom.xml index 7f01618935..3d84efb9f2 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -36,6 +36,7 @@ THE SOFTWARE. Jenkins war Creates a war file. Also includes additional static web resources, such as images, CSS, JavaScript, and some HTML files. + https://github.com/jenkinsci/jenkins ${basedir}/work From 6d80eac7e12e81fa8279f1c9daa384f55a2ce3eb Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Thu, 5 Aug 2021 12:18:28 -0700 Subject: [PATCH 59/98] Backport logging-related `AntClassLoader` fixes to our fork (#5650) - commit 189ad7e59 bz-62764 Add a debug log message when we skip invalid path elements from the `AntClassLoader`'s classpath - commit 008f1c8be Embrace `StringUtils#getStackTrace` - commit 13c01b296 Improve skipping of non-zips --- .../java/jenkins/util/AntClassLoader.java | 46 ++++++++++++++++++- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/jenkins/util/AntClassLoader.java b/core/src/main/java/jenkins/util/AntClassLoader.java index c0dff2ed86..8665e99b51 100644 --- a/core/src/main/java/jenkins/util/AntClassLoader.java +++ b/core/src/main/java/jenkins/util/AntClassLoader.java @@ -29,7 +29,9 @@ import org.apache.tools.ant.util.FileUtils; import org.apache.tools.ant.util.JavaEnvUtils; import org.apache.tools.ant.util.LoaderUtils; import org.apache.tools.ant.util.ReflectUtil; +import org.apache.tools.ant.util.StringUtils; import org.apache.tools.ant.util.VectorSet; +import org.apache.tools.zip.ZipLong; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -403,6 +405,8 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener, Clo } catch (final BuildException e) { // ignore path elements which are invalid // relative to the project + log("Ignoring path element " + pathElement + " from " + + "classpath due to exception " + e, Project.MSG_DEBUG); } } } @@ -441,6 +445,8 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener, Clo protected void log(final String message, final int priority) { if (project != null) { project.log(message, priority); + } else if (priority < Project.MSG_INFO) { + System.err.println(message); } } @@ -1043,6 +1049,12 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener, Clo } else { if (jarFile == null) { if (file.exists()) { + if (!isZip(file)) { + final String msg = "CLASSPATH element " + file + + " is not a JAR."; + log(msg, Project.MSG_WARN); + return null; + } jarFile = newJarFile(file); jarFiles.put(file, jarFile); } else { @@ -1063,8 +1075,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener, Clo } catch (final Exception e) { final String msg = "Unable to obtain resource from " + file + ": "; log(msg + e, Project.MSG_WARN); - System.err.println(msg); - e.printStackTrace(); + log(StringUtils.getStackTrace(e), Project.MSG_WARN); } return null; } @@ -1591,6 +1602,37 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener, Clo return new AntClassLoader(parent, project, path, parentFirst); } + private static final ZipLong EOCD_SIG = new ZipLong(0X06054B50L); + private static final ZipLong SINGLE_SEGMENT_SPLIT_MARKER = + new ZipLong(0X30304B50L); + + private static boolean isZip(final File file) throws IOException { + final byte[] sig = new byte[4]; + if (readFully(file, sig)) { + final ZipLong start = new ZipLong(sig); + return ZipLong.LFH_SIG.equals(start) // normal file + || EOCD_SIG.equals(start) // empty zip + || ZipLong.DD_SIG.equals(start) // split zip + || SINGLE_SEGMENT_SPLIT_MARKER.equals(start); + } + return false; + } + + private static boolean readFully(final File f, final byte[] b) throws IOException { + try (InputStream fis = Files.newInputStream(f.toPath())) { + final int len = b.length; + int count = 0, x = 0; + while (count != len) { + x = fis.read(b, count, len - count); + if (x == -1) { + break; + } + count += x; + } + return count == len; + } + } + /** * * @param file The file representing the jar From 4168403de92dfa9df6caa770b7cabd5a6b63f000 Mon Sep 17 00:00:00 2001 From: Ikko Ashimine Date: Sat, 7 Aug 2021 03:33:58 +0900 Subject: [PATCH 60/98] Fix typo in connection-debug.js (#5655) Intialize -> Initialize --- war/src/main/webapp/scripts/yui/connection/connection-debug.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/war/src/main/webapp/scripts/yui/connection/connection-debug.js b/war/src/main/webapp/scripts/yui/connection/connection-debug.js index dde2842ca4..4eac5ba9b4 100644 --- a/war/src/main/webapp/scripts/yui/connection/connection-debug.js +++ b/war/src/main/webapp/scripts/yui/connection/connection-debug.js @@ -424,7 +424,7 @@ YAHOO.util.Connect = } else{ - // Intialize any transaction-specific custom events, if provided. + // Initialize any transaction-specific custom events, if provided. if(callback && callback.customevents){ this.initCustomEvents(o, callback); } From fc40a0ca124b1e08d50560a80d4289dce20903ef Mon Sep 17 00:00:00 2001 From: Jenkins Release Bot <66998184+jenkins-release-bot@users.noreply.github.com> Date: Tue, 10 Aug 2021 12:19:18 +0000 Subject: [PATCH 61/98] [maven-release-plugin] prepare release jenkins-2.306 --- 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 a2da81ddea..9a6c39fde5 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.306 jenkins-bom diff --git a/cli/pom.xml b/cli/pom.xml index 746123378f..529cfca283 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.306 cli diff --git a/core/pom.xml b/core/pom.xml index 5dc235ccf3..de47f2bad9 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.306 jenkins-core diff --git a/pom.xml b/pom.xml index 7fc48c613e..be651f6890 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.306 pom Jenkins main module @@ -61,7 +61,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.306 diff --git a/test/pom.xml b/test/pom.xml index a0b2928aa3..ed7c6bd429 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.306 jenkins-test diff --git a/war/pom.xml b/war/pom.xml index 3d84efb9f2..501db8f953 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.306 jenkins-war From 138039016194095f242427b55abf163926270274 Mon Sep 17 00:00:00 2001 From: Jenkins Release Bot <66998184+jenkins-release-bot@users.noreply.github.com> Date: Tue, 10 Aug 2021 12:19:36 +0000 Subject: [PATCH 62/98] [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 9a6c39fde5..a2da81ddea 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.306 + ${revision}${changelist} jenkins-bom diff --git a/cli/pom.xml b/cli/pom.xml index 529cfca283..746123378f 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main jenkins-parent - 2.306 + ${revision}${changelist} cli diff --git a/core/pom.xml b/core/pom.xml index de47f2bad9..5dc235ccf3 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.306 + ${revision}${changelist} jenkins-core diff --git a/pom.xml b/pom.xml index be651f6890..6113d1440a 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.306 + ${revision}${changelist} pom Jenkins main module @@ -61,7 +61,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.306 + ${scmTag} @@ -70,7 +70,7 @@ THE SOFTWARE. - 2.306 + 2.307 -SNAPSHOT From aa488db5f7bb2b0d83aedfd56fa777f002f858de Mon Sep 17 00:00:00 2001 From: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Thu, 12 Aug 2021 16:57:09 +0100 Subject: [PATCH 65/98] Use the scalable SVG logo over the PNG (#5660) --- .../main/resources/lib/layout/pageHeader.jelly | 2 +- war/src/main/webapp/images/title.png | Bin 2510 -> 0 bytes 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 war/src/main/webapp/images/title.png diff --git a/core/src/main/resources/lib/layout/pageHeader.jelly b/core/src/main/resources/lib/layout/pageHeader.jelly index 6b4933f399..987142bb62 100644 --- a/core/src/main/resources/lib/layout/pageHeader.jelly +++ b/core/src/main/resources/lib/layout/pageHeader.jelly @@ -31,7 +31,7 @@ diff --git a/war/src/main/webapp/images/title.png b/war/src/main/webapp/images/title.png deleted file mode 100644 index db3623b9f55932bed72339bc31755686ef332af4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2510 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 Cc5NmA From a343185e907a4b6cf2405a3cee8a59750c0c1c4a Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Thu, 12 Aug 2021 08:57:28 -0700 Subject: [PATCH 66/98] Remove Woodstox from WAR (#5651) --- bom/pom.xml | 5 ----- core/pom.xml | 11 ----------- 2 files changed, 16 deletions(-) diff --git a/bom/pom.xml b/bom/pom.xml index a2da81ddea..e9ab4106a6 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -258,11 +258,6 @@ THE SOFTWARE. memory-monitor 1.9 - - org.codehaus.woodstox - wstx-asl - 3.2.9 - net.java.dev.jna jna diff --git a/core/pom.xml b/core/pom.xml index 5dc235ccf3..a617d48cd9 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -471,17 +471,6 @@ THE SOFTWARE. org.jenkins-ci memory-monitor - - org.codehaus.woodstox - wstx-asl - - - - stax - stax-api - - - net.java.dev.jna jna From c7c3ae18ef7fe2b65f0c3ce4343cf26c66cfe475 Mon Sep 17 00:00:00 2001 From: Ullrich Hafner Date: Thu, 12 Aug 2021 17:57:50 +0200 Subject: [PATCH 67/98] Add a small gap between build status icon and text (#5507) --- .../main/resources/hudson/model/AbstractBuild/index.jelly | 5 +---- .../main/resources/hudson/model/Run/artifacts-index.jelly | 6 ++---- core/src/main/resources/hudson/model/Run/console.jelly | 6 ++---- core/src/main/resources/lib/hudson/buildCaption.jelly | 8 +++++--- 4 files changed, 10 insertions(+), 15 deletions(-) diff --git a/core/src/main/resources/hudson/model/AbstractBuild/index.jelly b/core/src/main/resources/hudson/model/AbstractBuild/index.jelly index e259553075..3f1774061c 100644 --- a/core/src/main/resources/hudson/model/AbstractBuild/index.jelly +++ b/core/src/main/resources/hudson/model/AbstractBuild/index.jelly @@ -47,10 +47,7 @@ THE SOFTWARE. - - ${%Build} ${it.displayName} - () - + ${%Build} ${it.displayName} ()
diff --git a/core/src/main/resources/hudson/model/Run/artifacts-index.jelly b/core/src/main/resources/hudson/model/Run/artifacts-index.jelly index b954198257..67a057e8ea 100644 --- a/core/src/main/resources/hudson/model/Run/artifacts-index.jelly +++ b/core/src/main/resources/hudson/model/Run/artifacts-index.jelly @@ -28,9 +28,7 @@ THE SOFTWARE. - - ${%Build Artifacts} - + ${%Build Artifacts} @@ -49,4 +47,4 @@ THE SOFTWARE. - \ No newline at end of file + diff --git a/core/src/main/resources/hudson/model/Run/console.jelly b/core/src/main/resources/hudson/model/Run/console.jelly index 132ed4747b..a8e6e99573 100644 --- a/core/src/main/resources/hudson/model/Run/console.jelly +++ b/core/src/main/resources/hudson/model/Run/console.jelly @@ -32,9 +32,7 @@ THE SOFTWARE. - - ${%Console Output} - + ${%Console Output} @@ -54,7 +52,7 @@ THE SOFTWARE.
             
- +
diff --git a/core/src/main/resources/lib/hudson/buildCaption.jelly b/core/src/main/resources/lib/hudson/buildCaption.jelly index 4193d4a169..feb3c13934 100644 --- a/core/src/main/resources/lib/hudson/buildCaption.jelly +++ b/core/src/main/resources/lib/hudson/buildCaption.jelly @@ -43,7 +43,7 @@ THE SOFTWARE.
- + - - + + + + From d697219e803c7c8df59cdcbd01b5477c848d5f65 Mon Sep 17 00:00:00 2001 From: Tim Jacomb <21194782+timja@users.noreply.github.com> Date: Sun, 15 Aug 2021 12:20:22 +0100 Subject: [PATCH 68/98] Increase timeout for branch (#5667) --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 7d28d339c9..b378353018 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -36,7 +36,7 @@ for(j = 0; j < jdks.size(); j++) { // Now run the actual build. stage("${buildType} Build / Test") { - timeout(time: 180, unit: 'MINUTES') { + timeout(time: 300, unit: 'MINUTES') { // See below for what this method does - we're passing an arbitrary environment // variable to it so that JAVA_OPTS and MAVEN_OPTS are set correctly. withMavenEnv(["JAVA_OPTS=-Xmx1536m -Xms512m", From 96a5526bdb1bad3f553d9a9e7f5987a1e6b11456 Mon Sep 17 00:00:00 2001 From: Daniel Beck <1831569+daniel-beck@users.noreply.github.com> Date: Sun, 15 Aug 2021 21:31:53 +0200 Subject: [PATCH 69/98] Terminology cleanup: Controller / Built-In Node (#5425) Co-authored-by: Daniel Beck --- core/src/main/java/hudson/FilePath.java | 2 +- .../java/hudson/cli/ConnectNodeCommand.java | 2 +- .../hudson/cli/DisconnectNodeCommand.java | 2 +- .../java/hudson/cli/OfflineNodeCommand.java | 2 +- .../java/hudson/cli/OnlineNodeCommand.java | 2 +- .../hudson/console/ModelHyperlinkNote.java | 2 +- core/src/main/java/hudson/model/Computer.java | 7 +- .../java/hudson/slaves/SlaveComputer.java | 6 +- .../main/java/hudson/util/ProcessTree.java | 18 +-- .../jenkins/agents/AgentComputerUtil.java | 22 +++- .../ControllerExecutorsAgents.java | 4 +- .../ControllerExecutorsNoAgents.java | 2 +- .../jenkins/model/BuiltInNodeMigration.java | 67 ++++++++++ .../model/CoreEnvironmentContributor.java | 2 +- core/src/main/java/jenkins/model/Jenkins.java | 60 +++++++-- .../model/MasterBuildConfiguration.java | 6 +- .../security/s2m/AdminCallableMonitor.java | 4 +- .../s2m/CallableDirectionChecker.java | 6 +- .../s2m/MasterKillSwitchConfiguration.java | 4 +- .../slaves/DefaultJnlpSlaveReceiver.java | 2 +- .../main/java/jenkins/util/JenkinsJVM.java | 4 +- .../model/AbstractProject/help-label.html | 8 +- .../model/AbstractProject/help-label_bg.html | 4 +- .../model/AbstractProject/help-label_it.html | 2 +- .../hudson/model/Messages.properties | 12 +- .../hudson/model/Node/help-numExecutors.html | 8 +- .../PasswordParameterDefinition/help.html | 2 +- .../hudson/model/Slave/help-remoteFS.html | 4 +- .../node_monitors/ClockMonitor/help.html | 2 +- .../ResponseTimeMonitor/help.html | 4 +- .../TemporarySpaceMonitor/help.html | 2 +- .../TemporarySpaceMonitor/help_bg.html | 2 +- .../TemporarySpaceMonitor/help_ja.html | 2 +- .../slaves/JNLPLauncher/help.properties | 10 +- .../hudson/slaves/Messages.properties | 2 +- .../hudson/tools/Messages.properties | 2 +- .../ZipExtractionInstaller/help-url.html | 2 +- .../message.properties | 5 +- .../message.properties | 3 +- .../jenkins/diagnostics/Messages.properties | 4 +- .../model/BuiltInNodeMigration/message.jelly | 10 ++ .../BuiltInNodeMigration/message.properties | 40 ++++++ .../buildEnv.properties | 4 +- .../Jenkins/MasterComputer/configure.jelly | 2 +- .../MasterBuildConfiguration/config.groovy | 2 +- .../jenkins/model/Messages.properties | 8 +- .../config.groovy | 2 +- ... help-agentToControllerAccessControl.html} | 0 ...lp-agentToControllerAccessControl_bg.html} | 0 ...lp-agentToControllerAccessControl_it.html} | 0 .../message.properties | 2 +- .../description.properties | 2 +- .../resources/lib/hudson/executors.properties | 3 +- core/src/main/resources/lib/hudson/node.jelly | 2 +- .../resources/lib/hudson/node_bg.properties | 24 ---- .../resources/lib/hudson/node_da.properties | 23 ---- .../resources/lib/hudson/node_de.properties | 23 ---- .../resources/lib/hudson/node_es.properties | 23 ---- .../resources/lib/hudson/node_fr.properties | 23 ---- .../resources/lib/hudson/node_he.properties | 3 - .../resources/lib/hudson/node_it.properties | 24 ---- .../resources/lib/hudson/node_ja.properties | 23 ---- .../resources/lib/hudson/node_ko.properties | 3 - .../resources/lib/hudson/node_lv.properties | 23 ---- .../lib/hudson/node_nb_NO.properties | 23 ---- .../resources/lib/hudson/node_nl.properties | 23 ---- .../resources/lib/hudson/node_pl.properties | 23 ---- .../lib/hudson/node_pt_BR.properties | 23 ---- .../resources/lib/hudson/node_ru.properties | 23 ---- .../resources/lib/hudson/node_sk.properties | 3 - .../resources/lib/hudson/node_sr.properties | 3 - .../resources/lib/hudson/node_tr.properties | 23 ---- .../resources/lib/hudson/node_uk.properties | 23 ---- .../lib/hudson/node_zh_TW.properties | 23 ---- .../java/hudson/cli/GetNodeCommandTest.java | 8 ++ .../test/java/hudson/cli/HelpCommandTest.java | 2 +- .../hudson/cli/UpdateNodeCommandTest.java | 8 ++ .../hudson/model/AbstractProjectTest.java | 4 +- .../model/EnvironmentContributorTest.java | 2 +- .../test/java/hudson/model/HudsonTest.java | 14 +-- test/src/test/java/hudson/model/NodeTest.java | 4 +- test/src/test/java/hudson/model/RSSTest.java | 12 +- .../model/labels/LabelExpressionTest.java | 2 +- .../hudson/tools/InstallerTranslatorTest.java | 4 +- .../BuiltInNodeMigrationPropertyTest.java | 67 ++++++++++ .../BuiltInNodeMigrationRestartTest.java | 114 ++++++++++++++++++ .../model/BuiltInNodeMigrationTest.java | 79 ++++++++++++ .../model/JenkinsManagePermissionTest.java | 4 +- .../jenkins/widgets/BuildTimeTrendTest.java | 4 +- test/src/test/java/lib/form/PasswordTest.java | 4 +- .../config.xml | 37 ++++++ .../config.xml | 36 ++++++ .../config.xml | 37 ++++++ .../config.xml | 36 ++++++ .../config.xml | 37 ++++++ .../config.xml | 36 ++++++ .../help/project-config/custom-workspace.html | 2 +- 97 files changed, 805 insertions(+), 513 deletions(-) create mode 100644 core/src/main/java/jenkins/model/BuiltInNodeMigration.java create mode 100644 core/src/main/resources/jenkins/model/BuiltInNodeMigration/message.jelly create mode 100644 core/src/main/resources/jenkins/model/BuiltInNodeMigration/message.properties rename core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/{help-masterToSlaveAccessControl.html => help-agentToControllerAccessControl.html} (100%) rename core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/{help-masterToSlaveAccessControl_bg.html => help-agentToControllerAccessControl_bg.html} (100%) rename core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/{help-masterToSlaveAccessControl_it.html => help-agentToControllerAccessControl_it.html} (100%) delete mode 100644 core/src/main/resources/lib/hudson/node_bg.properties delete mode 100644 core/src/main/resources/lib/hudson/node_da.properties delete mode 100644 core/src/main/resources/lib/hudson/node_de.properties delete mode 100644 core/src/main/resources/lib/hudson/node_es.properties delete mode 100644 core/src/main/resources/lib/hudson/node_fr.properties delete mode 100644 core/src/main/resources/lib/hudson/node_he.properties delete mode 100644 core/src/main/resources/lib/hudson/node_it.properties delete mode 100644 core/src/main/resources/lib/hudson/node_ja.properties delete mode 100644 core/src/main/resources/lib/hudson/node_ko.properties delete mode 100644 core/src/main/resources/lib/hudson/node_lv.properties delete mode 100644 core/src/main/resources/lib/hudson/node_nb_NO.properties delete mode 100644 core/src/main/resources/lib/hudson/node_nl.properties delete mode 100644 core/src/main/resources/lib/hudson/node_pl.properties delete mode 100644 core/src/main/resources/lib/hudson/node_pt_BR.properties delete mode 100644 core/src/main/resources/lib/hudson/node_ru.properties delete mode 100644 core/src/main/resources/lib/hudson/node_sk.properties delete mode 100644 core/src/main/resources/lib/hudson/node_sr.properties delete mode 100644 core/src/main/resources/lib/hudson/node_tr.properties delete mode 100644 core/src/main/resources/lib/hudson/node_uk.properties delete mode 100644 core/src/main/resources/lib/hudson/node_zh_TW.properties create mode 100644 test/src/test/java/jenkins/model/BuiltInNodeMigrationPropertyTest.java create mode 100644 test/src/test/java/jenkins/model/BuiltInNodeMigrationRestartTest.java create mode 100644 test/src/test/java/jenkins/model/BuiltInNodeMigrationTest.java create mode 100644 test/src/test/resources/jenkins/model/BuiltInNodeMigrationPropertyTest/overrideAppliesToMigratedInstance/config.xml create mode 100644 test/src/test/resources/jenkins/model/BuiltInNodeMigrationPropertyTest/overrideAppliesToUnmigratedInstance/config.xml create mode 100644 test/src/test/resources/jenkins/model/BuiltInNodeMigrationRestartTest/migratedInstanceStartsWithNewTerminology/config.xml create mode 100644 test/src/test/resources/jenkins/model/BuiltInNodeMigrationRestartTest/oldDataStartsWithOldTerminology/config.xml create mode 100644 test/src/test/resources/jenkins/model/BuiltInNodeMigrationTest/migratedInstanceStartsWithNewTerminology/config.xml create mode 100644 test/src/test/resources/jenkins/model/BuiltInNodeMigrationTest/oldDataStartsWithOldTerminology/config.xml diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index 2214ed5321..92096a7d40 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -1012,7 +1012,7 @@ public final class FilePath implements SerializableOnlyOverRemoting { timestamp.touch(sourceTimestamp); return true; } catch (IOException x) { - Functions.printStackTrace(x, listener.error("Failed to download " + archive + " from agent; will retry from master")); + Functions.printStackTrace(x, listener.error("Failed to download " + archive + " from agent; will retry from controller")); } } diff --git a/core/src/main/java/hudson/cli/ConnectNodeCommand.java b/core/src/main/java/hudson/cli/ConnectNodeCommand.java index 0a9a152e22..670550ac76 100644 --- a/core/src/main/java/hudson/cli/ConnectNodeCommand.java +++ b/core/src/main/java/hudson/cli/ConnectNodeCommand.java @@ -42,7 +42,7 @@ import java.util.logging.Logger; public class ConnectNodeCommand extends CLICommand { @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") - @Argument(metaVar="NAME", usage="Slave name, or empty string for master; comma-separated list is supported", required=true, multiValued=true) + @Argument(metaVar="NAME", usage="Agent name, or empty string for built-in node; comma-separated list is supported", required=true, multiValued=true) private List nodes; @Option(name="-f", usage="Cancel any currently pending connect operation and retry from scratch") diff --git a/core/src/main/java/hudson/cli/DisconnectNodeCommand.java b/core/src/main/java/hudson/cli/DisconnectNodeCommand.java index 5a0a25c954..1a2e9c5570 100644 --- a/core/src/main/java/hudson/cli/DisconnectNodeCommand.java +++ b/core/src/main/java/hudson/cli/DisconnectNodeCommand.java @@ -43,7 +43,7 @@ import java.util.logging.Logger; */ @Extension public class DisconnectNodeCommand extends CLICommand { - @Argument(metaVar = "NAME", usage = "Slave name, or empty string for master; comma-separated list is supported", required = true, multiValued = true) + @Argument(metaVar = "NAME", usage = "Agent name, or empty string for built-in node; comma-separated list is supported", required = true, multiValued = true) private List nodes; @Option(name = "-m", usage = "Record the reason about why you are disconnecting this node") diff --git a/core/src/main/java/hudson/cli/OfflineNodeCommand.java b/core/src/main/java/hudson/cli/OfflineNodeCommand.java index 39270918dc..fca028627e 100644 --- a/core/src/main/java/hudson/cli/OfflineNodeCommand.java +++ b/core/src/main/java/hudson/cli/OfflineNodeCommand.java @@ -45,7 +45,7 @@ import java.util.List; @Extension public class OfflineNodeCommand extends CLICommand { - @Argument(metaVar = "NAME", usage = "Agent name, or empty string for master", required = true, multiValued = true) + @Argument(metaVar = "NAME", usage = "Agent name, or empty string for built-in node", required = true, multiValued = true) private List nodes; @Option(name = "-m", usage = "Record the reason about why you are disconnecting this node") diff --git a/core/src/main/java/hudson/cli/OnlineNodeCommand.java b/core/src/main/java/hudson/cli/OnlineNodeCommand.java index e4a56ef8ed..91fcc894dd 100644 --- a/core/src/main/java/hudson/cli/OnlineNodeCommand.java +++ b/core/src/main/java/hudson/cli/OnlineNodeCommand.java @@ -41,7 +41,7 @@ import java.util.List; @Extension public class OnlineNodeCommand extends CLICommand { - @Argument(metaVar = "NAME", usage = "Agent name, or empty string for master", required = true, multiValued = true) + @Argument(metaVar = "NAME", usage = "Agent name, or empty string for built-in node", required = true, multiValued = true) private List nodes; @Override diff --git a/core/src/main/java/hudson/console/ModelHyperlinkNote.java b/core/src/main/java/hudson/console/ModelHyperlinkNote.java index 233cac2f25..334aa56e84 100644 --- a/core/src/main/java/hudson/console/ModelHyperlinkNote.java +++ b/core/src/main/java/hudson/console/ModelHyperlinkNote.java @@ -56,7 +56,7 @@ public class ModelHyperlinkNote extends HyperlinkNote { if (c != null) { return encodeTo("/" + c.getUrl(), node.getDisplayName()); } - String nodePath = node == Jenkins.get() ? "(master)" : node.getNodeName(); + String nodePath = node == Jenkins.get() ? "(built-in)" : node.getNodeName(); return encodeTo("/computer/" + nodePath, node.getDisplayName()); } diff --git a/core/src/main/java/hudson/model/Computer.java b/core/src/main/java/hudson/model/Computer.java index 9780d77ceb..e90b18b354 100644 --- a/core/src/main/java/hudson/model/Computer.java +++ b/core/src/main/java/hudson/model/Computer.java @@ -1448,11 +1448,11 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces try (PrintWriter w = new PrintWriter(rsp.getCompressedWriter(req))) { VirtualChannel vc = getChannel(); if (vc instanceof Channel) { - w.println("Master to slave"); + w.println("Controller to agent"); ((Channel) vc).dumpExportTable(w); w.flush(); // flush here once so that even if the dump from the agent fails, the client gets some useful info - w.println("\n\n\nSlave to master"); + w.println("\n\n\nAgent to controller"); w.print(vc.call(new DumpExportTableTask())); } else { w.println(Messages.Computer_BadChannel()); @@ -1656,7 +1656,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces */ @CLIResolver public static Computer resolveForCLI( - @Argument(required=true,metaVar="NAME",usage="Agent name, or empty string for master") String name) throws CmdLineException { + @Argument(required=true,metaVar="NAME",usage="Agent name, or empty string for built-in node") String name) throws CmdLineException { Jenkins h = Jenkins.get(); Computer item = h.getComputer(name); if (item==null) { @@ -1677,6 +1677,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces * * @see #getLogFile() */ + // TODO(terminology) migrate from slaves/ to agents/ @Initializer public static void relocateOldLogs() { relocateOldLogs(Jenkins.get().getRootDir()); diff --git a/core/src/main/java/hudson/slaves/SlaveComputer.java b/core/src/main/java/hudson/slaves/SlaveComputer.java index 1ed9b73a43..12d51735f8 100644 --- a/core/src/main/java/hudson/slaves/SlaveComputer.java +++ b/core/src/main/java/hudson/slaves/SlaveComputer.java @@ -1061,7 +1061,7 @@ public class SlaveComputer extends Computer { } try { - getChannelOrFail().setProperty("slave",Boolean.TRUE); // indicate that this side of the channel is the agent side. + getChannelOrFail().setProperty("agent",Boolean.TRUE); // indicate that this side of the channel is the agent side. } catch (ChannelClosedException e) { throw new IllegalStateException(e); } @@ -1080,11 +1080,11 @@ public class SlaveComputer extends Computer { * * @return null if the calling thread doesn't have any trace of where its controller is. * @since 1.362 - * @deprecated Use {@link AgentComputerUtil#getChannelToMaster()} instead. + * @deprecated Use {@link AgentComputerUtil#getChannelToController()} instead. */ @Deprecated public static VirtualChannel getChannelToMaster() { - return AgentComputerUtil.getChannelToMaster(); + return AgentComputerUtil.getChannelToController(); } /** diff --git a/core/src/main/java/hudson/util/ProcessTree.java b/core/src/main/java/hudson/util/ProcessTree.java index 29435b931f..367c700023 100644 --- a/core/src/main/java/hudson/util/ProcessTree.java +++ b/core/src/main/java/hudson/util/ProcessTree.java @@ -195,9 +195,9 @@ public abstract class ProcessTree implements Iterable, IProcessTree, /*package*/ final List getKillers() throws InterruptedException { if (killers==null) try { - VirtualChannel channelToMaster = AgentComputerUtil.getChannelToMaster(); - if (channelToMaster!=null) { - killers = channelToMaster.call(new ListAll()); + VirtualChannel channelToController = AgentComputerUtil.getChannelToController(); + if (channelToController!=null) { + killers = channelToController.call(new ListAll()); } else { // used in an environment that doesn't support talk-back to the master. // let's do with what we have. @@ -294,10 +294,10 @@ public abstract class ProcessTree implements Iterable, IProcessTree, // Quick check, does anything exist to check against if (!skipVetoes) { try { - VirtualChannel channelToMaster = AgentComputerUtil.getChannelToMaster(); - if (channelToMaster!=null) { + VirtualChannel channelToController = AgentComputerUtil.getChannelToController(); + if (channelToController!=null) { CheckVetoes vetoCheck = new CheckVetoes(this); - causeMessage = channelToMaster.call(vetoCheck); + causeMessage = channelToController.call(vetoCheck); } } catch (IOException e) { LOGGER.log(Level.WARNING, "I/O Exception while checking for vetoes", e); @@ -438,9 +438,9 @@ public abstract class ProcessTree implements Iterable, IProcessTree, // Check for the existance of vetoers if I don't know already if (vetoersExist == null) { try { - VirtualChannel channelToMaster = AgentComputerUtil.getChannelToMaster(); - if (channelToMaster != null) { - vetoersExist = channelToMaster.call(new DoVetoersExist()); + VirtualChannel channelToController = AgentComputerUtil.getChannelToController(); + if (channelToController != null) { + vetoersExist = channelToController.call(new DoVetoersExist()); } } catch (InterruptedException ie) { diff --git a/core/src/main/java/jenkins/agents/AgentComputerUtil.java b/core/src/main/java/jenkins/agents/AgentComputerUtil.java index 021b236c53..5a105c8755 100644 --- a/core/src/main/java/jenkins/agents/AgentComputerUtil.java +++ b/core/src/main/java/jenkins/agents/AgentComputerUtil.java @@ -35,26 +35,36 @@ public final class AgentComputerUtil { } /** - * Obtains a {@link VirtualChannel} that allows some computation to be performed on the master. - * This method can be called from any thread on the master, or from agent (more precisely, + * Obtains a {@link VirtualChannel} that allows some computation to be performed on the controller. + * This method can be called from any thread on the controller, or from agent (more precisely, * it only works from the remoting request-handling thread in agents, which means if you've started * separate thread on agents, that'll fail.) * - * @return null if the calling thread doesn't have any trace of where its master is. - * @since 2.235 + * @return null if the calling thread doesn't have any trace of where its controller is. + * @since TODO */ @CheckForNull - public static VirtualChannel getChannelToMaster() { + public static VirtualChannel getChannelToController() { if (JenkinsJVM.isJenkinsJVM()) { return FilePath.localChannel; } // if this method is called from within the agent computation thread, this should work Channel c = Channel.current(); - if (c != null && Boolean.TRUE.equals(c.getProperty("slave"))) { + if (c != null && Boolean.TRUE.equals(c.getProperty("agent"))) { return c; } return null; } + + /** + * @deprecated Use {{@link #getChannelToController()}}. + * @since 2.235 + */ + @Deprecated + @CheckForNull + public static VirtualChannel getChannelToMaster() { + return getChannelToController(); + } } diff --git a/core/src/main/java/jenkins/diagnostics/ControllerExecutorsAgents.java b/core/src/main/java/jenkins/diagnostics/ControllerExecutorsAgents.java index f13df36018..31711ccbff 100644 --- a/core/src/main/java/jenkins/diagnostics/ControllerExecutorsAgents.java +++ b/core/src/main/java/jenkins/diagnostics/ControllerExecutorsAgents.java @@ -37,7 +37,7 @@ import org.kohsuke.stapler.interceptor.RequirePOST; import java.io.IOException; @Extension -@Symbol("controllerExecutorsWithAgents") +@Symbol({"executorsOnBuiltInNodeWithAgents", "controllerExecutorsWithAgents"}) @Restricted(NoExternalUse.class) public class ControllerExecutorsAgents extends AdministrativeMonitor { @@ -63,7 +63,7 @@ public class ControllerExecutorsAgents extends AdministrativeMonitor { disable(true); rsp.sendRedirect(req.getContextPath() + "/manage"); } else if (req.hasParameter("yes")) { - rsp.sendRedirect(req.getContextPath() + "/computer/(master)/configure"); + rsp.sendRedirect(req.getContextPath() + "/computer/(built-in)/configure"); } } diff --git a/core/src/main/java/jenkins/diagnostics/ControllerExecutorsNoAgents.java b/core/src/main/java/jenkins/diagnostics/ControllerExecutorsNoAgents.java index 52f5ae5517..bf036ba2b0 100644 --- a/core/src/main/java/jenkins/diagnostics/ControllerExecutorsNoAgents.java +++ b/core/src/main/java/jenkins/diagnostics/ControllerExecutorsNoAgents.java @@ -37,7 +37,7 @@ import org.kohsuke.stapler.interceptor.RequirePOST; import java.io.IOException; @Extension -@Symbol("controllerExecutorsWithoutAgents") +@Symbol({"executorsOnBuiltInNodeWithoutAgents", "controllerExecutorsWithoutAgents"}) @Restricted(NoExternalUse.class) public class ControllerExecutorsNoAgents extends AdministrativeMonitor { diff --git a/core/src/main/java/jenkins/model/BuiltInNodeMigration.java b/core/src/main/java/jenkins/model/BuiltInNodeMigration.java new file mode 100644 index 0000000000..744bdad0d3 --- /dev/null +++ b/core/src/main/java/jenkins/model/BuiltInNodeMigration.java @@ -0,0 +1,67 @@ +/* + * The MIT License + * + * Copyright (c) 2021 Daniel Beck + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package jenkins.model; + +import hudson.Extension; +import hudson.model.AdministrativeMonitor; +import org.jenkinsci.Symbol; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; +import org.kohsuke.stapler.QueryParameter; +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.StaplerResponse; +import org.kohsuke.stapler.interceptor.RequirePOST; + +import javax.servlet.ServletException; +import java.io.IOException; + +/** + * Inform the admin about the migration. This affects the parts of the rename + * that cannot be done compatibly: The self-label (although we could probably + * improvise something), and the node name as injected into build environments. + */ +@Extension +@Restricted(NoExternalUse.class) +@Symbol("builtinNodeMigration") +public class BuiltInNodeMigration extends AdministrativeMonitor { + @Override + public boolean isActivated() { + return !Jenkins.get().getRenameMigrationDone(); + } + + @RequirePOST + public void doAct(StaplerRequest req, StaplerResponse rsp, @QueryParameter String yes, @QueryParameter String no) throws IOException, ServletException { + if (yes != null) { + Jenkins.get().performRenameMigration(); + } else if (no != null) { + disable(true); + } + rsp.forwardToPreviousPage(req); + } + + @Override + public String getDisplayName() { + return Messages.BuiltInNodeMigration_DisplayName(); + } +} diff --git a/core/src/main/java/jenkins/model/CoreEnvironmentContributor.java b/core/src/main/java/jenkins/model/CoreEnvironmentContributor.java index d7d73cccfc..79d706e0a7 100644 --- a/core/src/main/java/jenkins/model/CoreEnvironmentContributor.java +++ b/core/src/main/java/jenkins/model/CoreEnvironmentContributor.java @@ -60,7 +60,7 @@ public class CoreEnvironmentContributor extends EnvironmentContributor { Executor e = (Executor) t; env.put("EXECUTOR_NUMBER", String.valueOf(e.getNumber())); if (e.getOwner() instanceof MasterComputer) { - env.put("NODE_NAME", "master"); + env.put("NODE_NAME", Jenkins.get().getSelfLabel().getName()); } else { env.put("NODE_NAME", e.getOwner().getName()); } diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index 2f1138a7d7..3c43012c63 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -672,10 +672,13 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve private transient Set agentProtocols; /** - * Whitespace-separated labels assigned to the master as a {@link Node}. + * Whitespace-separated labels assigned to the built-in node as a {@link Node}. */ private String label=""; + //@SuppressFBWarnings("MS_SHOULD_BE_FINAL") + private static /* non-final for Groovy */ String nodeNameAndSelfLabelOverride = SystemProperties.getString(Jenkins.class.getName() + ".nodeNameAndSelfLabelOverride"); + /** * {@link hudson.security.csrf.CrumbIssuer} */ @@ -859,6 +862,14 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve */ private Boolean noUsageStatistics; + /** + * If this is false, no migration is needed to reconfigure the built-in node (formerly 'master', now 'built-in'). + * Otherwise, {@link BuiltInNodeMigration} will show up. + */ + // See #readResolve for null -> true transition and #save for null -> false transition + @Restricted(NoExternalUse.class) + /* package-private */ Boolean nodeRenameMigrationNeeded; + /** * HTTP proxy configuration. */ @@ -1030,7 +1041,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve } catch (Exception e) { // Per Javadoc log exceptions but still go online. // NOTE: this does not include Errors, which indicate a fatal problem - LOGGER.log(WARNING, String.format("Exception in onOnline() for the computer listener %s on the Jenkins master node", + LOGGER.log(WARNING, String.format("Exception in onOnline() for the computer listener %s on the built-in node", cl.getClass()), e); } } @@ -1081,6 +1092,12 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve // no longer persisted installStateName = null; + + if (nodeRenameMigrationNeeded == null) { + /* deserializing without a value set means we need to migrate */ + nodeRenameMigrationNeeded = true; + } + return this; } @@ -2036,7 +2053,8 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve @CLIResolver public @CheckForNull Computer getComputer(@Argument(required=true,metaVar="NAME",usage="Node name") @NonNull String name) { - if(name.equals("(master)")) + if(name.equals("(built-in)") + || name.equals("(master)")) // backwards compatibility for URLs name = ""; for (Computer c : computers.values()) { @@ -2179,7 +2197,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve /** * Returns all {@link Node}s in the system, excluding {@link Jenkins} instance itself which - * represents the master. + * represents the built-in node (in other words, this only returns agents). */ @Override @NonNull @@ -3212,9 +3230,29 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve @Override public LabelAtom getSelfLabel() { + if (nodeNameAndSelfLabelOverride != null) { + return getLabelAtom(nodeNameAndSelfLabelOverride); + } + if (getRenameMigrationDone()) { + return getLabelAtom("built-in"); + } return getLabelAtom("master"); } + /* package */ boolean getRenameMigrationDone() { + if (nodeRenameMigrationNeeded == null) { + /* This is exceptionally unlikely to occur since we replace 'null' with 'true' on save */ + return true; + } + return !nodeRenameMigrationNeeded; + } + + /* package */ void performRenameMigration() throws IOException { + this.nodeRenameMigrationNeeded = false; + this.save(); + this.trimLabels(); + } + @Override @NonNull public Computer createComputer() { @@ -3470,6 +3508,14 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve LOGGER.log(FINE, "refusing to set version {0} to {1} during {2}", new Object[] {version, VERSION, currentMilestone}); } + if (nodeRenameMigrationNeeded == null) { + /* + If we initialized this object bypassing #readResolve, i.e. a new instance, + we need to persist this value, otherwise on restart we'd flag this as migration needed. + */ + nodeRenameMigrationNeeded = false; + } + getConfigFile().write(this); SaveableListener.fireOnChange(this, getConfigFile()); } @@ -4065,7 +4111,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve } } if (toComputer() == null) { - future.put("master", RemotingDiagnostics.getThreadDumpAsync(FilePath.localChannel)); + future.put("master", RemotingDiagnostics.getThreadDumpAsync(FilePath.localChannel)); // TODO(terminology) Built-in node? Controller? How is this used? } // if the result isn't available in 5 sec, ignore that. @@ -4411,7 +4457,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve private static Lifecycle restartableLifecycle() throws RestartNotSupportedException { if (Main.isUnitTest) { - throw new RestartNotSupportedException("Restarting the master JVM is not supported in JenkinsRule-based tests"); + throw new RestartNotSupportedException("Restarting the controller JVM is not supported in JenkinsRule-based tests"); } Lifecycle lifecycle = Lifecycle.get(); lifecycle.verifyRestartable(); @@ -5088,7 +5134,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve @Override public String getUrl() { - return "computer/(master)/"; + return "computer/(built-in)/"; } @Override diff --git a/core/src/main/java/jenkins/model/MasterBuildConfiguration.java b/core/src/main/java/jenkins/model/MasterBuildConfiguration.java index 0916394043..b7a5c66b8c 100644 --- a/core/src/main/java/jenkins/model/MasterBuildConfiguration.java +++ b/core/src/main/java/jenkins/model/MasterBuildConfiguration.java @@ -36,7 +36,7 @@ import java.io.IOException; * * @author Kohsuke Kawaguchi */ -@Extension(ordinal=500) @Symbol("masterBuild") +@Extension(ordinal=500) @Symbol({"builtInNode", "masterBuild"}) public class MasterBuildConfiguration extends GlobalConfiguration { public int getNumExecutors() { return Jenkins.get().getNumExecutors(); @@ -57,8 +57,8 @@ public class MasterBuildConfiguration extends GlobalConfiguration { } j.setNumExecutors(json.getInt("numExecutors")); - if (req.hasParameter("master.mode")) - j.setMode(Mode.valueOf(req.getParameter("master.mode"))); + if (req.hasParameter("builtin.mode")) + j.setMode(Mode.valueOf(req.getParameter("builtin.mode"))); else j.setMode(Mode.NORMAL); diff --git a/core/src/main/java/jenkins/security/s2m/AdminCallableMonitor.java b/core/src/main/java/jenkins/security/s2m/AdminCallableMonitor.java index 921ffa0db5..c0d3db7f7d 100644 --- a/core/src/main/java/jenkins/security/s2m/AdminCallableMonitor.java +++ b/core/src/main/java/jenkins/security/s2m/AdminCallableMonitor.java @@ -21,7 +21,7 @@ import java.io.IOException; * @since 1.587 / 1.580.1 * @author Kohsuke Kawaguchi */ -@Extension @Symbol("slaveToMasterAccessControl") +@Extension @Symbol({"agentToControllerAccessControl", "slaveToMasterAccessControl"}) public class AdminCallableMonitor extends AdministrativeMonitor { @Inject Jenkins jenkins; @@ -30,7 +30,7 @@ public class AdminCallableMonitor extends AdministrativeMonitor { AdminWhitelistRule rule; public AdminCallableMonitor() { - super("slaveToMasterAccessControl"); + super("slaveToMasterAccessControl"); // TODO Can we change this while retaining compatibility? } @Override diff --git a/core/src/main/java/jenkins/security/s2m/CallableDirectionChecker.java b/core/src/main/java/jenkins/security/s2m/CallableDirectionChecker.java index 546f398e69..8712737308 100644 --- a/core/src/main/java/jenkins/security/s2m/CallableDirectionChecker.java +++ b/core/src/main/java/jenkins/security/s2m/CallableDirectionChecker.java @@ -52,17 +52,17 @@ public class CallableDirectionChecker extends RoleChecker { final String name = subject.getClass().getName(); if (expected.contains(Roles.MASTER)) { - LOGGER.log(Level.FINE, "Executing {0} is allowed since it is targeted for the master role", name); + LOGGER.log(Level.FINE, "Executing {0} is allowed since it is targeted for the controller role", name); return; // known to be safe } if (isWhitelisted(subject,expected)) { // this subject is dubious, but we are letting it through as per whitelisting - LOGGER.log(Level.FINE, "Explicitly allowing {0} to be sent from agent to master", name); + LOGGER.log(Level.FINE, "Explicitly allowing {0} to be sent from agent to controller", name); return; } - throw new SecurityException("Sending " + name + " from agent to master is prohibited.\nSee https://www.jenkins.io/redirect/security-144 for more details"); + throw new SecurityException("Sending " + name + " from agent to controller is prohibited.\nSee https://www.jenkins.io/redirect/security-144 for more details"); } /** diff --git a/core/src/main/java/jenkins/security/s2m/MasterKillSwitchConfiguration.java b/core/src/main/java/jenkins/security/s2m/MasterKillSwitchConfiguration.java index 3e69570589..ddfb774226 100644 --- a/core/src/main/java/jenkins/security/s2m/MasterKillSwitchConfiguration.java +++ b/core/src/main/java/jenkins/security/s2m/MasterKillSwitchConfiguration.java @@ -11,7 +11,7 @@ import net.sf.json.JSONObject; import org.kohsuke.stapler.StaplerRequest; /** - * Exposes {@link AdminWhitelistRule#masterKillSwitch} to the admin. + * Exposes {@code AdminWhitelistRule#masterKillSwitch} to the admin. * * @author Kohsuke Kawaguchi * @since 1.587 / 1.580.1 @@ -38,7 +38,7 @@ public class MasterKillSwitchConfiguration extends GlobalConfiguration { if (isRelevant()) { // don't record on/off unless this becomes relevant, so that we can differentiate // those who have disabled vs those who haven't cared. - rule.setMasterKillSwitch(!json.has("masterToSlaveAccessControl")); + rule.setMasterKillSwitch(!json.has("agentToControllerAccessControl")); } return true; } diff --git a/core/src/main/java/jenkins/slaves/DefaultJnlpSlaveReceiver.java b/core/src/main/java/jenkins/slaves/DefaultJnlpSlaveReceiver.java index 0da6efa0e6..7b22dc27aa 100644 --- a/core/src/main/java/jenkins/slaves/DefaultJnlpSlaveReceiver.java +++ b/core/src/main/java/jenkins/slaves/DefaultJnlpSlaveReceiver.java @@ -137,7 +137,7 @@ public class DefaultJnlpSlaveReceiver extends JnlpAgentReceiver { } } else { event.reject(new ConnectionRefusalException(String.format( - "%s is already connected to this master. Rejecting this connection.", clientName))); + "%s is already connected to this controller. Rejecting this connection.", clientName))); return; } } diff --git a/core/src/main/java/jenkins/util/JenkinsJVM.java b/core/src/main/java/jenkins/util/JenkinsJVM.java index f8e37a79a1..fafe1223a3 100644 --- a/core/src/main/java/jenkins/util/JenkinsJVM.java +++ b/core/src/main/java/jenkins/util/JenkinsJVM.java @@ -43,7 +43,7 @@ public class JenkinsJVM { */ public static void checkJenkinsJVM() { if (!isJenkinsJVM()) { - throw new IllegalStateException("Not running on the Jenkins master JVM"); + throw new IllegalStateException("Not running on the Jenkins controller JVM"); } } @@ -55,7 +55,7 @@ public class JenkinsJVM { */ public static void checkNotJenkinsJVM() { if (isJenkinsJVM()) { - throw new IllegalStateException("Running on the Jenkins master JVM"); + throw new IllegalStateException("Running on the Jenkins controller JVM"); } } diff --git a/core/src/main/resources/hudson/model/AbstractProject/help-label.html b/core/src/main/resources/hudson/model/AbstractProject/help-label.html index df1b6a104a..24034668e2 100644 --- a/core/src/main/resources/hudson/model/AbstractProject/help-label.html +++ b/core/src/main/resources/hudson/model/AbstractProject/help-label.html @@ -5,8 +5,8 @@ expression evaluates to true, then that agent will be allowed to execute builds of this project.

- If this project should always be built on a specific agent, or on the Jenkins - master, then you can just enter the agent's name, or master, + If this project should always be built on a specific agent, or on the built-in + node, then you can just enter the agent's name, or built-in, respectively.

However, you should generally avoid using the Name of an agent here, @@ -98,8 +98,8 @@

Examples

-
master
-
Builds of this project may be executed only on the Jenkins master
+
built-in
+
Builds of this project may be executed only on the built-in node
linux-machine-42
diff --git a/core/src/main/resources/hudson/model/AbstractProject/help-label_bg.html b/core/src/main/resources/hudson/model/AbstractProject/help-label_bg.html index 194bd86cb9..1cae56db1a 100644 --- a/core/src/main/resources/hudson/model/AbstractProject/help-label_bg.html +++ b/core/src/main/resources/hudson/model/AbstractProject/help-label_bg.html @@ -6,7 +6,7 @@

Ако проектът трябва задължително да се изгражда на определен подчинен компютър или на основния, въведете съответно името на компютъра или - master. + built-in.

Като цяло трябва да избягвате употребата на името на подчинения компютър като вместо това използвате етикетите на компютрите. Както е @@ -96,7 +96,7 @@

Примери

-
master
+
built-in
Изгражданията на този проект може да са само на основния компютър на Jenkins.
diff --git a/core/src/main/resources/hudson/model/AbstractProject/help-label_it.html b/core/src/main/resources/hudson/model/AbstractProject/help-label_it.html index 1cf1afb930..8014cef95a 100644 --- a/core/src/main/resources/hudson/model/AbstractProject/help-label_it.html +++ b/core/src/main/resources/hudson/model/AbstractProject/help-label_it.html @@ -104,7 +104,7 @@

Esempi

-
master
+
built-in
Le compilazioni di questo progetto possono eseguite solo sul nodo master di Jenkins diff --git a/core/src/main/resources/hudson/model/Messages.properties b/core/src/main/resources/hudson/model/Messages.properties index fdf54887f0..5ee1cdb2a2 100644 --- a/core/src/main/resources/hudson/model/Messages.properties +++ b/core/src/main/resources/hudson/model/Messages.properties @@ -23,7 +23,7 @@ # THE SOFTWARE. AbstractBuild.BuildingRemotely=Building remotely on {0} -AbstractBuild.BuildingOnMaster=Building on master +AbstractBuild.BuildingOnMaster=Building on the built-in node AbstractBuild_Building=Building AbstractBuild.BuildingInWorkspace=\ in workspace {0} AbstractBuild.KeptBecause=This build is kept because of {0}. @@ -106,7 +106,7 @@ Computer.CreatePermission.Description=This permission allows users to create age Computer.ConnectPermission.Description=This permission allows users to connect agents or mark agents as online. Computer.DisconnectPermission.Description=This permission allows users to disconnect agents or mark agents as temporarily offline. Computer.BuildPermission.Description=This permission allows users to run jobs as them on agents. -Computer.BadChannel=Agent node offline or not a remote channel (such as master node). +Computer.BadChannel=Agent node offline or not a remote channel (such as the built-in node). ComputerSet.NoSuchSlave=No such agent: {0} ComputerSet.SlaveAlreadyExists=Agent called \u2018{0}\u2019 already exists @@ -125,8 +125,8 @@ FreeStyleProject.Description=\ HealthReport.EmptyString= Hudson.BadPortNumber=Bad port number {0} -Hudson.Computer.Caption=Master -Hudson.Computer.DisplayName=master +Hudson.Computer.Caption=Built-In Node +Hudson.Computer.DisplayName=Built-In Node Hudson.ControlCodeNotAllowed=No control code is allowed: {0} Hudson.DisplayName=Jenkins Hudson.JobAlreadyExists=A job already exists with the name \u2018{0}\u2019 @@ -168,7 +168,7 @@ Hudson.ReadPermission.Description=\ add "authenticated" pseudo-user and grant the read access. Hudson.RunScriptsPermission.Description=\ Deprecated - please use the Overall/Administer permission instead -Hudson.NodeDescription=the master Jenkins node +Hudson.NodeDescription=the Jenkins controller''s built-in node Item.Permissions.Title=Job Item.CREATE.description=Create a new job. @@ -253,7 +253,7 @@ Run.Summary.Unknown=? Slave.InvalidConfig.Executors=Invalid agent configuration for {0}. Invalid number of executors. Slave.InvalidConfig.NoName=Invalid agent configuration. Name is empty -Slave.Network.Mounted.File.System.Warning=Are you sure you want to use network mounted file system for FS root? Note that this directory does not need to be visible to the master. +Slave.Network.Mounted.File.System.Warning=Are you sure you want to use network mounted file system for FS root? Note that this directory does not need to be visible to the controller. Slave.Remote.Director.Mandatory=Remote directory is mandatory Slave.Terminated={0} agent was terminated Slave.Remote.Relative.Path.Warning=Are you sure you want to use a relative path for the FS root? Note that relative \ diff --git a/core/src/main/resources/hudson/model/Node/help-numExecutors.html b/core/src/main/resources/hudson/model/Node/help-numExecutors.html index 62ee113d35..89d1af5179 100644 --- a/core/src/main/resources/hudson/model/Node/help-numExecutors.html +++ b/core/src/main/resources/hudson/model/Node/help-numExecutors.html @@ -9,12 +9,12 @@ the second build could take advantage of the spare I/O capacity at that moment.

- Agents (nodes that are not the master) must have at least one executor. + Agents (nodes that are not the built-in node) must have at least one executor. To temporarily prevent any builds from being executed on an agent, use the Mark this node temporarily offline button on the agent's page.

- For the master, set the number of executors to zero to prevent it from - executing builds locally. - Note: master will always be able to run flyweight tasks including + For the built-in node, set the number of executors to zero to prevent it from + executing builds locally on the controller. + Note: The built-in node will always be able to run flyweight tasks including Pipeline's top-level task. diff --git a/core/src/main/resources/hudson/model/PasswordParameterDefinition/help.html b/core/src/main/resources/hudson/model/PasswordParameterDefinition/help.html index 0b9e3662e5..e78469f695 100644 --- a/core/src/main/resources/hudson/model/PasswordParameterDefinition/help.html +++ b/core/src/main/resources/hudson/model/PasswordParameterDefinition/help.html @@ -1,5 +1,5 @@

Pass a password to your build. The password entered here is made available to the build in plain text as an environment variable like a string parameter would be. - The value will be stored encrypted on the Jenkins master, similar to passwords in Jenkins configuration. + The value will be stored encrypted on the Jenkins controller, similar to passwords in Jenkins configuration.
\ No newline at end of file diff --git a/core/src/main/resources/hudson/model/Slave/help-remoteFS.html b/core/src/main/resources/hudson/model/Slave/help-remoteFS.html index 9b74e2e95c..38c0b42e27 100644 --- a/core/src/main/resources/hudson/model/Slave/help-remoteFS.html +++ b/core/src/main/resources/hudson/model/Slave/help-remoteFS.html @@ -4,10 +4,10 @@ the path to this directory on the agent. It is best to use an absolute path, such as /var/jenkins or c:\jenkins. This should be a path local to the agent machine. There is no need for - this path to be visible from the master. + this path to be visible from the controller.

Agents do not maintain important data; all job configurations, build logs and - artifacts are stored on the master, so it would be possible to use a temporary + artifacts are stored on the controller, so it would be possible to use a temporary directory as the agent root directory.
However, by giving an agent a directory that is not deleted after a machine diff --git a/core/src/main/resources/hudson/node_monitors/ClockMonitor/help.html b/core/src/main/resources/hudson/node_monitors/ClockMonitor/help.html index 31c27afba0..4311cc9b4f 100644 --- a/core/src/main/resources/hudson/node_monitors/ClockMonitor/help.html +++ b/core/src/main/resources/hudson/node_monitors/ClockMonitor/help.html @@ -1,5 +1,5 @@

- This monitors the clock difference between the master and nodes. + This monitors the clock difference between the controller and nodes. While Jenkins itself is generally capable of tolerating clock differences between systems, version control activities and distributed file access (such as NFS, Windows file shares) tend to have strange problems when systems involved have different clocks. diff --git a/core/src/main/resources/hudson/node_monitors/ResponseTimeMonitor/help.html b/core/src/main/resources/hudson/node_monitors/ResponseTimeMonitor/help.html index 5a6f27a412..977072ab4b 100644 --- a/core/src/main/resources/hudson/node_monitors/ResponseTimeMonitor/help.html +++ b/core/src/main/resources/hudson/node_monitors/ResponseTimeMonitor/help.html @@ -1,9 +1,9 @@
- This monitors the round trip network response time from the master to the agent, and if it + This monitors the round trip network response time from the controller to the agent, and if it goes above a threshold repeatedly, it marks the agent offline.

This is useful for detecting unresponsive agents, or other network problems that clog the - communication channel. More specifically, the master sends a no-op command to the agent, + communication channel. More specifically, the controller sends a no-op command to the agent, and checks the time it takes to get back the result of this no-op command.

\ No newline at end of file diff --git a/core/src/main/resources/hudson/node_monitors/TemporarySpaceMonitor/help.html b/core/src/main/resources/hudson/node_monitors/TemporarySpaceMonitor/help.html index d285370f91..a6626679f9 100644 --- a/core/src/main/resources/hudson/node_monitors/TemporarySpaceMonitor/help.html +++ b/core/src/main/resources/hudson/node_monitors/TemporarySpaceMonitor/help.html @@ -9,5 +9,5 @@

More specifically, it checks the available disk space of a partition that includes directory designated by the java.io.tmpdir system property. - To check where this directory is on a given agent, go to ${rootURL}/computer/SLAVENAME/systemInfo. + To check where this directory is on a given agent, go to ${rootURL}/computer/AGENTNAME/systemInfo.

\ No newline at end of file diff --git a/core/src/main/resources/hudson/node_monitors/TemporarySpaceMonitor/help_bg.html b/core/src/main/resources/hudson/node_monitors/TemporarySpaceMonitor/help_bg.html index 861a5d3e98..c2f38e2985 100644 --- a/core/src/main/resources/hudson/node_monitors/TemporarySpaceMonitor/help_bg.html +++ b/core/src/main/resources/hudson/node_monitors/TemporarySpaceMonitor/help_bg.html @@ -12,5 +12,5 @@ По-специално, проверката се извършва на файловата система, която съдържа директорията указвана от системното свойство „java.io.tmpdir“. За да проверите къде тя се намира на определена машина, прегледайте файла - „${rootURL}/computer/SLAVENAME/systemInfo“. + „${rootURL}/computer/AGENTNAME/systemInfo“. diff --git a/core/src/main/resources/hudson/node_monitors/TemporarySpaceMonitor/help_ja.html b/core/src/main/resources/hudson/node_monitors/TemporarySpaceMonitor/help_ja.html index 9654d171ef..3a553569d7 100644 --- a/core/src/main/resources/hudson/node_monitors/TemporarySpaceMonitor/help_ja.html +++ b/core/src/main/resources/hudson/node_monitors/TemporarySpaceMonitor/help_ja.html @@ -7,5 +7,5 @@

もっと具体的に言うと、システムプロパティjava.io.tmpdirで指定されたディレクトリを含むパーティションの利用可能なディスク容量をチェックします。 - エージェントでのこのディレクトリが何であるかチェックするには、${rootURL}/computer/SLAVENAME/systemInfoを参照してください。 + エージェントでのこのディレクトリが何であるかチェックするには、${rootURL}/computer/AGENTNAME/systemInfoを参照してください。 \ No newline at end of file diff --git a/core/src/main/resources/hudson/slaves/JNLPLauncher/help.properties b/core/src/main/resources/hudson/slaves/JNLPLauncher/help.properties index a14577f0f2..d49d20b0ed 100644 --- a/core/src/main/resources/hudson/slaves/JNLPLauncher/help.properties +++ b/core/src/main/resources/hudson/slaves/JNLPLauncher/help.properties @@ -21,14 +21,14 @@ # THE SOFTWARE. blurb=\ - Allows an agent to be connected to the Jenkins master whenever it is ready.
\ + Allows an agent to be connected to the Jenkins controller whenever it is ready.
\ In one mode, Java Web Start is used. \ In this case, a JNLP file must be opened on the agent machine, \ - which will establish a TCP connection to the Jenkins master. \ + which will establish a TCP connection to the Jenkins controller. \ (Other launch methods use a JNLP file but not Java Web Start, or do not use a JNLP file at all.)
\ - This means that the agent need not be reachable from the master; \ - the agent just needs to be able to reach the master. \ + This means that the agent need not be reachable from the controller; \ + the agent just needs to be able to reach the controller. \ If you have enabled security via the Configure Global Security page, \ - you can customize the port on which the Jenkins master will listen for incoming agent connections.
\ + you can customize the port on which the Jenkins controller will listen for incoming agent connections.
\ By default, the agent will launch a GUI, but it is also possible to run \ an agent without a GUI, for example as a Windows service. diff --git a/core/src/main/resources/hudson/slaves/Messages.properties b/core/src/main/resources/hudson/slaves/Messages.properties index e767585075..bf2dd8dc8f 100644 --- a/core/src/main/resources/hudson/slaves/Messages.properties +++ b/core/src/main/resources/hudson/slaves/Messages.properties @@ -23,7 +23,7 @@ RetentionStrategy.Always.displayName=Keep this agent online as much as possible RetentionStrategy.Demand.displayName=Bring this agent online when in demand, and take offline when idle RetentionStrategy.Demand.OfflineIdle=Offline because computer was idle; it will be relaunched when needed. -JNLPLauncher.displayName=Launch agent by connecting it to the master +JNLPLauncher.displayName=Launch agent by connecting it to the controller ComputerLauncher.unexpectedError=Unexpected error in launching an agent. This is probably a bug in Jenkins ComputerLauncher.abortedLaunch=Launching agent process aborted. ConnectionActivityMonitor.OfflineCause=Repeated ping attempts failed diff --git a/core/src/main/resources/hudson/tools/Messages.properties b/core/src/main/resources/hudson/tools/Messages.properties index 5eeb9f12f0..ab80fbb894 100644 --- a/core/src/main/resources/hudson/tools/Messages.properties +++ b/core/src/main/resources/hudson/tools/Messages.properties @@ -30,5 +30,5 @@ ZipExtractionInstaller.bad_connection=Server rejected connection. ZipExtractionInstaller.malformed_url=Malformed URL. ZipExtractionInstaller.could_not_connect=Could not connect to URL. InstallSourceProperty.DescriptorImpl.displayName=Install automatically -ToolDescriptor.NotADirectory={0} is not a directory on the Jenkins master (but perhaps it exists on some agents) +ToolDescriptor.NotADirectory={0} is not a directory on the Jenkins controller (but perhaps it exists on some agents) CannotBeInstalled=Installer "{0}" cannot be used to install "{1}" on the node "{2}" diff --git a/core/src/main/resources/hudson/tools/ZipExtractionInstaller/help-url.html b/core/src/main/resources/hudson/tools/ZipExtractionInstaller/help-url.html index 9637265029..f22666f219 100644 --- a/core/src/main/resources/hudson/tools/ZipExtractionInstaller/help-url.html +++ b/core/src/main/resources/hudson/tools/ZipExtractionInstaller/help-url.html @@ -3,5 +3,5 @@ Should be either a ZIP or a GZip-compressed TAR file. The timestamp on the server will be compared to the local version (if any) so you can publish updates easily. - The URL must be accessible from the Jenkins master but need not be accessible from agents. + The URL must be accessible from the Jenkins controller but need not be accessible from agents. diff --git a/core/src/main/resources/jenkins/diagnostics/ControllerExecutorsAgents/message.properties b/core/src/main/resources/jenkins/diagnostics/ControllerExecutorsAgents/message.properties index 0cbc65ecc0..b1dbea82dc 100644 --- a/core/src/main/resources/jenkins/diagnostics/ControllerExecutorsAgents/message.properties +++ b/core/src/main/resources/jenkins/diagnostics/ControllerExecutorsAgents/message.properties @@ -20,6 +20,7 @@ #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN #THE SOFTWARE. -ExecutorsWarning=Building on the controller node can be a security issue. \ - You should set the number of executors on the controller to 0. \ +ExecutorsWarning=Building on the built-in node can be a security issue. \ + You should set the number of executors on the built-in node to 0. \ See the documentation. +# TODO(terminology) rename redirect URL diff --git a/core/src/main/resources/jenkins/diagnostics/ControllerExecutorsNoAgents/message.properties b/core/src/main/resources/jenkins/diagnostics/ControllerExecutorsNoAgents/message.properties index eb387c79f4..7f981c08d6 100644 --- a/core/src/main/resources/jenkins/diagnostics/ControllerExecutorsNoAgents/message.properties +++ b/core/src/main/resources/jenkins/diagnostics/ControllerExecutorsNoAgents/message.properties @@ -20,6 +20,7 @@ #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN #THE SOFTWARE. -ExecutorsWarning=Building on the controller node can be a security issue. \ +ExecutorsWarning=Building on the built-in node can be a security issue. \ You should set up distributed builds. \ See the documentation. +# TODO(terminology) rename redirect URL diff --git a/core/src/main/resources/jenkins/diagnostics/Messages.properties b/core/src/main/resources/jenkins/diagnostics/Messages.properties index 16097ded39..f6ccd27507 100644 --- a/core/src/main/resources/jenkins/diagnostics/Messages.properties +++ b/core/src/main/resources/jenkins/diagnostics/Messages.properties @@ -2,5 +2,5 @@ CompletedInitializationMonitor.DisplayName=Jenkins Initialization Monitor SecurityIsOffMonitor.DisplayName=Disabled Security URICheckEncodingMonitor.DisplayName=Check URI Encoding RootUrlNotSetMonitor.DisplayName=Root URL not configured Monitor -ControllerExecutorsNoAgents.DisplayName=Controller Without Agents Executors Configured Monitor -ControllerExecutorsAgents.DisplayName=Controller With Agents Executors Configured Monitor +ControllerExecutorsNoAgents.DisplayName=Built-in Node Without Agents Executors Configured Monitor +ControllerExecutorsAgents.DisplayName=Built-in Node With Agents Executors Configured Monitor diff --git a/core/src/main/resources/jenkins/model/BuiltInNodeMigration/message.jelly b/core/src/main/resources/jenkins/model/BuiltInNodeMigration/message.jelly new file mode 100644 index 0000000000..82037ac2e4 --- /dev/null +++ b/core/src/main/resources/jenkins/model/BuiltInNodeMigration/message.jelly @@ -0,0 +1,10 @@ + + +

+
+ + + + ${%blurb} +
+ diff --git a/core/src/main/resources/jenkins/model/BuiltInNodeMigration/message.properties b/core/src/main/resources/jenkins/model/BuiltInNodeMigration/message.properties new file mode 100644 index 0000000000..10f3afcf7b --- /dev/null +++ b/core/src/main/resources/jenkins/model/BuiltInNodeMigration/message.properties @@ -0,0 +1,40 @@ +# The MIT License +# +# Copyright (c) 2021 Daniel Beck +# +# 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. + +blurb = The word "master" is being retired as the term for the main Jenkins process and the built-in node. \ + The main process is now called "controller" and the built-in node is called just "built-in node". \ + The UI has been updated with these changes. \ + The following features are also affected: \ +
    \ +
  • The implicit label of the built-in node changes from master to built-in.
  • \ +
  • The built-in node''s NODE_NAME environment variable also changes from master to built-in.
  • \ +
\ + These changes could affect build behavior, so are not applied automatically. \ + Before you apply these changes, you should do the following: \ +
    \ +
  • Review label assignments in job configurations and tool installers for uses of master label. \ + Any such label assignments will not match the built-in node after migration. \ + Besides updating these assignments, you could also explicitly add the master label to the built-in node. \ +
  • \ +
  • Review use of the NODE_NAME environment variable in build scripts.
  • \ +
\ + Learn more. diff --git a/core/src/main/resources/jenkins/model/CoreEnvironmentContributor/buildEnv.properties b/core/src/main/resources/jenkins/model/CoreEnvironmentContributor/buildEnv.properties index 11c7828ddb..9e4ecf591b 100644 --- a/core/src/main/resources/jenkins/model/CoreEnvironmentContributor/buildEnv.properties +++ b/core/src/main/resources/jenkins/model/CoreEnvironmentContributor/buildEnv.properties @@ -10,14 +10,14 @@ EXECUTOR_NUMBER.blurb=\ (among executors of the same machine) that\u2019s \ carrying out this build. This is the number you see in \ the "build executor status", except that the number starts from 0, not 1. -NODE_NAME.blurb=Name of the agent if the build is on an agent, or "master" if run on master. +NODE_NAME.blurb=Name of the agent if the build is on an agent, or "built-in" if run on the built-in node (or "master" until Jenkins 2.TODO). NODE_LABELS.blurb=Whitespace-separated list of labels that the node is assigned. WORKSPACE.blurb=The absolute path of the directory assigned to the build as a workspace. WORKSPACE_TMP.blurb=\ A temporary directory near the workspace that will not be browsable and will not interfere with SCM checkouts. \ May not initially exist, so be sure to create the directory as needed (e.g., mkdir -p on Linux). \ Not defined when the regular workspace is a drive root. -JENKINS_HOME.blurb=The absolute path of the directory assigned on the master node for Jenkins to store data. +JENKINS_HOME.blurb=The absolute path of the directory assigned on the controller file system for Jenkins to store data. JENKINS_URL.blurb=Full URL of Jenkins, like http://server:port/jenkins/ (note: only available if Jenkins URL set in system configuration). BUILD_URL.blurb=Full URL of this build, like http://server:port/jenkins/job/foo/15/ (Jenkins URL must be set). JOB_URL.blurb=Full URL of this job, like http://server:port/jenkins/job/foo/ (Jenkins URL must be set). diff --git a/core/src/main/resources/jenkins/model/Jenkins/MasterComputer/configure.jelly b/core/src/main/resources/jenkins/model/Jenkins/MasterComputer/configure.jelly index 883b6b6a3e..e40af4263a 100644 --- a/core/src/main/resources/jenkins/model/Jenkins/MasterComputer/configure.jelly +++ b/core/src/main/resources/jenkins/model/Jenkins/MasterComputer/configure.jelly @@ -50,7 +50,7 @@ THE SOFTWARE. - + diff --git a/core/src/main/resources/jenkins/model/MasterBuildConfiguration/config.groovy b/core/src/main/resources/jenkins/model/MasterBuildConfiguration/config.groovy index 9ad0aaa0c5..e0e2224df7 100644 --- a/core/src/main/resources/jenkins/model/MasterBuildConfiguration/config.groovy +++ b/core/src/main/resources/jenkins/model/MasterBuildConfiguration/config.groovy @@ -8,4 +8,4 @@ f.entry(title:_("# of executors"), field:"numExecutors") { f.entry(title:_("Labels"),field:"labelString") { f.textbox() } -f.slave_mode(name:"master.mode",node:app) +f.slave_mode(name:"builtin.mode",node:app) diff --git a/core/src/main/resources/jenkins/model/Messages.properties b/core/src/main/resources/jenkins/model/Messages.properties index c7c1d079c7..d5afb130ac 100644 --- a/core/src/main/resources/jenkins/model/Messages.properties +++ b/core/src/main/resources/jenkins/model/Messages.properties @@ -24,8 +24,8 @@ # THE SOFTWARE. Hudson.BadPortNumber=Bad port number {0} -Hudson.Computer.Caption=Master -Hudson.Computer.DisplayName=master +Hudson.Computer.Caption=Built-in Node +Hudson.Computer.DisplayName=Built-in Hudson.Computer.IncorrectNumberOfExecutors=Incorrect field "Number of executors". It should be a non-negative number. Hudson.ControlCodeNotAllowed=No control code is allowed: {0} Hudson.DisplayName=Jenkins @@ -41,7 +41,7 @@ Hudson.NotUsesUTF8ToDecodeURL=\ Your container doesn\u2019t use UTF-8 to decode URLs. If you use non-ASCII characters as a job name etc, \ this will cause problems. \ See Tomcat i18n for more details. -Hudson.NodeDescription=the master Jenkins node +Hudson.NodeDescription=the Jenkins controller''s built-in node CLI.restart.shortDescription=Restart Jenkins. CLI.safe-restart.shortDescription=Safely restart Jenkins. @@ -77,5 +77,7 @@ CLI.enable-job.shortDescription=Enables a job. GlobalCloudConfiguration.DisplayName=Configure Clouds +BuiltInNodeMigration.DisplayName=Built-In Node Name and Label Migration + SimpleGlobalBuildDiscarderStrategy.displayName=Specific Build Discarder JobGlobalBuildDiscarderStrategy.displayName=Project Build Discarder diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/config.groovy b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/config.groovy index ae486437d3..40d93c8fd9 100644 --- a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/config.groovy +++ b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/config.groovy @@ -4,7 +4,7 @@ def f=namespace(lib.FormTagLib) if (instance.isRelevant()) { f.section(title: _('Agent \u2192 Controller Security')) { - f.optionalBlock(field: "masterToSlaveAccessControl", title: _("Enable Agent \u2192 Controller Access Control")) { + f.optionalBlock(field: "agentToControllerAccessControl", title: _("Enable Agent \u2192 Controller Access Control")) { f.nested() { raw _("Rules can be tweaked here") } diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/help-masterToSlaveAccessControl.html b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/help-agentToControllerAccessControl.html similarity index 100% rename from core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/help-masterToSlaveAccessControl.html rename to core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/help-agentToControllerAccessControl.html diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/help-masterToSlaveAccessControl_bg.html b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/help-agentToControllerAccessControl_bg.html similarity index 100% rename from core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/help-masterToSlaveAccessControl_bg.html rename to core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/help-agentToControllerAccessControl_bg.html diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/help-masterToSlaveAccessControl_it.html b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/help-agentToControllerAccessControl_it.html similarity index 100% rename from core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/help-masterToSlaveAccessControl_it.html rename to core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/help-agentToControllerAccessControl_it.html diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message.properties b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message.properties index 0470434922..04be9aaf93 100644 --- a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message.properties +++ b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message.properties @@ -1,2 +1,2 @@ -blurb=Agent to master security subsystem is currently off. \ +blurb=Agent to controller security subsystem is currently off. \ Please read the documentation and consider turning it on. diff --git a/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol4/description.properties b/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol4/description.properties index 9cf2b4b2f8..4ecb60357f 100644 --- a/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol4/description.properties +++ b/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol4/description.properties @@ -1 +1 @@ -summary=A TLS secured connection between the master and the agent performed by TLS upgrade of the socket. +summary=A TLS secured connection between the controller and the agent performed by TLS upgrade of the socket. diff --git a/core/src/main/resources/lib/hudson/executors.properties b/core/src/main/resources/lib/hudson/executors.properties index d21742283c..951d07ec26 100644 --- a/core/src/main/resources/lib/hudson/executors.properties +++ b/core/src/main/resources/lib/hudson/executors.properties @@ -1,3 +1,4 @@ -Computers=master{0,choice,0#|1# + {0,number} computer ({1} of {2} executors)|1< + {0,number} computers ({1} of {2} executors)} +# TODO This label should distinguish between having and not having executors on the built-in node +Computers=built-in node{0,choice,0# ({1} of {2} executors busy)|1# + {0,number} agent ({1} of {2} executors busy)|1< + {0,number} agents ({1} of {2} executors busy)} confirm=Are you sure you want to abort {0}? launching=launching... diff --git a/core/src/main/resources/lib/hudson/node.jelly b/core/src/main/resources/lib/hudson/node.jelly index d6a74ede8b..db88102acb 100644 --- a/core/src/main/resources/lib/hudson/node.jelly +++ b/core/src/main/resources/lib/hudson/node.jelly @@ -37,7 +37,7 @@ THE SOFTWARE. ${valueStr} - ${%master} + ${%builtin} \ No newline at end of file diff --git a/core/src/main/resources/lib/hudson/node_bg.properties b/core/src/main/resources/lib/hudson/node_bg.properties deleted file mode 100644 index 1b8e9a626e..0000000000 --- a/core/src/main/resources/lib/hudson/node_bg.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Bulgarian translation: Copyright (c) 2015, 2016, Alexander Shopov -# -# 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. - -master=\ - \u0423\u043f\u0440\u0430\u0432\u043b\u044f\u0432\u0430\u0449 \u043a\u043e\u043c\u043f\u044e\u0442\u044a\u0440 diff --git a/core/src/main/resources/lib/hudson/node_da.properties b/core/src/main/resources/lib/hudson/node_da.properties deleted file mode 100644 index 199e3db8bc..0000000000 --- a/core/src/main/resources/lib/hudson/node_da.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, Inc. Kohsuke Kawaguchi. Knud Poulsen. -# -# 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. - -master=master diff --git a/core/src/main/resources/lib/hudson/node_de.properties b/core/src/main/resources/lib/hudson/node_de.properties deleted file mode 100644 index 7871885968..0000000000 --- a/core/src/main/resources/lib/hudson/node_de.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Simon Wiest -# -# 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. - -master=Master diff --git a/core/src/main/resources/lib/hudson/node_es.properties b/core/src/main/resources/lib/hudson/node_es.properties deleted file mode 100644 index 9269e6a688..0000000000 --- a/core/src/main/resources/lib/hudson/node_es.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, 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. - -master=principal diff --git a/core/src/main/resources/lib/hudson/node_fr.properties b/core/src/main/resources/lib/hudson/node_fr.properties deleted file mode 100644 index 8d98360c00..0000000000 --- a/core/src/main/resources/lib/hudson/node_fr.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Eric Lefevre-Ardant -# -# 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. - -master=maître diff --git a/core/src/main/resources/lib/hudson/node_he.properties b/core/src/main/resources/lib/hudson/node_he.properties deleted file mode 100644 index 9af9d68d6e..0000000000 --- a/core/src/main/resources/lib/hudson/node_he.properties +++ /dev/null @@ -1,3 +0,0 @@ -# This file is under the MIT License by authors - -master=\u05E8\u05D0\u05E9\u05D9 diff --git a/core/src/main/resources/lib/hudson/node_it.properties b/core/src/main/resources/lib/hudson/node_it.properties deleted file mode 100644 index 27f0451747..0000000000 --- a/core/src/main/resources/lib/hudson/node_it.properties +++ /dev/null @@ -1,24 +0,0 @@ -# The MIT License -# -# Italian localization plugin for Jenkins -# Copyright © 2020 Alessandro Menti -# -# 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. - -master=master diff --git a/core/src/main/resources/lib/hudson/node_ja.properties b/core/src/main/resources/lib/hudson/node_ja.properties deleted file mode 100644 index 9fe631574c..0000000000 --- a/core/src/main/resources/lib/hudson/node_ja.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2011, Sun Microsystems, Inc., Kohsuke Kawaguchi, Seiji Sogabe -# -# 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. - -master=master diff --git a/core/src/main/resources/lib/hudson/node_ko.properties b/core/src/main/resources/lib/hudson/node_ko.properties deleted file mode 100644 index 4754bdb4f5..0000000000 --- a/core/src/main/resources/lib/hudson/node_ko.properties +++ /dev/null @@ -1,3 +0,0 @@ -# This file is under the MIT License by authors - -master=\uB9C8\uC2A4\uD130 diff --git a/core/src/main/resources/lib/hudson/node_lv.properties b/core/src/main/resources/lib/hudson/node_lv.properties deleted file mode 100644 index f17c6857dc..0000000000 --- a/core/src/main/resources/lib/hudson/node_lv.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, 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. - -master=master diff --git a/core/src/main/resources/lib/hudson/node_nb_NO.properties b/core/src/main/resources/lib/hudson/node_nb_NO.properties deleted file mode 100644 index f17c6857dc..0000000000 --- a/core/src/main/resources/lib/hudson/node_nb_NO.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, 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. - -master=master diff --git a/core/src/main/resources/lib/hudson/node_nl.properties b/core/src/main/resources/lib/hudson/node_nl.properties deleted file mode 100644 index 0b6e6f9046..0000000000 --- a/core/src/main/resources/lib/hudson/node_nl.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, id:sorokh -# -# 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. - -master=hoofdnode diff --git a/core/src/main/resources/lib/hudson/node_pl.properties b/core/src/main/resources/lib/hudson/node_pl.properties deleted file mode 100644 index c71caf50dc..0000000000 --- a/core/src/main/resources/lib/hudson/node_pl.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, 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. - -master= diff --git a/core/src/main/resources/lib/hudson/node_pt_BR.properties b/core/src/main/resources/lib/hudson/node_pt_BR.properties deleted file mode 100644 index c9b0c63452..0000000000 --- a/core/src/main/resources/lib/hudson/node_pt_BR.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Reginaldo L. Russinholi, Cleiber Silva, Fernando Boaglio -# -# 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. - -master=master diff --git a/core/src/main/resources/lib/hudson/node_ru.properties b/core/src/main/resources/lib/hudson/node_ru.properties deleted file mode 100644 index d16173a4b5..0000000000 --- a/core/src/main/resources/lib/hudson/node_ru.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Mike Salnikov -# -# 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. - -master=\u0413\u043b\u0430\u0432\u043d\u044b\u0439 diff --git a/core/src/main/resources/lib/hudson/node_sk.properties b/core/src/main/resources/lib/hudson/node_sk.properties deleted file mode 100644 index 0c7115d8f5..0000000000 --- a/core/src/main/resources/lib/hudson/node_sk.properties +++ /dev/null @@ -1,3 +0,0 @@ -# This file is under the MIT License by authors - -master=velite\u013E diff --git a/core/src/main/resources/lib/hudson/node_sr.properties b/core/src/main/resources/lib/hudson/node_sr.properties deleted file mode 100644 index e2aa681592..0000000000 --- a/core/src/main/resources/lib/hudson/node_sr.properties +++ /dev/null @@ -1,3 +0,0 @@ -# This file is under the MIT License by authors - -master=\u043C\u0430\u0441\u0442\u0435\u0440 diff --git a/core/src/main/resources/lib/hudson/node_tr.properties b/core/src/main/resources/lib/hudson/node_tr.properties deleted file mode 100644 index 9cdc1f395f..0000000000 --- a/core/src/main/resources/lib/hudson/node_tr.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Oguz Dag -# -# 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. - -master= diff --git a/core/src/main/resources/lib/hudson/node_uk.properties b/core/src/main/resources/lib/hudson/node_uk.properties deleted file mode 100644 index a77f2faefb..0000000000 --- a/core/src/main/resources/lib/hudson/node_uk.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-2010, Sun Microsystems, 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. - -master=\u0433\u043E\u043B\u043E\u0432\u043D\u043E\u043C\u0443 diff --git a/core/src/main/resources/lib/hudson/node_zh_TW.properties b/core/src/main/resources/lib/hudson/node_zh_TW.properties deleted file mode 100644 index 565e2cb800..0000000000 --- a/core/src/main/resources/lib/hudson/node_zh_TW.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2013, Chunghwa Telecom Co., Ltd., Pei-Tang Huang -# -# 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. - -master=master diff --git a/test/src/test/java/hudson/cli/GetNodeCommandTest.java b/test/src/test/java/hudson/cli/GetNodeCommandTest.java index 45adbac47a..20c667f48c 100644 --- a/test/src/test/java/hudson/cli/GetNodeCommandTest.java +++ b/test/src/test/java/hudson/cli/GetNodeCommandTest.java @@ -102,10 +102,18 @@ public class GetNodeCommandTest { assertThat(result.stderr(), containsString("No such node ''")); assertThat(result, failedWith(3)); assertThat(result, hasNoStandardOutput()); + + // old name result = command.authorizedTo(Computer.EXTENDED_READ, Jenkins.READ).invokeWithArgs("(master)"); assertThat(result.stderr(), containsString("No such node '(master)'")); assertThat(result, failedWith(3)); assertThat(result, hasNoStandardOutput()); + + // new name + result = command.authorizedTo(Computer.EXTENDED_READ, Jenkins.READ).invokeWithArgs("(built-in)"); + assertThat(result.stderr(), containsString("No such node '(built-in)'")); + assertThat(result, failedWith(3)); + assertThat(result, hasNoStandardOutput()); } } diff --git a/test/src/test/java/hudson/cli/HelpCommandTest.java b/test/src/test/java/hudson/cli/HelpCommandTest.java index 95ea8baa15..abc6c96db5 100644 --- a/test/src/test/java/hudson/cli/HelpCommandTest.java +++ b/test/src/test/java/hudson/cli/HelpCommandTest.java @@ -102,7 +102,7 @@ public class HelpCommandTest { private void assertContainsUsageOfMethodCommand(String text) { assertThat(text, containsString("offline-node NAME ... [-m VAL]")); - assertThat(text, containsStrings("NAME", "Agent name, or empty string for master")); + assertThat(text, containsStrings("NAME", "Agent name, or empty string for built-in node")); assertThat(text, containsStrings("-m VAL", "Record the reason about why you are disconnecting this node")); } diff --git a/test/src/test/java/hudson/cli/UpdateNodeCommandTest.java b/test/src/test/java/hudson/cli/UpdateNodeCommandTest.java index bfd956a500..f4fcfc400c 100644 --- a/test/src/test/java/hudson/cli/UpdateNodeCommandTest.java +++ b/test/src/test/java/hudson/cli/UpdateNodeCommandTest.java @@ -109,10 +109,18 @@ public class UpdateNodeCommandTest { assertThat(result.stderr(), containsString("No such node ''")); assertThat(result, failedWith(3)); assertThat(result, hasNoStandardOutput()); + + // old name result = command.authorizedTo(Computer.EXTENDED_READ, Jenkins.READ).withStdin(Computer.class.getResourceAsStream("node.xml")).invokeWithArgs("(master)"); assertThat(result.stderr(), containsString("No such node '(master)'")); assertThat(result, failedWith(3)); assertThat(result, hasNoStandardOutput()); + + // new name + result = command.authorizedTo(Computer.EXTENDED_READ, Jenkins.READ).withStdin(Computer.class.getResourceAsStream("node.xml")).invokeWithArgs("(built-in)"); + assertThat(result.stderr(), containsString("No such node '(built-in)'")); + assertThat(result, failedWith(3)); + assertThat(result, hasNoStandardOutput()); } @Test diff --git a/test/src/test/java/hudson/model/AbstractProjectTest.java b/test/src/test/java/hudson/model/AbstractProjectTest.java index e532feae16..a2e65992ec 100644 --- a/test/src/test/java/hudson/model/AbstractProjectTest.java +++ b/test/src/test/java/hudson/model/AbstractProjectTest.java @@ -566,8 +566,8 @@ public class AbstractProjectTest { */ assertThat(responseContent, allOf( containsString("warning"), - // as there is only master that is currently used, it's de facto the nearest to whatever - containsString("master"), + // as there is only the built-in node that is currently used, it's de facto the nearest to whatever + containsString("built-in"), containsString("whatever") )); } diff --git a/test/src/test/java/hudson/model/EnvironmentContributorTest.java b/test/src/test/java/hudson/model/EnvironmentContributorTest.java index c426647dea..40946e4c26 100644 --- a/test/src/test/java/hudson/model/EnvironmentContributorTest.java +++ b/test/src/test/java/hudson/model/EnvironmentContributorTest.java @@ -26,7 +26,7 @@ public class EnvironmentContributorTest { j.assertBuildStatusSuccess(p.scheduleBuild2(0)); assertEquals("Issac Newton", c.getEnvVars().get("ABC")); - assertEquals("master", c.getEnvVars().get("NODE_NAME")); + assertEquals("built-in", c.getEnvVars().get("NODE_NAME")); } @TestExtension("projectScoped") diff --git a/test/src/test/java/hudson/model/HudsonTest.java b/test/src/test/java/hudson/model/HudsonTest.java index 33dfdf6c92..cf88609533 100644 --- a/test/src/test/java/hudson/model/HudsonTest.java +++ b/test/src/test/java/hudson/model/HudsonTest.java @@ -155,35 +155,35 @@ public class HudsonTest { } /** - * Configure link from "/computer/(master)/" should work. + * Configure link from "/computer/(built-in)/" should work. */ @Test @Email("http://www.nabble.com/Master-slave-refactor-td21361880.html") public void computerConfigureLink() throws Exception { - HtmlPage page = j.createWebClient().goTo("computer/(master)/configure"); + HtmlPage page = j.createWebClient().goTo("computer/(built-in)/configure"); j.submit(page.getFormByName("config")); } /** - * Configure link from "/computer/(master)/" should work. + * Configure link from "/computer/(built-in)/" should work. */ @Test @Email("http://www.nabble.com/Master-slave-refactor-td21361880.html") public void deleteHudsonComputer() throws Exception { WebClient wc = j.createWebClient(); - HtmlPage page = wc.goTo("computer/(master)/"); + HtmlPage page = wc.goTo("computer/(built-in)/"); for (HtmlAnchor a : page.getAnchors()) { assertFalse(a.getHrefAttribute(), a.getHrefAttribute().endsWith("delete")); } wc.setThrowExceptionOnFailingStatusCode(false); // try to delete it by hitting the final URL directly - WebRequest req = new WebRequest(new URL(wc.getContextPath()+"computer/(master)/doDelete"), HttpMethod.POST); + WebRequest req = new WebRequest(new URL(wc.getContextPath()+"computer/(built-in)/doDelete"), HttpMethod.POST); page = wc.getPage(wc.addCrumb(req)); assertEquals(HttpURLConnection.HTTP_BAD_REQUEST, page.getWebResponse().getStatusCode()); - // the master computer object should be still here - page = wc.goTo("computer/(master)/"); + // the built-in computer object should be still here + page = wc.goTo("computer/(built-in)/"); assertEquals(HttpURLConnection.HTTP_OK, page.getWebResponse().getStatusCode()); } diff --git a/test/src/test/java/hudson/model/NodeTest.java b/test/src/test/java/hudson/model/NodeTest.java index bfa11836b4..f6af1e766d 100644 --- a/test/src/test/java/hudson/model/NodeTest.java +++ b/test/src/test/java/hudson/model/NodeTest.java @@ -379,8 +379,8 @@ public class NodeTest { @Test public void builtInComputerConfigDotXml() throws Exception { JenkinsRule.WebClient wc = j.createWebClient(); - wc.assertFails("computer/(master)/config.xml", HttpURLConnection.HTTP_BAD_REQUEST); - WebRequest settings = new WebRequest(wc.createCrumbedUrl("computer/(master)/config.xml")); + wc.assertFails("computer/(built-in)/config.xml", HttpURLConnection.HTTP_BAD_REQUEST); + WebRequest settings = new WebRequest(wc.createCrumbedUrl("computer/(built-in)/config.xml")); settings.setHttpMethod(HttpMethod.POST); settings.setRequestBody(""); diff --git a/test/src/test/java/hudson/model/RSSTest.java b/test/src/test/java/hudson/model/RSSTest.java index 2053fd6475..0fc35a1322 100644 --- a/test/src/test/java/hudson/model/RSSTest.java +++ b/test/src/test/java/hudson/model/RSSTest.java @@ -183,8 +183,8 @@ public class RSSTest { public void checkWithSingleBuild_Rss_All_Computer() throws Exception { runSuccessfulBuild(); - String pathPrefix = "computer/(master)/"; - String displayName = "master"; + String pathPrefix = "computer/(built-in)/"; + String displayName = Messages.Hudson_Computer_DisplayName(); String buildType = ALL_BUILD_TYPE; String buildTitle = STABLE_BUILD_TITLE; XmlPage page = getRssAllPage(pathPrefix); @@ -195,8 +195,8 @@ public class RSSTest { public void checkWithSingleBuild_Rss_Failed_Computer() throws Exception { runFailingBuild(); - String pathPrefix = "computer/(master)/"; - String displayName = "master"; + String pathPrefix = "computer/(built-in)/"; + String displayName = Messages.Hudson_Computer_DisplayName(); String buildType = FAILED_BUILD_TYPE; String buildTitle = FAILED_BUILD_TITLE; XmlPage page = getRssFailedPage(pathPrefix); @@ -205,8 +205,8 @@ public class RSSTest { @Test public void latestBuilds_Computer() throws Exception { - String pathPrefix = "computer/(master)/"; - String displayName = "master"; + String pathPrefix = "computer/(built-in)/"; + String displayName = Messages.Hudson_Computer_DisplayName(); String buildType = LATEST_BUILD_TYPE; int expectedLinks = 3; int expectedAllLinks = 6; diff --git a/test/src/test/java/hudson/model/labels/LabelExpressionTest.java b/test/src/test/java/hudson/model/labels/LabelExpressionTest.java index eeb39e4821..c82672a470 100644 --- a/test/src/test/java/hudson/model/labels/LabelExpressionTest.java +++ b/test/src/test/java/hudson/model/labels/LabelExpressionTest.java @@ -262,7 +262,7 @@ public class LabelExpressionTest { assertTrue(msg.contains("foo")); assertTrue(msg.contains("goo")); - msg = LabelExpression.validate("master && goo").renderHtml(); + msg = LabelExpression.validate("built-in && goo").renderHtml(); assertTrue(msg.contains("foo")); assertTrue(msg.contains("goo")); return null; diff --git a/test/src/test/java/hudson/tools/InstallerTranslatorTest.java b/test/src/test/java/hudson/tools/InstallerTranslatorTest.java index 25cbef83f9..d4d5f09c74 100644 --- a/test/src/test/java/hudson/tools/InstallerTranslatorTest.java +++ b/test/src/test/java/hudson/tools/InstallerTranslatorTest.java @@ -128,8 +128,8 @@ public class InstallerTranslatorTest { ? new BatchCommandInstaller("wrong1", "echo hello", "C:\\jdk") : new CommandInstaller("wrong1", "echo hello", "/opt/jdk"); final AbstractCommandInstaller ci2 = Functions.isWindows() - ? new BatchCommandInstaller("master", "echo hello", "C:\\jdk2") - : new CommandInstaller("master", "echo hello", "/opt/jdk2"); + ? new BatchCommandInstaller("built-in", "echo hello", "C:\\jdk2") + : new CommandInstaller("built-in", "echo hello", "/opt/jdk2"); InstallSourceProperty isp = new InstallSourceProperty(Arrays.asList(ci, ci2)); JDK jdk = new JDK("jdk", null, Collections.singletonList(isp)); diff --git a/test/src/test/java/jenkins/model/BuiltInNodeMigrationPropertyTest.java b/test/src/test/java/jenkins/model/BuiltInNodeMigrationPropertyTest.java new file mode 100644 index 0000000000..07dfa44310 --- /dev/null +++ b/test/src/test/java/jenkins/model/BuiltInNodeMigrationPropertyTest.java @@ -0,0 +1,67 @@ +/* + * The MIT License + * + * Copyright (c) 2021 Daniel Beck + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package jenkins.model; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.recipes.LocalData; + +public class BuiltInNodeMigrationPropertyTest { + public static final String KEY = Jenkins.class.getName() + ".nodeNameAndSelfLabelOverride"; + public static final String OVERRIDE_VALUE = "foo"; + @Rule + public JenkinsRule j = new JenkinsRule(); + + @BeforeClass + public static void setProperty() { + System.setProperty(KEY, OVERRIDE_VALUE); + } + + @AfterClass + public static void clearProperty() { + System.clearProperty(KEY); + } + + @Test + public void overrideAppliesToNewInstance() throws Exception { + BuiltInNodeMigrationTest.assertStatus(j, true, false, OVERRIDE_VALUE, OVERRIDE_VALUE); + } + + @Test + @LocalData + public void overrideAppliesToUnmigratedInstance() throws Exception { + BuiltInNodeMigrationTest.assertStatus(j, false, true, OVERRIDE_VALUE, OVERRIDE_VALUE); + j.jenkins.performRenameMigration(); + BuiltInNodeMigrationTest.assertStatus(j, true, false, OVERRIDE_VALUE, OVERRIDE_VALUE); + } + + @Test + @LocalData + public void overrideAppliesToMigratedInstance() throws Exception { + BuiltInNodeMigrationTest.assertStatus(j, true, false, OVERRIDE_VALUE, OVERRIDE_VALUE); + } +} diff --git a/test/src/test/java/jenkins/model/BuiltInNodeMigrationRestartTest.java b/test/src/test/java/jenkins/model/BuiltInNodeMigrationRestartTest.java new file mode 100644 index 0000000000..585141ad8e --- /dev/null +++ b/test/src/test/java/jenkins/model/BuiltInNodeMigrationRestartTest.java @@ -0,0 +1,114 @@ +package jenkins.model; + +import org.apache.commons.io.FileUtils; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.Description; +import org.jvnet.hudson.test.HudsonHomeLoader; +import org.jvnet.hudson.test.JenkinsRecipe; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.JenkinsSessionRule; +import org.jvnet.hudson.test.recipes.LocalData; + +import java.io.File; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.lang.reflect.Method; +import java.util.Objects; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +public class BuiltInNodeMigrationRestartTest { + @Rule + public JenkinsSessionRule r = new JenkinsSessionRule(); + + @Test + public void testNewInstanceWithoutConfiguration() throws Throwable { + r.then(j -> { + Assert.assertTrue(j.jenkins.getRenameMigrationDone()); + Assert.assertFalse(j.jenkins.nodeRenameMigrationNeeded); + Assert.assertFalse(Objects.requireNonNull(j.jenkins.getAdministrativeMonitor(BuiltInNodeMigration.class.getName())).isActivated()); + }); + r.then(j -> { + Assert.assertTrue(j.jenkins.getRenameMigrationDone()); + Assert.assertFalse(j.jenkins.nodeRenameMigrationNeeded); + Assert.assertFalse(Objects.requireNonNull(j.jenkins.getAdministrativeMonitor(BuiltInNodeMigration.class.getName())).isActivated()); + }); + } + + @Test + @LocalDataOnce + public void migratedInstanceStartsWithNewTerminology() throws Throwable { + r.then(j -> { + Assert.assertTrue(j.jenkins.getRenameMigrationDone()); + Assert.assertFalse(j.jenkins.nodeRenameMigrationNeeded); + Assert.assertFalse(Objects.requireNonNull(j.jenkins.getAdministrativeMonitor(BuiltInNodeMigration.class.getName())).isActivated()); + }); + r.then(j -> { + Assert.assertTrue(j.jenkins.getRenameMigrationDone()); + Assert.assertFalse(j.jenkins.nodeRenameMigrationNeeded); + Assert.assertFalse(Objects.requireNonNull(j.jenkins.getAdministrativeMonitor(BuiltInNodeMigration.class.getName())).isActivated()); + }); + } + + @Test + @LocalDataOnce + public void oldDataStartsWithOldTerminology() throws Throwable { + r.then(j -> { + Assert.assertFalse(j.jenkins.getRenameMigrationDone()); + Assert.assertTrue(j.jenkins.nodeRenameMigrationNeeded); + Assert.assertTrue(Objects.requireNonNull(j.jenkins.getAdministrativeMonitor(BuiltInNodeMigration.class.getName())).isActivated()); + }); + r.then(j -> { + Assert.assertFalse(j.jenkins.getRenameMigrationDone()); + Assert.assertTrue(j.jenkins.nodeRenameMigrationNeeded); + Assert.assertTrue(Objects.requireNonNull(j.jenkins.getAdministrativeMonitor(BuiltInNodeMigration.class.getName())).isActivated()); + + j.jenkins.performRenameMigration(); + + Assert.assertTrue(j.jenkins.getRenameMigrationDone()); + Assert.assertFalse(j.jenkins.nodeRenameMigrationNeeded); + Assert.assertFalse(Objects.requireNonNull(j.jenkins.getAdministrativeMonitor(BuiltInNodeMigration.class.getName())).isActivated()); + }); + r.then(j -> { + Assert.assertTrue(j.jenkins.getRenameMigrationDone()); + Assert.assertFalse(j.jenkins.nodeRenameMigrationNeeded); + Assert.assertFalse(Objects.requireNonNull(j.jenkins.getAdministrativeMonitor(BuiltInNodeMigration.class.getName())).isActivated()); + }); + } + + /** + * Like {@link LocalData} except it only provides the JENKINS_HOME content if the directory is currently empty. + */ + @JenkinsRecipe(LocalDataOnce.RuleRunnerImpl.class) + @Target(METHOD) + @Retention(RUNTIME) + private @interface LocalDataOnce { + class RuleRunnerImpl extends JenkinsRecipe.Runner { + public static final Logger LOGGER = Logger.getLogger(RuleRunnerImpl.class.getName()); + private Method method; + + public void setup(JenkinsRule jenkinsRule, LocalDataOnce recipe) throws Exception { + Description desc = jenkinsRule.getTestDescription(); + method = desc.getTestClass().getMethod((desc.getMethodName())); + } + + @Override + public void decorateHome(JenkinsRule jenkinsRule, File home) throws Exception { + // Only copy the home directory if it doesn't have content yet + final File[] files = home.listFiles(); + if (!home.exists() || !home.isDirectory() || files == null || files.length == 0) { + LOGGER.log(Level.INFO, () -> "Copying JENKINS_HOME from test resources to " + home); + File source = new HudsonHomeLoader.Local(method, "").allocate(); + FileUtils.copyDirectory(source, home); + } else { + LOGGER.log(Level.INFO, () -> "JENKINS_HOME " + home + " is not empty, skipping copy from test resources"); + } + } + } + } +} diff --git a/test/src/test/java/jenkins/model/BuiltInNodeMigrationTest.java b/test/src/test/java/jenkins/model/BuiltInNodeMigrationTest.java new file mode 100644 index 0000000000..080a717bfc --- /dev/null +++ b/test/src/test/java/jenkins/model/BuiltInNodeMigrationTest.java @@ -0,0 +1,79 @@ +/* + * The MIT License + * + * Copyright (c) 2021 Daniel Beck + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package jenkins.model; + +import hudson.ExtensionList; +import hudson.model.FreeStyleProject; +import hudson.model.labels.LabelAtom; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.CaptureEnvironmentBuilder; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.recipes.LocalData; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class BuiltInNodeMigrationTest { + @Rule + public JenkinsRule j = new JenkinsRule(); + + @Test + public void newInstanceHasNewTerminology() throws Exception { + assertStatus(j, true, false, "built-in", "built-in"); + assertFalse(ExtensionList.lookupSingleton(BuiltInNodeMigration.class).isActivated()); + } + + @Test + @LocalData + public void oldDataStartsWithOldTerminology() throws Exception { + assertStatus(j, false, true, "master", "master"); + final BuiltInNodeMigration builtInNodeMigration = ExtensionList.lookupSingleton(BuiltInNodeMigration.class); + assertTrue(builtInNodeMigration.isActivated()); + + // Now perform rename and confirm it's done + j.jenkins.performRenameMigration(); + assertStatus(j, true, false, "built-in", "built-in"); + assertFalse(builtInNodeMigration.isActivated()); + } + + @Test + @LocalData + public void migratedInstanceStartsWithNewTerminology() throws Exception { + assertStatus(j, true, false, "built-in", "built-in"); + assertFalse(ExtensionList.lookupSingleton(BuiltInNodeMigration.class).isActivated()); + } + + public static void assertStatus(JenkinsRule j, boolean migrationDoneGetterValue, Boolean migrationNeededFieldValue, String label, String nodeName) throws Exception { + assertEquals(migrationDoneGetterValue, j.jenkins.getRenameMigrationDone()); + assertEquals(migrationNeededFieldValue, j.jenkins.nodeRenameMigrationNeeded); + assertEquals(new LabelAtom(label), j.jenkins.getSelfLabel()); + final FreeStyleProject freeStyleProject = j.createFreeStyleProject(); + final CaptureEnvironmentBuilder environmentBuilder = new CaptureEnvironmentBuilder(); + freeStyleProject.getBuildersList().add(environmentBuilder); + j.buildAndAssertSuccess(freeStyleProject); + assertEquals(nodeName, environmentBuilder.getEnvVars().get("NODE_NAME")); + } +} diff --git a/test/src/test/java/jenkins/model/JenkinsManagePermissionTest.java b/test/src/test/java/jenkins/model/JenkinsManagePermissionTest.java index cf6b175ef9..890bb37391 100644 --- a/test/src/test/java/jenkins/model/JenkinsManagePermissionTest.java +++ b/test/src/test/java/jenkins/model/JenkinsManagePermissionTest.java @@ -113,8 +113,8 @@ public class JenkinsManagePermissionTest { .grant(Jenkins.MANAGE).everywhere().to(MANAGER) .grant(Jenkins.READ).everywhere().to(MANAGER) ); - j.createWebClient().login(READER).assertFails("computer/(master)/dumpExportTable", HttpURLConnection.HTTP_FORBIDDEN); - j.createWebClient().login(MANAGER).assertFails("computer/(master)/dumpExportTable", HttpURLConnection.HTTP_FORBIDDEN); + j.createWebClient().login(READER).assertFails("computer/(built-in)/dumpExportTable", HttpURLConnection.HTTP_FORBIDDEN); + j.createWebClient().login(MANAGER).assertFails("computer/(built-in)/dumpExportTable", HttpURLConnection.HTTP_FORBIDDEN); } // End of ComputerTest diff --git a/test/src/test/java/jenkins/widgets/BuildTimeTrendTest.java b/test/src/test/java/jenkins/widgets/BuildTimeTrendTest.java index 5b48cac25b..461344003c 100644 --- a/test/src/test/java/jenkins/widgets/BuildTimeTrendTest.java +++ b/test/src/test/java/jenkins/widgets/BuildTimeTrendTest.java @@ -117,10 +117,10 @@ public class BuildTimeTrendTest { // for the build on agent assertTrue(anchor.isPresent()); - String masterName = hudson.model.Messages.Hudson_Computer_DisplayName(); + String builtInNode = hudson.model.Messages.Hudson_Computer_DisplayName(); DomNodeList tds = page.getDocumentElement().querySelectorAll("table[data-is-distributed-build-enabled=true] td"); Optional td = tds.stream() - .filter(t -> t.getTextContent().equals(masterName)) + .filter(t -> t.getTextContent().equals(builtInNode)) .findFirst(); // for the build on built-in node assertTrue(td.isPresent()); diff --git a/test/src/test/java/lib/form/PasswordTest.java b/test/src/test/java/lib/form/PasswordTest.java index 2dcad3763c..885baea78c 100644 --- a/test/src/test/java/lib/form/PasswordTest.java +++ b/test/src/test/java/lib/form/PasswordTest.java @@ -715,7 +715,7 @@ public class PasswordTest { { wc.login(READONLY); - HtmlPage page = wc.goTo("computer/(master)/secured/"); + HtmlPage page = wc.goTo("computer/(built-in)/secured/"); String value = ((HtmlInput)page.getElementById("password")).getValueAttribute(); assertThat(value, is("********")); @@ -723,7 +723,7 @@ public class PasswordTest { { wc.login(ADMIN); - HtmlPage page = wc.goTo("computer/(master)/secured/"); + HtmlPage page = wc.goTo("computer/(built-in)/secured/"); String value = ((HtmlInput)page.getElementById("password")).getValueAttribute(); assertThat(Secret.fromString(value).getPlainText(), is("abcdefgh")); diff --git a/test/src/test/resources/jenkins/model/BuiltInNodeMigrationPropertyTest/overrideAppliesToMigratedInstance/config.xml b/test/src/test/resources/jenkins/model/BuiltInNodeMigrationPropertyTest/overrideAppliesToMigratedInstance/config.xml new file mode 100644 index 0000000000..5625041604 --- /dev/null +++ b/test/src/test/resources/jenkins/model/BuiltInNodeMigrationPropertyTest/overrideAppliesToMigratedInstance/config.xml @@ -0,0 +1,37 @@ + + + + 2 + NORMAL + true + + + false + + ${JENKINS_HOME}/workspace/${ITEM_FULL_NAME} + ${ITEM_ROOTDIR}/builds + + + + + 5 + 0 + + + + all + false + false + + + + all + 0 + + + false + + + + false + \ No newline at end of file diff --git a/test/src/test/resources/jenkins/model/BuiltInNodeMigrationPropertyTest/overrideAppliesToUnmigratedInstance/config.xml b/test/src/test/resources/jenkins/model/BuiltInNodeMigrationPropertyTest/overrideAppliesToUnmigratedInstance/config.xml new file mode 100644 index 0000000000..f1626a8892 --- /dev/null +++ b/test/src/test/resources/jenkins/model/BuiltInNodeMigrationPropertyTest/overrideAppliesToUnmigratedInstance/config.xml @@ -0,0 +1,36 @@ + + + + 2 + NORMAL + true + + + false + + ${JENKINS_HOME}/workspace/${ITEM_FULL_NAME} + ${ITEM_ROOTDIR}/builds + + + + + 5 + 0 + + + + all + false + false + + + + all + 0 + + + false + + + + \ No newline at end of file diff --git a/test/src/test/resources/jenkins/model/BuiltInNodeMigrationRestartTest/migratedInstanceStartsWithNewTerminology/config.xml b/test/src/test/resources/jenkins/model/BuiltInNodeMigrationRestartTest/migratedInstanceStartsWithNewTerminology/config.xml new file mode 100644 index 0000000000..5625041604 --- /dev/null +++ b/test/src/test/resources/jenkins/model/BuiltInNodeMigrationRestartTest/migratedInstanceStartsWithNewTerminology/config.xml @@ -0,0 +1,37 @@ + + + + 2 + NORMAL + true + + + false + + ${JENKINS_HOME}/workspace/${ITEM_FULL_NAME} + ${ITEM_ROOTDIR}/builds + + + + + 5 + 0 + + + + all + false + false + + + + all + 0 + + + false + + + + false + \ No newline at end of file diff --git a/test/src/test/resources/jenkins/model/BuiltInNodeMigrationRestartTest/oldDataStartsWithOldTerminology/config.xml b/test/src/test/resources/jenkins/model/BuiltInNodeMigrationRestartTest/oldDataStartsWithOldTerminology/config.xml new file mode 100644 index 0000000000..f1626a8892 --- /dev/null +++ b/test/src/test/resources/jenkins/model/BuiltInNodeMigrationRestartTest/oldDataStartsWithOldTerminology/config.xml @@ -0,0 +1,36 @@ + + + + 2 + NORMAL + true + + + false + + ${JENKINS_HOME}/workspace/${ITEM_FULL_NAME} + ${ITEM_ROOTDIR}/builds + + + + + 5 + 0 + + + + all + false + false + + + + all + 0 + + + false + + + + \ No newline at end of file diff --git a/test/src/test/resources/jenkins/model/BuiltInNodeMigrationTest/migratedInstanceStartsWithNewTerminology/config.xml b/test/src/test/resources/jenkins/model/BuiltInNodeMigrationTest/migratedInstanceStartsWithNewTerminology/config.xml new file mode 100644 index 0000000000..5625041604 --- /dev/null +++ b/test/src/test/resources/jenkins/model/BuiltInNodeMigrationTest/migratedInstanceStartsWithNewTerminology/config.xml @@ -0,0 +1,37 @@ + + + + 2 + NORMAL + true + + + false + + ${JENKINS_HOME}/workspace/${ITEM_FULL_NAME} + ${ITEM_ROOTDIR}/builds + + + + + 5 + 0 + + + + all + false + false + + + + all + 0 + + + false + + + + false + \ No newline at end of file diff --git a/test/src/test/resources/jenkins/model/BuiltInNodeMigrationTest/oldDataStartsWithOldTerminology/config.xml b/test/src/test/resources/jenkins/model/BuiltInNodeMigrationTest/oldDataStartsWithOldTerminology/config.xml new file mode 100644 index 0000000000..f1626a8892 --- /dev/null +++ b/test/src/test/resources/jenkins/model/BuiltInNodeMigrationTest/oldDataStartsWithOldTerminology/config.xml @@ -0,0 +1,36 @@ + + + + 2 + NORMAL + true + + + false + + ${JENKINS_HOME}/workspace/${ITEM_FULL_NAME} + ${ITEM_ROOTDIR}/builds + + + + + 5 + 0 + + + + all + false + false + + + + all + 0 + + + false + + + + \ No newline at end of file diff --git a/war/src/main/webapp/help/project-config/custom-workspace.html b/war/src/main/webapp/help/project-config/custom-workspace.html index 3211e0ddfc..e6985c1145 100644 --- a/war/src/main/webapp/help/project-config/custom-workspace.html +++ b/war/src/main/webapp/help/project-config/custom-workspace.html @@ -22,5 +22,5 @@

If this path is relative, it's resolved against the "remote FS root" directory of the agent, or $JENKINS_HOME - on the master. + on the controller. \ No newline at end of file From 8e768057d772eb1107fc3e85c5164f01d88604dd Mon Sep 17 00:00:00 2001 From: Tim Jacomb <21194782+timja@users.noreply.github.com> Date: Sun, 15 Aug 2021 20:32:58 +0100 Subject: [PATCH 70/98] Update TODO in buildEnv.properties Ref https://github.com/jenkinsci/jenkins/pull/5425#issuecomment-898410550 --- .../model/CoreEnvironmentContributor/buildEnv.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/jenkins/model/CoreEnvironmentContributor/buildEnv.properties b/core/src/main/resources/jenkins/model/CoreEnvironmentContributor/buildEnv.properties index 9e4ecf591b..d967351652 100644 --- a/core/src/main/resources/jenkins/model/CoreEnvironmentContributor/buildEnv.properties +++ b/core/src/main/resources/jenkins/model/CoreEnvironmentContributor/buildEnv.properties @@ -10,7 +10,7 @@ EXECUTOR_NUMBER.blurb=\ (among executors of the same machine) that\u2019s \ carrying out this build. This is the number you see in \ the "build executor status", except that the number starts from 0, not 1. -NODE_NAME.blurb=Name of the agent if the build is on an agent, or "built-in" if run on the built-in node (or "master" until Jenkins 2.TODO). +NODE_NAME.blurb=Name of the agent if the build is on an agent, or "built-in" if run on the built-in node (or "master" until Jenkins 2.307). NODE_LABELS.blurb=Whitespace-separated list of labels that the node is assigned. WORKSPACE.blurb=The absolute path of the directory assigned to the build as a workspace. WORKSPACE_TMP.blurb=\ From 95d9b55e6f5a3cf510dc6da809c57ac8c30782cd Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Sun, 15 Aug 2021 12:34:01 -0700 Subject: [PATCH 71/98] Align resource loading logic in `AntClassLoader` with upstream (#5656) --- .../java/hudson/ClassicPluginStrategy.java | 3 +- .../java/hudson/PluginFirstClassLoader.java | 4 +- .../jenkins/ClassLoaderReflectionToolkit.java | 17 ++-- .../java/jenkins/util/AntClassLoader.java | 78 +++++++++++++------ .../util/AntWithFindResourceClassLoader.java | 56 ------------- 5 files changed, 65 insertions(+), 93 deletions(-) delete mode 100644 core/src/main/java/jenkins/util/AntWithFindResourceClassLoader.java diff --git a/core/src/main/java/hudson/ClassicPluginStrategy.java b/core/src/main/java/hudson/ClassicPluginStrategy.java index fd15af1a76..432b028abd 100644 --- a/core/src/main/java/hudson/ClassicPluginStrategy.java +++ b/core/src/main/java/hudson/ClassicPluginStrategy.java @@ -36,7 +36,6 @@ import jenkins.ClassLoaderReflectionToolkit; import jenkins.ExtensionFilter; import jenkins.plugins.DetachedPluginsUtil; import jenkins.util.AntClassLoader; -import jenkins.util.AntWithFindResourceClassLoader; import jenkins.util.SystemProperties; import org.apache.commons.io.output.NullOutputStream; import org.apache.tools.ant.BuildException; @@ -302,7 +301,7 @@ public class ClassicPluginStrategy implements PluginStrategy { } } - AntWithFindResourceClassLoader classLoader = new AntWithFindResourceClassLoader(parent, true); + AntClassLoader classLoader = new AntClassLoader(parent, true); classLoader.addPathFiles(paths); return classLoader; } diff --git a/core/src/main/java/hudson/PluginFirstClassLoader.java b/core/src/main/java/hudson/PluginFirstClassLoader.java index 8fc8323160..ec7e0732dc 100644 --- a/core/src/main/java/hudson/PluginFirstClassLoader.java +++ b/core/src/main/java/hudson/PluginFirstClassLoader.java @@ -32,7 +32,7 @@ import java.util.Collection; import java.util.Enumeration; import java.util.List; -import jenkins.util.AntWithFindResourceClassLoader; +import jenkins.util.AntClassLoader; /** * classLoader which use first /WEB-INF/lib/*.jar and /WEB-INF/classes before core classLoader @@ -41,7 +41,7 @@ import jenkins.util.AntWithFindResourceClassLoader; * @since 1.371 */ public class PluginFirstClassLoader - extends AntWithFindResourceClassLoader + extends AntClassLoader { public PluginFirstClassLoader() { diff --git a/core/src/main/java/jenkins/ClassLoaderReflectionToolkit.java b/core/src/main/java/jenkins/ClassLoaderReflectionToolkit.java index 5dc7859f13..141ed7b28a 100644 --- a/core/src/main/java/jenkins/ClassLoaderReflectionToolkit.java +++ b/core/src/main/java/jenkins/ClassLoaderReflectionToolkit.java @@ -9,7 +9,6 @@ import java.util.Enumeration; import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; import jenkins.util.AntClassLoader; -import jenkins.util.AntWithFindResourceClassLoader; /** * Reflective access to various {@link ClassLoader} methods which are otherwise {@code protected}. @@ -47,8 +46,8 @@ public class ClassLoaderReflectionToolkit { } private static Object getClassLoadingLock(ClassLoader cl, String name) { - if (cl instanceof AntWithFindResourceClassLoader) { - return ((AntWithFindResourceClassLoader) cl).getClassLoadingLock(name); + if (cl instanceof AntClassLoader) { + return ((AntClassLoader) cl).getClassLoadingLock(name); } return invoke(GetClassLoadingLock.GET_CLASS_LOADING_LOCK, RuntimeException.class, cl, name); } @@ -75,8 +74,8 @@ public class ClassLoaderReflectionToolkit { public static @CheckForNull Class _findLoadedClass(ClassLoader cl, String name) { synchronized (getClassLoadingLock(cl, name)) { Class c; - if (cl instanceof AntWithFindResourceClassLoader) { - c = ((AntWithFindResourceClassLoader) cl).findLoadedClass2(name); + if (cl instanceof AntClassLoader) { + c = ((AntClassLoader) cl).findLoadedClass2(name); } else { c = (Class) invoke(FindLoadedClass.FIND_LOADED_CLASS, RuntimeException.class, cl, name); } @@ -132,8 +131,8 @@ public class ClassLoaderReflectionToolkit { */ public static @CheckForNull URL _findResource(ClassLoader cl, String name) { URL url; - if (cl instanceof AntWithFindResourceClassLoader) { - url = ((AntWithFindResourceClassLoader) cl).findResource(name); + if (cl instanceof AntClassLoader) { + url = ((AntClassLoader) cl).findResource(name); } else if (cl instanceof URLClassLoader) { url = ((URLClassLoader) cl).findResource(name); } else { @@ -162,8 +161,8 @@ public class ClassLoaderReflectionToolkit { */ public static @NonNull Enumeration _findResources(ClassLoader cl, String name) throws IOException { Enumeration urls; - if (cl instanceof AntWithFindResourceClassLoader) { - urls = ((AntWithFindResourceClassLoader) cl).findResources(name); + if (cl instanceof AntClassLoader) { + urls = ((AntClassLoader) cl).findResources(name); } else { urls = (Enumeration) invoke(FindResources.FIND_RESOURCES, IOException.class, cl, name); } diff --git a/core/src/main/java/jenkins/util/AntClassLoader.java b/core/src/main/java/jenkins/util/AntClassLoader.java index 8665e99b51..f3cac15914 100644 --- a/core/src/main/java/jenkins/util/AntClassLoader.java +++ b/core/src/main/java/jenkins/util/AntClassLoader.java @@ -35,7 +35,6 @@ import org.apache.tools.zip.ZipLong; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; -import edu.umd.cs.findbugs.annotations.CheckForNull; import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.File; @@ -48,6 +47,7 @@ import java.nio.file.Files; import java.security.CodeSource; import java.security.ProtectionDomain; import java.security.cert.Certificate; +import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; @@ -284,7 +284,6 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener, Clo * @param classpath The classpath to use to load classes. */ public AntClassLoader(final ClassLoader parent, final Project project, final Path classpath) { - super(parent); // KK patch for JENKINS-21579 setParent(parent); setClassPath(classpath); setProject(project); @@ -371,7 +370,6 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener, Clo * load the a class through this loader. */ public AntClassLoader(final ClassLoader parent, final boolean parentFirst) { - super(parent); // KK patch for JENKINS-21579 setParent(parent); project = null; this.parentFirst = parentFirst; @@ -514,6 +512,12 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener, Clo pathComponents.addElement(file); } + public void addPathFiles(Collection paths) throws IOException { + for (File f : paths) { + addPathFile(f); + } + } + /** * Add a file to the path. * Reads the manifest, if available, and adds any additional class path jars @@ -902,9 +906,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener, Clo if (url != null) { log("Resource " + name + " loaded from parent loader", Project.MSG_DEBUG); } else { - // try and load from this loader if the parent either didn't find - // it or wasn't consulted. - url = getUrl(pathComponents, name); + url = getUrl(name); } if (url == null && !isParentFirst(name)) { // this loader was first but it didn't find it - try the parent @@ -924,15 +926,17 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener, Clo } /** - * Finds a matching file by iterating pathComponents. + * Finds a matching file by iterating through path components. * - * @param pathComponents Path to a folder, split into the individual folder names * @param name File to find - * @return Url to found object + * @return A {@code URL} object for reading the resource, or {@code null} if the + * resource could not be found */ - @CheckForNull - protected URL getUrl(Iterable pathComponents, String name) { + private URL getUrl(String name) { URL url = null; + + // try and load from this loader if the parent either didn't find + // it or wasn't consulted. for (final File pathComponent : pathComponents) { url = getResourceURL(pathComponent, name); if (url != null) { @@ -940,6 +944,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener, Clo break; } } + return url; } @@ -948,9 +953,6 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener, Clo * data (images, audio, text, etc) that can be accessed by class * code in a way that is independent of the location of the code. * - *

Would override getResources if that wasn't final in Java - * 1.4.

- * * @param name name of the resource * @return possible URLs as enumeration * @throws IOException if something goes wrong @@ -962,6 +964,18 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener, Clo return findResources(name, false); } + /** + * Finds the resource with the given name. + * + * @param name The resource name + * @return A {@code URL} object for reading the resource, or {@code null} if the + * resource could not be found + */ + @Override + public URL findResource(final String name) { + return getUrl(name); + } + /** * Returns an enumeration of URLs representing all the resources with the * given name by searching the class loader's classpath. @@ -982,26 +996,21 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener, Clo * * @param name The resource name to search for. * Must not be {@code null}. - * @param parentHasBeenSearched whether ClassLoader.this.parent - * has been searched - will be true if the method is (indirectly) - * called from ClassLoader.getResources + * @param skipParent whether to skip searching the parent first - will be false if the method is + * invoked from {@link #getResources(String)} or {@link #getNamedResources(String)} and true + * if the method is invoked from {@link #findResources(String)}. * @return an enumeration of URLs for the resources * @exception IOException if I/O errors occurs (can't happen) */ protected Enumeration findResources(final String name, - final boolean parentHasBeenSearched) + final boolean skipParent) throws IOException { final Enumeration mine = new ResourceEnumeration(name); Enumeration base; - if (parent != null && (!parentHasBeenSearched || parent != getParent())) { + if (parent != null && !skipParent) { // Delegate to the parent: base = parent.getResources(name); - // Note: could cause overlaps in case - // ClassLoader.this.parent has matches and - // parentHasBeenSearched is true } else { - // ClassLoader.this.parent is already delegated to for example from - // ClassLoader.getResources, no need: base = Collections.emptyEnumeration(); } if (isParentFirst(name)) { @@ -1580,6 +1589,27 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener, Clo return "AntClassLoader[" + getClasspath() + "]"; } + /** + * Public version of {@link ClassLoader#findLoadedClass(String)} + */ + public Class findLoadedClass2(String name) { + return findLoadedClass(name); + } + + /** + * Public version of {@link ClassLoader#getClassLoadingLock(String)} + */ + @Override + public Object getClassLoadingLock(String className) { + return super.getClassLoadingLock(className); + } + + /** {@inheritDoc} */ + @Override + public Enumeration getResources(String name) throws IOException { + return getNamedResources(name); + } + /** {@inheritDoc} */ @Override public void close() { diff --git a/core/src/main/java/jenkins/util/AntWithFindResourceClassLoader.java b/core/src/main/java/jenkins/util/AntWithFindResourceClassLoader.java deleted file mode 100644 index 5cc31c4856..0000000000 --- a/core/src/main/java/jenkins/util/AntWithFindResourceClassLoader.java +++ /dev/null @@ -1,56 +0,0 @@ -package jenkins.util; - -import java.io.File; -import java.io.IOException; -import java.lang.reflect.Field; -import java.net.URL; -import java.util.Collection; -import java.util.Vector; - -/** - * As of 1.8.0, {@link org.apache.tools.ant.AntClassLoader} doesn't implement {@link #findResource(String)} - * in any meaningful way, which breaks fast lookup. Implement it properly. - */ -public class AntWithFindResourceClassLoader extends AntClassLoader { - private final Vector pathComponents; - - public AntWithFindResourceClassLoader(ClassLoader parent, boolean parentFirst) { - super(parent, parentFirst); - - try { - Field $pathComponents = AntClassLoader.class.getDeclaredField("pathComponents"); - $pathComponents.setAccessible(true); - pathComponents = (Vector)$pathComponents.get(this); - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new LinkageError(e.getMessage(), e); - } - } - - public void addPathFiles(Collection paths) throws IOException { - for (File f : paths) - addPathFile(f); - } - - @Override - public URL findResource(String name) { - // try and load from this loader if the parent either didn't find - // it or wasn't consulted. - return getUrl(pathComponents, name); - } - - /** - * Public version of {@link ClassLoader#findLoadedClass(String)} - */ - public Class findLoadedClass2(String name) { - return findLoadedClass(name); - } - - /** - * Public version of {@link ClassLoader#getClassLoadingLock(String)} - */ - @Override - public Object getClassLoadingLock(String className) { - return super.getClassLoadingLock(className); - } - -} From fd133da734ab01528709dbe8f879e36f2e204132 Mon Sep 17 00:00:00 2001 From: Wadeck Follonier Date: Sun, 15 Aug 2021 21:35:25 +0200 Subject: [PATCH 72/98] Add file handle leak detection (#5612) Co-Authored-By: Jeff Thompson Co-authored-by: Jeff Thompson --- .../model/DirectoryBrowserSupportTest.java | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/test/src/test/java/hudson/model/DirectoryBrowserSupportTest.java b/test/src/test/java/hudson/model/DirectoryBrowserSupportTest.java index 1a4332a836..628f2d069a 100644 --- a/test/src/test/java/hudson/model/DirectoryBrowserSupportTest.java +++ b/test/src/test/java/hudson/model/DirectoryBrowserSupportTest.java @@ -27,6 +27,7 @@ import com.gargoylesoftware.htmlunit.Page; import com.gargoylesoftware.htmlunit.UnexpectedPage; import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.gargoylesoftware.htmlunit.util.NameValuePair; +import com.sun.management.UnixOperatingSystemMXBean; import edu.umd.cs.findbugs.annotations.NonNull; import hudson.ExtensionList; import hudson.FilePath; @@ -62,6 +63,8 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.lang.management.ManagementFactory; +import java.lang.management.OperatingSystemMXBean; import java.net.HttpURLConnection; import java.net.URI; import java.net.URISyntaxException; @@ -73,6 +76,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.TreeMap; import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -210,6 +214,85 @@ public class DirectoryBrowserSupportTest { zipfile.delete(); } + @Test + public void zipDownloadFileLeakMx_hypothesis() throws Exception { + // this test is meant to just ensure zipDownloadFileLeakMx hypothesis about the UI work fine + + String content = "Hello world!"; + FreeStyleProject p = j.createFreeStyleProject(); + p.setScm(new SingleFileSCM("artifact.out", content)); + p.getPublishersList().add(new ArtifactArchiver("*", "", true)); + assertEquals(Result.SUCCESS, p.scheduleBuild2(0).get().getResult()); + + HtmlPage page = j.createWebClient().goTo("job/" + p.getName() + "/lastSuccessfulBuild/artifact/"); + Page downloadPage = page.getAnchorByHref("artifact.out").click(); + assertEquals(content, downloadPage.getWebResponse().getContentAsString()); + } + + @Test + @Issue({"JENKINS-64632", "JENKINS-61121"}) + public void zipDownloadFileLeakMx() throws Exception { + Assume.assumeFalse(Functions.isWindows()); + + int numOfClicks = 10; + int totalRuns = 10; + boolean freeFromLeak = false; + long[][] openFds = new long[totalRuns][2]; + for (int runs = 0; runs < totalRuns && !freeFromLeak; runs++) { + long initialOpenFds = getOpenFdCount(); + FreeStyleProject p = j.createFreeStyleProject(); + + // add randomness just to prevent any potential caching issue + p.setScm(new SingleFileSCM("artifact.out", "Hello world! " + Math.random())); + p.getPublishersList().add(new ArtifactArchiver("*", "", true)); + assertEquals(Result.SUCCESS, p.scheduleBuild2(0).get().getResult()); + + HtmlPage page = j.createWebClient().goTo("job/" + p.getName() + "/lastSuccessfulBuild/artifact/"); + for (int clicks = 0; clicks < numOfClicks; clicks++) { + page.getAnchorByHref("artifact.out").click(); + } + long finalOpenFds = getOpenFdCount(); + + if (finalOpenFds < initialOpenFds + numOfClicks) { + // when there was a file leak, the number of open file handle was always + // greater or equal to the number of download + // in reverse, since the correction, the likelihood to overpass the limit was less than 1% + freeFromLeak = true; + } + + openFds[runs][0] = initialOpenFds; + openFds[runs][1] = finalOpenFds; + } + + List messages = new ArrayList<>(); + Map differences = new TreeMap<>(); + for (int runs = 0; runs < totalRuns; runs++) { + long difference = openFds[runs][1] - openFds[runs][0]; + Long storedDifference = differences.get(difference); + if (storedDifference == null) { + differences.put(difference, 1L); + } else { + differences.put(difference, ++storedDifference); + } + messages.add("Initial=" + openFds[runs][0] + ", Final=" + openFds[runs][1] + ", difference=" + difference); + } + for (Long difference : differences.keySet()) { + messages.add("Difference=" + difference + " occurs " + differences.get(difference) + " times"); + } + + String summary = String.join("\n", messages); + System.out.println("Summary of the test: \n"+ summary); + assertTrue("There should be no difference greater than "+numOfClicks+", but the output was: \n"+ summary, freeFromLeak); + } + + private long getOpenFdCount() { + OperatingSystemMXBean os = ManagementFactory.getOperatingSystemMXBean(); + if(os instanceof UnixOperatingSystemMXBean){ + return ((UnixOperatingSystemMXBean) os).getOpenFileDescriptorCount(); + } + return -1; + } + @Issue("SECURITY-95") @Test public void contentSecurityPolicy() throws Exception { From a621460bccea4942ad328098e4c35d7adbf6dae7 Mon Sep 17 00:00:00 2001 From: Tim Jacomb <21194782+timja@users.noreply.github.com> Date: Mon, 16 Aug 2021 08:31:17 +0100 Subject: [PATCH 73/98] Correct version https://github.com/jenkinsci/jenkins/pull/5425#issuecomment-899112566 --- .../model/CoreEnvironmentContributor/buildEnv.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/jenkins/model/CoreEnvironmentContributor/buildEnv.properties b/core/src/main/resources/jenkins/model/CoreEnvironmentContributor/buildEnv.properties index d967351652..8373df3368 100644 --- a/core/src/main/resources/jenkins/model/CoreEnvironmentContributor/buildEnv.properties +++ b/core/src/main/resources/jenkins/model/CoreEnvironmentContributor/buildEnv.properties @@ -10,7 +10,7 @@ EXECUTOR_NUMBER.blurb=\ (among executors of the same machine) that\u2019s \ carrying out this build. This is the number you see in \ the "build executor status", except that the number starts from 0, not 1. -NODE_NAME.blurb=Name of the agent if the build is on an agent, or "built-in" if run on the built-in node (or "master" until Jenkins 2.307). +NODE_NAME.blurb=Name of the agent if the build is on an agent, or "built-in" if run on the built-in node (or "master" until Jenkins 2.306). NODE_LABELS.blurb=Whitespace-separated list of labels that the node is assigned. WORKSPACE.blurb=The absolute path of the directory assigned to the build as a workspace. WORKSPACE_TMP.blurb=\ From 62b747a9a3f7fdb6e1fa765b0ceef352466caadb Mon Sep 17 00:00:00 2001 From: Konstantin Nisht <36202647+knisht@users.noreply.github.com> Date: Mon, 16 Aug 2021 10:40:09 +0300 Subject: [PATCH 74/98] Do not generate properties for Groovy views in Stapler if there is no admissible root object (#5662) --- core/src/main/resources/dsld/IntelliJ.gdsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/dsld/IntelliJ.gdsl b/core/src/main/resources/dsld/IntelliJ.gdsl index 6babd92f23..0be510b16f 100644 --- a/core/src/main/resources/dsld/IntelliJ.gdsl +++ b/core/src/main/resources/dsld/IntelliJ.gdsl @@ -25,7 +25,7 @@ contributor(ctx) { for (def f=ct.containingFile.virtualFile; f!=null && !roots.contains(f); f=f.parent) { contentPaths << f.name } - if (contentPaths.size()>0) { + if (contentPaths.size()>1) { // directory portion matches the FQCN def fqcn = contentPaths.reverse()[0.. Date: Tue, 17 Aug 2021 08:59:33 +0200 Subject: [PATCH 75/98] Mark Resource Domain Recommendation monitor as security --- .../java/jenkins/security/ResourceDomainRecommendation.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/src/main/java/jenkins/security/ResourceDomainRecommendation.java b/core/src/main/java/jenkins/security/ResourceDomainRecommendation.java index 30b2c2467d..1826a77c4b 100644 --- a/core/src/main/java/jenkins/security/ResourceDomainRecommendation.java +++ b/core/src/main/java/jenkins/security/ResourceDomainRecommendation.java @@ -80,4 +80,9 @@ public class ResourceDomainRecommendation extends AdministrativeMonitor { public Permission getRequiredPermission() { return Jenkins.SYSTEM_READ; } + + @Override + public boolean isSecurity() { + return true; + } } From f749dd85742f3fa2c72b6fb85433d5cd761dda8d Mon Sep 17 00:00:00 2001 From: Jenkins Release Bot <66998184+jenkins-release-bot@users.noreply.github.com> Date: Tue, 17 Aug 2021 12:27:36 +0000 Subject: [PATCH 76/98] [maven-release-plugin] prepare release jenkins-2.307 --- 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 e9ab4106a6..52bd70dab2 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.307 jenkins-bom diff --git a/cli/pom.xml b/cli/pom.xml index 746123378f..da3a824c14 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.307 cli diff --git a/core/pom.xml b/core/pom.xml index a617d48cd9..a1afaa78b0 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.307 jenkins-core diff --git a/pom.xml b/pom.xml index 2a11bb62fd..1edd5f402e 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.307 pom Jenkins main module @@ -61,7 +61,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.307 diff --git a/test/pom.xml b/test/pom.xml index a0b2928aa3..e90512eb32 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.307 jenkins-test diff --git a/war/pom.xml b/war/pom.xml index 3d84efb9f2..c6cad8a4b3 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.307 jenkins-war From 50e4cd9ce29fc107beffdfbc41af89abcb5cd427 Mon Sep 17 00:00:00 2001 From: Jenkins Release Bot <66998184+jenkins-release-bot@users.noreply.github.com> Date: Tue, 17 Aug 2021 12:27:55 +0000 Subject: [PATCH 77/98] [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 52bd70dab2..e9ab4106a6 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.307 + ${revision}${changelist} jenkins-bom diff --git a/cli/pom.xml b/cli/pom.xml index da3a824c14..746123378f 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main jenkins-parent - 2.307 + ${revision}${changelist} cli diff --git a/core/pom.xml b/core/pom.xml index a1afaa78b0..a617d48cd9 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.307 + ${revision}${changelist} jenkins-core diff --git a/pom.xml b/pom.xml index 1edd5f402e..e68d080729 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.307 + ${revision}${changelist} pom Jenkins main module @@ -61,7 +61,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.307 + ${scmTag} @@ -70,7 +70,7 @@ THE SOFTWARE. - 2.307 + 2.308 -SNAPSHOT + org.jenkins-ci.plugins + display-url-api + 2.3.5 + org.jenkins-ci.plugins @@ -129,7 +135,7 @@ THE SOFTWARE. org.jenkins-ci.plugins junit - 1.51 + 1.52 test diff --git a/test/src/test/resources/hudson/model/UsageStatisticsTest/jobs.json b/test/src/test/resources/hudson/model/UsageStatisticsTest/jobs.json index 1e2cebac29..c86a459197 100644 --- a/test/src/test/resources/hudson/model/UsageStatisticsTest/jobs.json +++ b/test/src/test/resources/hudson/model/UsageStatisticsTest/jobs.json @@ -1 +1 @@ -{"com-cloudbees-hudson-plugins-folder-Folder":0,"hudson-matrix-MatrixProject":0,"hudson-model-FreeStyleProject":0,"org-jenkinsci-plugins-workflow-job-WorkflowJob":0,"org-jvnet-hudson-test-MockFolder":0,"org-jvnet-hudson-test-SecuredMockFolder":0} \ No newline at end of file +{"com-cloudbees-hudson-plugins-folder-Folder":0,"hudson-matrix-MatrixProject":0,"hudson-model-FreeStyleProject":0,"org-jvnet-hudson-test-MockFolder":0,"org-jvnet-hudson-test-SecuredMockFolder":0} \ No newline at end of file From 319b91053e8a67d8913f2eb2607b525c240f00b0 Mon Sep 17 00:00:00 2001 From: Stefan Spieker Date: Tue, 17 Aug 2021 21:41:40 +0200 Subject: [PATCH 79/98] Minor deprecation fixes (#5668) --- core/src/main/java/hudson/util/BootFailure.java | 4 +++- core/src/main/java/hudson/util/SecretRewriter.java | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/hudson/util/BootFailure.java b/core/src/main/java/hudson/util/BootFailure.java index ae122e8c42..3b4f1300a7 100644 --- a/core/src/main/java/hudson/util/BootFailure.java +++ b/core/src/main/java/hudson/util/BootFailure.java @@ -10,6 +10,7 @@ import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; +import java.text.DateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -62,9 +63,10 @@ public abstract class BootFailure extends ErrorObject { if (f.exists()) { try (BufferedReader failureFileReader = new BufferedReader(new FileReader(f))) { String line; + DateFormat df = DateFormat.getDateInstance(); while ((line=failureFileReader.readLine())!=null) { try { - dates.add(new Date(line)); + dates.add(df.parse(line)); } catch (Exception e) { // ignore any parse error } diff --git a/core/src/main/java/hudson/util/SecretRewriter.java b/core/src/main/java/hudson/util/SecretRewriter.java index 0122503366..9a98477067 100644 --- a/core/src/main/java/hudson/util/SecretRewriter.java +++ b/core/src/main/java/hudson/util/SecretRewriter.java @@ -54,7 +54,7 @@ public class SecretRewriter { this(); } - private String tryRewrite(String s) throws IOException, InvalidKeyException { + private String tryRewrite(String s) throws InvalidKeyException { if (s.length()<24) return s; // Encrypting "" in Secret produces 24-letter characters, so this must be the minimum length if (!isBase64(s)) @@ -82,7 +82,7 @@ public class SecretRewriter { public boolean rewrite(File f) throws InvalidKeyException, IOException { - AtomicFileWriter w = new AtomicFileWriter(f, "UTF-8"); + AtomicFileWriter w = new AtomicFileWriter(f.toPath(), StandardCharsets.UTF_8); try { boolean modified = false; // did we actually change anything? try (PrintWriter out = new PrintWriter(new BufferedWriter(w)); From e0ada43610d93a62b7c73659434e35d8277ffbf2 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Tue, 17 Aug 2021 12:41:46 -0700 Subject: [PATCH 80/98] Stop setting `useAntClassLoader` which nothing consumes (#5610) --- test/pom.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/pom.xml b/test/pom.xml index 395cb352b1..4264e3531f 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -217,8 +217,6 @@ THE SOFTWARE. ${jacocoSurefireArgs} -Dfile.encoding=UTF-8 -Xmx1g -Djdk.net.URLClassPath.disableClassPathURLCheck=true - - true ${mavenDebug} ${project.build.directory} From 3c8dd1664fcc2818a78a06950d47a7b7c54ad981 Mon Sep 17 00:00:00 2001 From: offa Date: Thu, 19 Aug 2021 08:56:57 +0200 Subject: [PATCH 81/98] Some test code clean-up (#5669) Co-authored-by: Tim Jacomb --- .../hudson/cli/WaitNodeOnlineCommandTest.java | 46 ++++++++----------- .../hudson/diagnosis/OldDataMonitorTest.java | 8 ---- .../hudson/model/DependencyGraphTest.java | 4 +- .../test/java/hudson/model/MyViewTest.java | 4 +- test/src/test/java/hudson/model/NodeTest.java | 6 +-- test/src/test/java/hudson/model/ViewTest.java | 4 +- .../hudson/security/SecurityRealmTest.java | 2 +- .../java/hudson/tasks/FingerprinterTest.java | 4 +- .../java/hudson/util/BootFailureTest.java | 2 +- .../java/jenkins/install/SetupWizardTest.java | 4 +- .../beanutils/BasicTagWithObjectProperty.java | 3 +- .../beanutils/DynaTagWithStringProperty.java | 3 +- 12 files changed, 34 insertions(+), 56 deletions(-) diff --git a/test/src/test/java/hudson/cli/WaitNodeOnlineCommandTest.java b/test/src/test/java/hudson/cli/WaitNodeOnlineCommandTest.java index 8e92a30f82..403714cd49 100644 --- a/test/src/test/java/hudson/cli/WaitNodeOnlineCommandTest.java +++ b/test/src/test/java/hudson/cli/WaitNodeOnlineCommandTest.java @@ -31,7 +31,6 @@ import org.junit.Rule; import org.junit.Test; import org.jvnet.hudson.test.JenkinsRule; -import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -86,15 +85,12 @@ public class WaitNodeOnlineCommandTest { slave.toComputer().setTemporarilyOffline(true); boolean timeoutOccurred = false; - FutureTask task = new FutureTask(new Callable() { - @Override - public Object call() { - final CLICommandInvoker.Result result = command - .authorizedTo(Jenkins.READ) - .invokeWithArgs("aNode"); - fail("Never should return from previous CLI call!"); - return null; - } + FutureTask task = new FutureTask(() -> { + final CLICommandInvoker.Result result = command + .authorizedTo(Jenkins.READ) + .invokeWithArgs("aNode"); + fail("Never should return from previous CLI call!"); + return null; }); try { task.get(30, TimeUnit.SECONDS); @@ -115,15 +111,12 @@ public class WaitNodeOnlineCommandTest { slave.toComputer().waitUntilOffline(); boolean timeoutOccurred = false; - FutureTask task = new FutureTask(new Callable() { - @Override - public Object call() { - final CLICommandInvoker.Result result = command - .authorizedTo(Jenkins.READ) - .invokeWithArgs("aNode"); - fail("Never should return from previous CLI call!"); - return null; - } + FutureTask task = new FutureTask(() -> { + final CLICommandInvoker.Result result = command + .authorizedTo(Jenkins.READ) + .invokeWithArgs("aNode"); + fail("Never should return from previous CLI call!"); + return null; }); try { task.get(30, TimeUnit.SECONDS); @@ -143,15 +136,12 @@ public class WaitNodeOnlineCommandTest { slave.toComputer().disconnect(); boolean timeoutOccurred = false; - FutureTask task = new FutureTask(new Callable() { - @Override - public Object call() { - final CLICommandInvoker.Result result = command - .authorizedTo(Jenkins.READ) - .invokeWithArgs("aNode"); - fail("Never should return from previous CLI call!"); - return null; - } + FutureTask task = new FutureTask(() -> { + final CLICommandInvoker.Result result = command + .authorizedTo(Jenkins.READ) + .invokeWithArgs("aNode"); + fail("Never should return from previous CLI call!"); + return null; }); try { task.get(30, TimeUnit.SECONDS); diff --git a/test/src/test/java/hudson/diagnosis/OldDataMonitorTest.java b/test/src/test/java/hudson/diagnosis/OldDataMonitorTest.java index afb198fe04..11586b9918 100644 --- a/test/src/test/java/hudson/diagnosis/OldDataMonitorTest.java +++ b/test/src/test/java/hudson/diagnosis/OldDataMonitorTest.java @@ -67,14 +67,6 @@ public class OldDataMonitorTest { OldDataMonitor odm = OldDataMonitor.get(r.jenkins); FreeStyleProject p = r.jenkins.getItemByFullName("busted", FreeStyleProject.class); assertNotNull(p); - /* - System.err.println(p.getActions()); - for (Map.Entry entry : odm.getData().entrySet()) { - System.err.println(entry.getKey()); - System.err.println(entry.getValue()); - System.err.println(entry.getValue().extra); - } - */ assertEquals(Collections.singleton(p), odm.getData().keySet()); odm.doDiscard(null, null); assertEquals(Collections.emptySet(), odm.getData().keySet()); diff --git a/test/src/test/java/hudson/model/DependencyGraphTest.java b/test/src/test/java/hudson/model/DependencyGraphTest.java index 66a3c2be32..a00bfc40be 100644 --- a/test/src/test/java/hudson/model/DependencyGraphTest.java +++ b/test/src/test/java/hudson/model/DependencyGraphTest.java @@ -69,7 +69,7 @@ public class DependencyGraphTest { j.jenkins.rebuildDependencyGraph(); // First build won't trigger down1 (Unstable doesn't meet threshold) // but will trigger down2 (build #1 is odd). - Build b = (Build)p.scheduleBuild2(0, new Cause.UserCause()).get(); + Build b = (Build)p.scheduleBuild2(0, new Cause.UserIdCause()).get(); String log = j.getLog(b); Queue.Item q = j.jenkins.getQueue().getItem(down1); assertNull("down1 should not be triggered: " + log, q); @@ -85,7 +85,7 @@ public class DependencyGraphTest { j.jenkins.rebuildDependencyGraph(); // ..and next build will trigger down1 (Success meets threshold), // but not down2 (build #2 is even) - b = (Build)p.scheduleBuild2(0, new Cause.UserCause()).get(); + b = (Build)p.scheduleBuild2(0, new Cause.UserIdCause()).get(); log = j.getLog(b); q = j.jenkins.getQueue().getItem(down2); assertNull("down2 should not be triggered: " + log, q); diff --git a/test/src/test/java/hudson/model/MyViewTest.java b/test/src/test/java/hudson/model/MyViewTest.java index d464897d9a..839dafede2 100644 --- a/test/src/test/java/hudson/model/MyViewTest.java +++ b/test/src/test/java/hudson/model/MyViewTest.java @@ -61,7 +61,7 @@ public class MyViewTest { } @Test - public void testContains() throws IOException, Exception{ + public void testContains() throws Exception{ GlobalMatrixAuthorizationStrategy auth = new GlobalMatrixAuthorizationStrategy(); rule.jenkins.setAuthorizationStrategy(auth); @@ -77,7 +77,7 @@ public class MyViewTest { } @Test - public void testDoCreateItem() throws IOException, Exception{ + public void testDoCreateItem() throws Exception{ logs.record(AbstractItem.class, Level.ALL); MyView view = new MyView("My", rule.jenkins); rule.jenkins.addView(view); diff --git a/test/src/test/java/hudson/model/NodeTest.java b/test/src/test/java/hudson/model/NodeTest.java index f6af1e766d..852795cc6c 100644 --- a/test/src/test/java/hudson/model/NodeTest.java +++ b/test/src/test/java/hudson/model/NodeTest.java @@ -408,13 +408,11 @@ public class NodeTest { } // Gather information for failure message just in case. - containsFailureMessage.append("{").append(entry.item.toString()).append(", ").append(entry.weight).append("}"); + containsFailureMessage.append("{").append(entry.item).append(", ").append(entry.weight).append("}"); } // If a label should be part of the cloud label then fail. - if (contains) { - fail(containsFailureMessage.toString() + "]"); - } + assertFalse(containsFailureMessage + "]", contains); } /** diff --git a/test/src/test/java/hudson/model/ViewTest.java b/test/src/test/java/hudson/model/ViewTest.java index 936471d32b..60b8ab410f 100644 --- a/test/src/test/java/hudson/model/ViewTest.java +++ b/test/src/test/java/hudson/model/ViewTest.java @@ -313,7 +313,7 @@ public class ViewTest { } @Test - public void testGetQueueItems() throws IOException, Exception{ + public void testGetQueueItems() throws Exception{ ListView view1 = listView("view1"); view1.filterQueue = true; ListView view2 = listView("view2"); @@ -367,7 +367,7 @@ public class ViewTest { } @Test - public void testGetComputers() throws IOException, Exception{ + public void testGetComputers() throws Exception{ ListView view1 = listView("view1"); ListView view2 = listView("view2"); ListView view3 = listView("view3"); diff --git a/test/src/test/java/hudson/security/SecurityRealmTest.java b/test/src/test/java/hudson/security/SecurityRealmTest.java index e4b5b41e31..e2f95d9093 100644 --- a/test/src/test/java/hudson/security/SecurityRealmTest.java +++ b/test/src/test/java/hudson/security/SecurityRealmTest.java @@ -143,7 +143,7 @@ public class SecurityRealmTest { } } } - System.err.println(builder.toString()); + System.err.println(builder); assertThat(unexpectedSessionCookies, is(0)); } diff --git a/test/src/test/java/hudson/tasks/FingerprinterTest.java b/test/src/test/java/hudson/tasks/FingerprinterTest.java index 2043aa2c0b..701c01e9e9 100644 --- a/test/src/test/java/hudson/tasks/FingerprinterTest.java +++ b/test/src/test/java/hudson/tasks/FingerprinterTest.java @@ -383,7 +383,7 @@ public class FingerprinterTest { } - private FreeStyleProject createFreeStyleProjectWithFingerprints(String[] contents, String[] files) throws IOException, Exception { + private FreeStyleProject createFreeStyleProjectWithFingerprints(String[] contents, String[] files) throws Exception { FreeStyleProject project = j.createFreeStyleProject(); addFingerprinterToProject(project, contents, files); @@ -391,7 +391,7 @@ public class FingerprinterTest { return project; } - private void addFingerprinterToProject(AbstractProject project, String[] contents, String[] files) throws Exception { + private void addFingerprinterToProject(AbstractProject project, String[] contents, String[] files) { StringBuilder targets = new StringBuilder(); for (int i = 0; i < contents.length; i++) { if (project instanceof MatrixProject) { diff --git a/test/src/test/java/hudson/util/BootFailureTest.java b/test/src/test/java/hudson/util/BootFailureTest.java index 4a2a0f292a..f519e372fc 100644 --- a/test/src/test/java/hudson/util/BootFailureTest.java +++ b/test/src/test/java/hudson/util/BootFailureTest.java @@ -71,7 +71,7 @@ public class BootFailureTest { ServletContext ws = createWebServer((context, server) -> { NoListenerConfiguration noListenerConfiguration = context.getBean(NoListenerConfiguration.class); // future-proof - Assert.notNull(noListenerConfiguration); + Assert.notNull(noListenerConfiguration, "Value must not be null"); if (noListenerConfiguration != null) { context.removeBean(noListenerConfiguration); context.addBean(new AbstractLifeCycle() { diff --git a/test/src/test/java/jenkins/install/SetupWizardTest.java b/test/src/test/java/jenkins/install/SetupWizardTest.java index bfa3534d81..7f34c82fe6 100644 --- a/test/src/test/java/jenkins/install/SetupWizardTest.java +++ b/test/src/test/java/jenkins/install/SetupWizardTest.java @@ -179,7 +179,7 @@ public class SetupWizardTest { private final File tmpdir; CustomLocalUpdateSite(File tmpdir) throws MalformedURLException { - super("custom-uc", tmpdir.toURI().toURL().toString() + "update-center.json"); + super("custom-uc", tmpdir.toURI().toURL() + "update-center.json"); this.tmpdir = tmpdir; } @@ -197,7 +197,7 @@ public class SetupWizardTest { private final File tmpdir; CustomLocalUpdateSiteWithWrapperJSON(File tmpdir) throws MalformedURLException { - super("custom-uc2", tmpdir.toURI().toURL().toString() + "update-center.json"); + super("custom-uc2", tmpdir.toURI().toURL() + "update-center.json"); this.tmpdir = tmpdir; } diff --git a/test/src/test/java/org/kohsuke/stapler/beanutils/BasicTagWithObjectProperty.java b/test/src/test/java/org/kohsuke/stapler/beanutils/BasicTagWithObjectProperty.java index 98d2a221f7..9c12391224 100644 --- a/test/src/test/java/org/kohsuke/stapler/beanutils/BasicTagWithObjectProperty.java +++ b/test/src/test/java/org/kohsuke/stapler/beanutils/BasicTagWithObjectProperty.java @@ -1,7 +1,6 @@ package org.kohsuke.stapler.beanutils; import org.apache.commons.jelly.JellyTagException; -import org.apache.commons.jelly.MissingAttributeException; import org.apache.commons.jelly.TagSupport; import org.apache.commons.jelly.XMLOutput; import org.xml.sax.SAXException; @@ -14,7 +13,7 @@ public class BasicTagWithObjectProperty extends TagSupport { } @Override - public void doTag(XMLOutput output) throws MissingAttributeException, JellyTagException { + public void doTag(XMLOutput output) throws JellyTagException { try { output.writeComment("Tag with object property\n"); output.writeCDATA(getClass().getName() + ":" + clazz); diff --git a/test/src/test/java/org/kohsuke/stapler/beanutils/DynaTagWithStringProperty.java b/test/src/test/java/org/kohsuke/stapler/beanutils/DynaTagWithStringProperty.java index b22de9b477..7b723c1d82 100644 --- a/test/src/test/java/org/kohsuke/stapler/beanutils/DynaTagWithStringProperty.java +++ b/test/src/test/java/org/kohsuke/stapler/beanutils/DynaTagWithStringProperty.java @@ -2,7 +2,6 @@ package org.kohsuke.stapler.beanutils; import org.apache.commons.jelly.DynaTag; import org.apache.commons.jelly.JellyTagException; -import org.apache.commons.jelly.MissingAttributeException; import org.apache.commons.jelly.TagSupport; import org.apache.commons.jelly.XMLOutput; import org.xml.sax.SAXException; @@ -15,7 +14,7 @@ public class DynaTagWithStringProperty extends TagSupport implements DynaTag { } @Override - public void doTag(XMLOutput output) throws MissingAttributeException, JellyTagException { + public void doTag(XMLOutput output) throws JellyTagException { try { output.writeComment("Tag with string property\n"); output.writeCDATA(getClass().getName() + ":" + clazz); From 485450d0456237f2cfd186b4a640328683bdce79 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Aug 2021 06:57:14 +0000 Subject: [PATCH 82/98] Bump spring-security-bom from 5.5.1 to 5.5.2 (#5672) --- bom/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/pom.xml b/bom/pom.xml index e9ab4106a6..ba477f0b1b 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -49,7 +49,7 @@ THE SOFTWARE. org.springframework.security spring-security-bom - 5.5.1 + 5.5.2 pom import From 2ea81d3f7f8ef3c64b5e19fdd57cac547f074fe7 Mon Sep 17 00:00:00 2001 From: Daniel Beck Date: Thu, 19 Aug 2021 20:01:22 +0200 Subject: [PATCH 83/98] Terminology: Warn about use of 'master' in label expression --- core/src/main/java/hudson/model/labels/LabelExpression.java | 6 ++++++ core/src/main/resources/hudson/model/Messages.properties | 2 ++ 2 files changed, 8 insertions(+) diff --git a/core/src/main/java/hudson/model/labels/LabelExpression.java b/core/src/main/java/hudson/model/labels/LabelExpression.java index 688167fa4b..8443174fdb 100644 --- a/core/src/main/java/hudson/model/labels/LabelExpression.java +++ b/core/src/main/java/hudson/model/labels/LabelExpression.java @@ -286,6 +286,12 @@ public abstract class LabelExpression extends Label { final Jenkins j = Jenkins.get(); Label l = j.getLabel(expression); if (l.isEmpty()) { + final LabelAtom masterLabel = LabelAtom.get("master"); + if (!masterLabel.equals(Jenkins.get().getSelfLabel()) && l.listAtoms().contains(masterLabel) && masterLabel.isEmpty()) { + // Show a warning if this expression's lack of nodes and clouds is likely caused by the built-in node name migration. + // This can probably be done better (e.g. also when `!l.isEmpty()`), but it's a start. + return FormValidation.warningWithMarkup(Messages.LabelExpression_ObsoleteMasterLabel()); + } for (LabelAtom a : l.listAtoms()) { if (a.isEmpty()) { LabelAtom nearest = LabelAtom.findNearest(a.getName()); diff --git a/core/src/main/resources/hudson/model/Messages.properties b/core/src/main/resources/hudson/model/Messages.properties index 5ee1cdb2a2..6981bc4b3a 100644 --- a/core/src/main/resources/hudson/model/Messages.properties +++ b/core/src/main/resources/hudson/model/Messages.properties @@ -189,6 +189,8 @@ Label.GroupOf=group of {0} Label.InvalidLabel=invalid label Label.ProvisionedFrom=Provisioned from {0} LabelExpression.InvalidBooleanExpression=Invalid boolean expression: {0} +LabelExpression_ObsoleteMasterLabel=This expression includes the label master that is no longer used for the built-in node. Use built-in instead. \ + Learn more. LabelExpression.LabelLink=Label {1} matches {3,choice,0#no nodes|1#1 node|1<{3} nodes}{4,choice,0#|1# and 1 cloud|1< and {4} clouds}. \ Permissions or other restrictions provided by plugins may further reduce that list. LabelExpression.NoMatch=No agent/cloud matches this label expression. From d3acf6ac9dd529326a6f11745a1b6ccc16e1a94c Mon Sep 17 00:00:00 2001 From: James Nord Date: Sun, 22 Aug 2021 21:18:01 +0100 Subject: [PATCH 84/98] [JENKINS-66379] upgrade winstone to allow non JKS keystores for https certificates (#5670) * [JENKINS-66379] upgrade winstone to pickup upstream fix The change uses the JVM default keystore. This allows jenkins to be used for TLS termination even when JKS is not the default keystore, or is not allowed for other restrictions. If someone has a non default keystore yet is using a keystore here then this will break on upgrade, however it is unlikely that they would be having a non default keystore as the normal reason for doing that is due to a restriction like FIPS-140 and given the keystore we require has a key using JDK here would be non compliant. * Update war/pom.xml update winstone to release version (5.21) --- war/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/war/pom.xml b/war/pom.xml index 3d84efb9f2..a6bc9e2fa1 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -106,7 +106,7 @@ THE SOFTWARE. org.jenkins-ci winstone - 5.20 + 5.21 test From 04a98ff1349aa530907f81fcf40d146ea1035d1d Mon Sep 17 00:00:00 2001 From: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Mon, 23 Aug 2021 08:28:16 +0100 Subject: [PATCH 85/98] Revamp Feed bar and Add/Edit description button to be consistent + correctly spaced (#5664) Co-authored-by: Oleg Nenashev --- .../logging/LogRecorderManager/feeds.jelly | 12 ++-- .../AbstractModelObject/descriptionForm.jelly | 2 +- .../AbstractModelObject/editDescription.jelly | 2 +- .../hudson/widgets/HistoryWidget/index.jelly | 4 +- .../lib/hudson/editableDescription.jelly | 26 +++++---- .../lib/hudson/editableDescription.properties | 24 ++++++++ .../main/resources/lib/hudson/iconSize.jelly | 54 ++++++++++-------- .../resources/lib/hudson/iconSize.properties | 6 +- .../main/resources/lib/hudson/rssBar.jelly | 56 ++++++++----------- .../resources/lib/hudson/rssBar.properties | 1 + .../main/resources/lib/layout/breadcrumbs.css | 2 +- .../resources/lib/layout/layout.properties | 1 + war/src/main/less/base-styles-v2.less | 1 + war/src/main/less/base/style.less | 13 ----- war/src/main/less/base/visibility-utils.less | 24 ++++++++ war/src/main/less/modules/buttons.less | 18 ++++++ war/src/main/less/modules/icon-size.less | 47 ++++++++++++++++ .../webapp/images/material-icons/edit.svg | 6 ++ .../webapp/images/material-icons/feed.svg | 6 ++ .../svg-sprite-communication-symbol.svg | 1 - 20 files changed, 213 insertions(+), 93 deletions(-) create mode 100644 core/src/main/resources/lib/hudson/editableDescription.properties create mode 100644 core/src/main/resources/lib/hudson/rssBar.properties create mode 100644 war/src/main/less/modules/icon-size.less create mode 100644 war/src/main/webapp/images/material-icons/edit.svg create mode 100644 war/src/main/webapp/images/material-icons/feed.svg delete mode 100644 war/src/main/webapp/images/material-icons/svg-sprite-communication-symbol.svg diff --git a/core/src/main/resources/hudson/logging/LogRecorderManager/feeds.jelly b/core/src/main/resources/hudson/logging/LogRecorderManager/feeds.jelly index e64510514e..c9841b5b5b 100644 --- a/core/src/main/resources/hudson/logging/LogRecorderManager/feeds.jelly +++ b/core/src/main/resources/hudson/logging/LogRecorderManager/feeds.jelly @@ -28,22 +28,22 @@ THE SOFTWARE.
- + ${%All} - + ${%SEVERE} - + ${%WARNING} diff --git a/core/src/main/resources/hudson/model/AbstractModelObject/descriptionForm.jelly b/core/src/main/resources/hudson/model/AbstractModelObject/descriptionForm.jelly index 8bb30e90ce..2d793ab4b9 100644 --- a/core/src/main/resources/hudson/model/AbstractModelObject/descriptionForm.jelly +++ b/core/src/main/resources/hudson/model/AbstractModelObject/descriptionForm.jelly @@ -39,7 +39,7 @@ THE SOFTWARE.
- +
diff --git a/core/src/main/resources/hudson/model/AbstractModelObject/editDescription.jelly b/core/src/main/resources/hudson/model/AbstractModelObject/editDescription.jelly index 5ee89972ff..9d715ab6f3 100644 --- a/core/src/main/resources/hudson/model/AbstractModelObject/editDescription.jelly +++ b/core/src/main/resources/hudson/model/AbstractModelObject/editDescription.jelly @@ -39,7 +39,7 @@ THE SOFTWARE. codemirror-mode="${app.markupFormatter.codeMirrorMode}" codemirror-config="${app.markupFormatter.codeMirrorConfig}" previewEndpoint="/markupFormatter/previewDescription"/> - + diff --git a/core/src/main/resources/hudson/widgets/HistoryWidget/index.jelly b/core/src/main/resources/hudson/widgets/HistoryWidget/index.jelly index ad4d05280f..7b56363123 100644 --- a/core/src/main/resources/hudson/widgets/HistoryWidget/index.jelly +++ b/core/src/main/resources/hudson/widgets/HistoryWidget/index.jelly @@ -42,11 +42,11 @@ THE SOFTWARE. --> - + Atom feed ${%for all} - + Atom feed ${%for failures} diff --git a/core/src/main/resources/lib/hudson/editableDescription.jelly b/core/src/main/resources/lib/hudson/editableDescription.jelly index 585b844c2b..7517833b57 100644 --- a/core/src/main/resources/lib/hudson/editableDescription.jelly +++ b/core/src/main/resources/lib/hudson/editableDescription.jelly @@ -40,17 +40,21 @@ THE SOFTWARE.
- + diff --git a/core/src/main/resources/lib/hudson/editableDescription.properties b/core/src/main/resources/lib/hudson/editableDescription.properties new file mode 100644 index 0000000000..0d5b672a9b --- /dev/null +++ b/core/src/main/resources/lib/hudson/editableDescription.properties @@ -0,0 +1,24 @@ +# The MIT License +# +# Copyright (c) 2021, Jenkins contributors +# +# 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. + +add\ description=Add description +edit\ description=Edit description diff --git a/core/src/main/resources/lib/hudson/iconSize.jelly b/core/src/main/resources/lib/hudson/iconSize.jelly index 4dd5fbeff1..1fbb33d8c9 100644 --- a/core/src/main/resources/lib/hudson/iconSize.jelly +++ b/core/src/main/resources/lib/hudson/iconSize.jelly @@ -23,30 +23,40 @@ THE SOFTWARE. --> - - - - - - - - - ${title} - - - ${title} - - - - + +
+ + + + + +
  • + ${title.charAt(0)}${title.substring(1)} +
  • +
    + +
  • + + ${title.charAt(0)}${title.substring(1)} + +
  • +
    +
    +
    +
    -
    - -
    ${%Icon}: +
    + ${%Icon}: +
      -
    - -
    + + + +
    + +
    +
    diff --git a/core/src/main/resources/lib/hudson/iconSize.properties b/core/src/main/resources/lib/hudson/iconSize.properties index d4292959f9..517fa42843 100644 --- a/core/src/main/resources/lib/hudson/iconSize.properties +++ b/core/src/main/resources/lib/hudson/iconSize.properties @@ -21,6 +21,6 @@ # THE SOFTWARE. Icon=Icon -S=S -M=M -L=L +S=Small +M=Medium +L=Large diff --git a/core/src/main/resources/lib/hudson/rssBar.jelly b/core/src/main/resources/lib/hudson/rssBar.jelly index dcd9842c86..734cc36f5d 100644 --- a/core/src/main/resources/lib/hudson/rssBar.jelly +++ b/core/src/main/resources/lib/hudson/rssBar.jelly @@ -25,39 +25,31 @@ THE SOFTWARE. - diff --git a/core/src/main/resources/lib/hudson/rssBar.properties b/core/src/main/resources/lib/hudson/rssBar.properties new file mode 100644 index 0000000000..b48bf0b490 --- /dev/null +++ b/core/src/main/resources/lib/hudson/rssBar.properties @@ -0,0 +1 @@ +Legend=Icon legend diff --git a/core/src/main/resources/lib/layout/breadcrumbs.css b/core/src/main/resources/lib/layout/breadcrumbs.css index b9d0840a5c..77483bc52d 100644 --- a/core/src/main/resources/lib/layout/breadcrumbs.css +++ b/core/src/main/resources/lib/layout/breadcrumbs.css @@ -100,7 +100,7 @@ #breadcrumbs LI.children:after, #breadcrumbs LI.separator:after { /* Right arrow */ content: ""; - border-left: 0.25em solid #bcbcbc; + border-left: 0.25em solid currentColor; border-top: 0.25em solid transparent; border-bottom: 0.25em solid transparent; border-right: 0; diff --git a/core/src/main/resources/lib/layout/layout.properties b/core/src/main/resources/lib/layout/layout.properties index 84295a04e9..35e647a0c0 100644 --- a/core/src/main/resources/lib/layout/layout.properties +++ b/core/src/main/resources/lib/layout/layout.properties @@ -25,3 +25,4 @@ searchBox.url=https://www.jenkins.io/redirect/search-box logout=log out title=Jenkins jenkinshead.alt=Jenkins +search=Search \ No newline at end of file diff --git a/war/src/main/less/base-styles-v2.less b/war/src/main/less/base-styles-v2.less index 6171a10ef5..1bc091f3d7 100644 --- a/war/src/main/less/base-styles-v2.less +++ b/war/src/main/less/base-styles-v2.less @@ -23,6 +23,7 @@ html { @import './base/style.less'; @import './base/visibility-utils.less'; @import './base/yui-compatibility.less'; +@import './modules/icon-size.less'; @import './modules/icons.less'; @import './modules/buttons.less'; @import './modules/page-header.less'; diff --git a/war/src/main/less/base/style.less b/war/src/main/less/base/style.less index a5af8b5137..cf2a2a1bb1 100644 --- a/war/src/main/less/base/style.less +++ b/war/src/main/less/base/style.less @@ -1713,19 +1713,6 @@ svg.icon-xlg { /* -------------------------------------- */ -/* --------------- RSS bar -------------- */ - -#rss-bar { - margin: 1em; - text-align: right; -} - -#rss-bar .icon-rss { - border: 0; -} - -/* -------------------------------------- */ - /* -------------- SVG icons ------------- */ diff --git a/war/src/main/less/base/visibility-utils.less b/war/src/main/less/base/visibility-utils.less index 7ad346c26a..5fe7862799 100644 --- a/war/src/main/less/base/visibility-utils.less +++ b/war/src/main/less/base/visibility-utils.less @@ -141,3 +141,27 @@ display: none !important; } } + +/// Hide an element visually, but have it available for screen readers +.jenkins-visually-hidden { + position: absolute; + + width: 1px; + height: 1px; + + // If margin is set to a negative value it can cause text to be announced in + // the wrong order in VoiceOver for OSX + margin: 0; + padding: 0; + + overflow: hidden; + clip: rect(0 0 0 0); + clip-path: inset(50%); + + border: 0; + + // For long content, line feeds are not interpreted as spaces and small width + // causes content to wrap 1 word per line: + // https://medium.com/@jessebeach/beware-smushed-off-screen-accessible-text-5952a4c2cbfe + white-space: nowrap; +} diff --git a/war/src/main/less/modules/buttons.less b/war/src/main/less/modules/buttons.less index 359329b3f5..4102872089 100644 --- a/war/src/main/less/modules/buttons.less +++ b/war/src/main/less/modules/buttons.less @@ -304,3 +304,21 @@ a.yui-button.icon-button.large-button { font-size: 1.25rem; } } + +.jenkins-buttons-row { + display: flex; + + .yui-button { + margin-left: 0!important; + margin-right: 0.5rem!important; + } + + &--invert { + justify-content: flex-end; + + .yui-button { + margin-left: 0.5rem!important; + margin-right: 0!important; + } + } +} \ No newline at end of file diff --git a/war/src/main/less/modules/icon-size.less b/war/src/main/less/modules/icon-size.less new file mode 100644 index 0000000000..d5cdbda499 --- /dev/null +++ b/war/src/main/less/modules/icon-size.less @@ -0,0 +1,47 @@ +.jenkins-icon-size { + display: flex; + align-items: center; + justify-content: space-between; + margin: 15px 0; + + &__items { + display: flex; + align-items: center; + + ol { + margin: 0; + padding: 0; + list-style-type: none; + display: flex; + align-items: center; + } + + li { + width: 32px; + margin-right: 0.2rem; + + .yui-button { + margin: 0!important; + padding: 0!important; + width: 32px!important; + height: 32px!important; + min-width: 0!important; + } + } + + .jenkins-icon-size__items-item { + height: 32px; + width: 32px; + border-radius: 4px; + line-height: 32px; + background: var(--btn-link-bg--hover); + text-align: center; + font-weight: var(--btn-link-font-weight); + font-size: var(--btn-font-size); + } + + & > * { + margin-right: 0.5rem; + } + } +} diff --git a/war/src/main/webapp/images/material-icons/edit.svg b/war/src/main/webapp/images/material-icons/edit.svg new file mode 100644 index 0000000000..6288932905 --- /dev/null +++ b/war/src/main/webapp/images/material-icons/edit.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/war/src/main/webapp/images/material-icons/feed.svg b/war/src/main/webapp/images/material-icons/feed.svg new file mode 100644 index 0000000000..42d72645a1 --- /dev/null +++ b/war/src/main/webapp/images/material-icons/feed.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/war/src/main/webapp/images/material-icons/svg-sprite-communication-symbol.svg b/war/src/main/webapp/images/material-icons/svg-sprite-communication-symbol.svg deleted file mode 100644 index 0fd7304b8c..0000000000 --- a/war/src/main/webapp/images/material-icons/svg-sprite-communication-symbol.svg +++ /dev/null @@ -1 +0,0 @@ - From 85797ebe63425844d922ca8ef6bb77f8aad1951b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Aug 2021 08:31:15 +0100 Subject: [PATCH 86/98] Bump mockito-core from 3.11.2 to 3.12.1 (#5676) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.11.2 to 3.12.1. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.11.2...v3.12.1) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e68d080729..4a303fc4a2 100644 --- a/pom.xml +++ b/pom.xml @@ -131,7 +131,7 @@ THE SOFTWARE. org.mockito mockito-core - 3.11.2 + 3.12.1 From 16854fef0e0128bda2c5feff9b897c6ad0bf3da2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Aug 2021 18:04:46 +0000 Subject: [PATCH 87/98] Bump spotless-maven-plugin from 2.12.2 to 2.12.3 Bumps [spotless-maven-plugin](https://github.com/diffplug/spotless) from 2.12.2 to 2.12.3. - [Release notes](https://github.com/diffplug/spotless/releases) - [Changelog](https://github.com/diffplug/spotless/blob/main/CHANGES.md) - [Commits](https://github.com/diffplug/spotless/compare/maven/2.12.2...maven/2.12.3) --- updated-dependencies: - dependency-name: com.diffplug.spotless:spotless-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4a303fc4a2..3e7fad2fe4 100644 --- a/pom.xml +++ b/pom.xml @@ -103,7 +103,7 @@ THE SOFTWARE. 1.24 5.7.2 2.0.9 - 2.12.2 + 2.12.3
    - + diff --git a/core/src/main/resources/hudson/model/ComputerSet/sidepanel.jelly b/core/src/main/resources/hudson/model/ComputerSet/sidepanel.jelly index 6940dd6714..d26786989f 100644 --- a/core/src/main/resources/hudson/model/ComputerSet/sidepanel.jelly +++ b/core/src/main/resources/hudson/model/ComputerSet/sidepanel.jelly @@ -32,11 +32,11 @@ THE SOFTWARE. - + - + diff --git a/core/src/main/resources/hudson/model/Label/sidepanel.jelly b/core/src/main/resources/hudson/model/Label/sidepanel.jelly index 7d662a0a45..5b4608c846 100644 --- a/core/src/main/resources/hudson/model/Label/sidepanel.jelly +++ b/core/src/main/resources/hudson/model/Label/sidepanel.jelly @@ -34,7 +34,7 @@ THE SOFTWARE. - + diff --git a/core/src/main/resources/hudson/model/UpdateCenter/sidepanel.jelly b/core/src/main/resources/hudson/model/UpdateCenter/sidepanel.jelly index d9625aa282..5f62d34583 100644 --- a/core/src/main/resources/hudson/model/UpdateCenter/sidepanel.jelly +++ b/core/src/main/resources/hudson/model/UpdateCenter/sidepanel.jelly @@ -31,7 +31,7 @@ THE SOFTWARE. - + diff --git a/core/src/main/resources/hudson/model/User/sidepanel.jelly b/core/src/main/resources/hudson/model/User/sidepanel.jelly index 46bad7a605..40d91da48b 100644 --- a/core/src/main/resources/hudson/model/User/sidepanel.jelly +++ b/core/src/main/resources/hudson/model/User/sidepanel.jelly @@ -34,7 +34,7 @@ THE SOFTWARE. - + diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/index.jelly b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/index.jelly index 8a5fde08fe..9f1ab4374e 100644 --- a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/index.jelly +++ b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/index.jelly @@ -43,7 +43,7 @@ THE SOFTWARE. ${user.id} ${user} - + diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/sidepanel.jelly b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/sidepanel.jelly index 8e3c84edcc..eeb89b3e88 100644 --- a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/sidepanel.jelly +++ b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/sidepanel.jelly @@ -28,7 +28,7 @@ THE SOFTWARE. - + diff --git a/core/src/main/resources/lib/hudson/project/configurable.jelly b/core/src/main/resources/lib/hudson/project/configurable.jelly index 91569d4f82..eacf85006e 100644 --- a/core/src/main/resources/lib/hudson/project/configurable.jelly +++ b/core/src/main/resources/lib/hudson/project/configurable.jelly @@ -38,10 +38,10 @@ THE SOFTWARE. - + - + diff --git a/core/src/main/resources/lib/layout/breadcrumbs.css b/core/src/main/resources/lib/layout/breadcrumbs.css index 77483bc52d..f808e93864 100644 --- a/core/src/main/resources/lib/layout/breadcrumbs.css +++ b/core/src/main/resources/lib/layout/breadcrumbs.css @@ -18,7 +18,7 @@ min-height: 2.5rem; font-size: 1rem; line-height: 1.5; - padding: 0 1.25rem; + padding: 0 0.7rem; background-color: #F8F8F8; background-color: var(--breadcrumbs-bg); } diff --git a/test/src/test/java/hudson/tasks/UserAvatarResolverTest.java b/test/src/test/java/hudson/tasks/UserAvatarResolverTest.java index ef3e4b1e94..53b1e37c1e 100644 --- a/test/src/test/java/hudson/tasks/UserAvatarResolverTest.java +++ b/test/src/test/java/hudson/tasks/UserAvatarResolverTest.java @@ -23,6 +23,8 @@ */ package hudson.tasks; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.endsWith; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -45,21 +47,21 @@ public class UserAvatarResolverTest { @Test public void defaultImageIsReturnedIfRegexFails() { - String avatar = UserAvatarResolver.resolve(User.get("USER"), "meh"); - assertTrue(avatar.endsWith("/images/meh/user.png")); + String avatar = UserAvatarResolver.resolve(User.getOrCreateByIdOrFullName("USER"), "meh"); + assertThat(avatar, endsWith("/images/svgs/person.svg")); } @Test public void resolverIsUsed() { - expUser = User.get("unique-user-not-used-in-anyother-test", true); + expUser = User.getOrCreateByIdOrFullName("unique-user-not-used-in-anyother-test"); String avatar = UserAvatarResolver.resolve(expUser, "20x20"); assertEquals("http://myown.image", avatar); } @Test public void noResolverCanFindAvatar() { - String avatar = UserAvatarResolver.resolve(User.get("USER"), "20x20"); - assertTrue(avatar.endsWith("/images/20x20/user.png")); + String avatar = UserAvatarResolver.resolve(User.getOrCreateByIdOrFullName("USER"), "20x20"); + assertThat(avatar, endsWith("/images/svgs/person.svg")); } @Test @@ -73,7 +75,6 @@ public class UserAvatarResolverTest { @Extension public static final class UserAvatarResolverImpl extends UserAvatarResolver { - @Override public String findAvatarFor(User u, int width, int height) { if (u != null && u == expUser) { diff --git a/test/src/test/java/lib/layout/IconTest.java b/test/src/test/java/lib/layout/IconTest.java index 7671ec3446..5139ccdc38 100644 --- a/test/src/test/java/lib/layout/IconTest.java +++ b/test/src/test/java/lib/layout/IconTest.java @@ -63,7 +63,9 @@ public class IconTest { public void testIcons() throws Exception { HtmlPage p = j.createWebClient().goTo("testIcons"); DomElement iconsBlock = p.getElementById("iconsBlock"); - List icons = StreamSupport.stream(iconsBlock.getChildElements().spliterator(), false).collect(Collectors.toList()); + List icons = StreamSupport + .stream(iconsBlock.getChildElements().spliterator(), false) + .collect(Collectors.toList()); assertIconToImageOkay(icons.get(0), "/images/16x16/empty.png", "icon-empty icon-sm"); assertIconToImageOkay(icons.get(1), "/images/24x24/empty.png", "icon-empty icon-md"); @@ -96,7 +98,8 @@ public class IconTest { DomElement statusIcons = p.getElementById("statusIcons"); List statusIconsList = StreamSupport.stream(statusIcons.getChildElements().spliterator(), false).collect(Collectors.toList()); - assertIconToImageOkay(statusIconsList.get(0), "/images/32x32/folder.png", "icon-folder icon-lg"); + + assertIconToImageOkay(statusIconsList.get(0), "/images/svgs/user.svg", "icon-user icon-xlg"); assertIconToImageOkay(statusIconsList.get(1), "/plugin/12345/icons/s2.png"); } @@ -112,9 +115,8 @@ public class IconTest { return BallColor.ABORTED; } - public StatusIcon getStatusIcon1() { - return new StockStatusIcon("folder.png", new NonLocalizable("A Folder")); + return new StockStatusIcon("user.svg", new NonLocalizable("A User")); } public StatusIcon getStatusIcon2() { @@ -138,11 +140,13 @@ public class IconTest { DomElement tasksDiv = p.getElementById("tasks"); List taskDivs = StreamSupport.stream(tasksDiv.getChildElements().spliterator(), false).collect(Collectors.toList()); - assertIconToImageOkay(taskDivs.get(0).getElementsByTagName("img").get(0), "/images/24x24/up.png", "icon-up icon-md"); + assertIconToImageOkay(taskDivs.get(0).getElementsByTagName("img").get(0), "/images/svgs/up.svg", "icon-up icon-md"); + // this is loading the png from cloudbees-folder plugin + // when this is swapped to an SVG and the dep updated this test will need to change assertIconToImageOkay(taskDivs.get(1).getElementsByTagName("img").get(0), "/images/24x24/folder.png", "icon-folder icon-md"); - assertIconToImageOkay(taskDivs.get(2).getElementsByTagName("img").get(0), "/images/16x16/package.png", "icon-package icon-sm"); - assertIconToImageOkay(taskDivs.get(3).getElementsByTagName("img").get(0), "/images/16x16/package.png", "icon-package icon-sm"); - assertIconToImageOkay(taskDivs.get(4).getElementsByTagName("img").get(0), "/images/16x16/package.png", "icon-package icon-sm"); + assertIconToImageOkay(taskDivs.get(2).getElementsByTagName("img").get(0), "/images/svgs/package.svg", "icon-package icon-xlg"); + assertIconToImageOkay(taskDivs.get(3).getElementsByTagName("img").get(0), "/images/svgs/package.svg", "icon-package icon-xlg"); + assertIconToImageOkay(taskDivs.get(4).getElementsByTagName("img").get(0), "/images/svgs/package.svg", "icon-package icon-xlg"); assertIconToImageOkay(taskDivs.get(5).getElementsByTagName("img").get(0), "/plugin/xxx/icon.png"); assertIconToImageOkay(taskDivs.get(6).getElementsByTagName("img").get(0), "/plugin/xxx/icon.png"); @@ -164,7 +168,7 @@ public class IconTest { assertThat("img", is(icon.getTagName())); assertThat(icon.getAttribute("src"), endsWith(imgPath)); if (classSpec != null) { - assertThat(classSpec, is(icon.getAttribute("class"))); + assertThat(icon.getAttribute("class"), is(classSpec)); } } diff --git a/test/src/test/resources/lib/layout/IconTest/TestTasks/index.jelly b/test/src/test/resources/lib/layout/IconTest/TestTasks/index.jelly index 0678eaba41..d03d2daab2 100644 --- a/test/src/test/resources/lib/layout/IconTest/TestTasks/index.jelly +++ b/test/src/test/resources/lib/layout/IconTest/TestTasks/index.jelly @@ -32,9 +32,9 @@ THE SOFTWARE. - - - + + + diff --git a/war/images/Tango-Palette.png b/war/images/Tango-Palette.png deleted file mode 100644 index 63b8eef6a1bd39b324d3514babe93de8221b2e36..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47070 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 AiU0rr diff --git a/war/images/accept-24x24.svg b/war/images/accept-24x24.svg deleted file mode 100644 index c828e5dd2a..0000000000 --- a/war/images/accept-24x24.svg +++ /dev/null @@ -1,601 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - Rodney Dawes - - - - - Jakub Steiner, Garrett LeSage - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/war/images/blue.svg b/war/images/blue.svg deleted file mode 100644 index cd73d4b61f..0000000000 --- a/war/images/blue.svg +++ /dev/null @@ -1,266 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - Face - Plain - - - emoticon - emote - face - plain - :| - :-| - - - - - - Steven Garrity - - - http://www.tango-project.org - - - Based on face-smile by jimmac - - - - - - - - - - - - - - - - - - - diff --git a/war/images/compress_png.sh b/war/images/compress_png.sh deleted file mode 100755 index 7d1c37a30c..0000000000 --- a/war/images/compress_png.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -function compress { - echo "Crushing $1" - pngcrush -rem gAMA -rem cHRM -rem iCCP -rem sRGB $1 $1.tmp - mv $1.tmp $1 - echo "Compressing $1" - optipng -o9 $1 > /dev/null -} - -if [ -z "$*" ] -then - echo 'Please pass a list of pngs to compress.'; -fi - -for image in "$@" # Doesn't work properly if "$*" isn't quoted. -do - compress $image -done - diff --git a/war/images/dark-grey.svg b/war/images/dark-grey.svg deleted file mode 100644 index 72f8d199aa..0000000000 --- a/war/images/dark-grey.svg +++ /dev/null @@ -1,178 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - emoticon - emote - face - plain - :| - :-| - - - - - - Steven Garrity - - - http://www.tango-project.org - - - Based on face-smile by jimmac - - - - - - - - - - - - - - - - - - - diff --git a/war/images/gear.svg b/war/images/gear.svg deleted file mode 100644 index f5b116006a..0000000000 --- a/war/images/gear.svg +++ /dev/null @@ -1,181 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - Jakub Steiner - - - http://jimmac.musichall.cz - - Emblem System - - - emblem - system - library - crucial - base - - - - - - - - - - - - - - - - - - - - diff --git a/war/images/green.svg b/war/images/green.svg deleted file mode 100644 index 9fd8c88e20..0000000000 --- a/war/images/green.svg +++ /dev/null @@ -1,273 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - Face - Plain - - - emoticon - emote - face - plain - :| - :-| - - - - - - Steven Garrity - - - http://www.tango-project.org - - - Based on face-smile by jimmac - - - - - - - - - - - - - - - - - - - diff --git a/war/images/health-00to19.svg b/war/images/health-00to19.svg deleted file mode 100644 index 8fb48bdecc..0000000000 --- a/war/images/health-00to19.svg +++ /dev/null @@ -1,5206 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - weather-storm - January 2006 - - - Ryan Collier - - - - - http://www.tango-project.org - - - http://www.pseudocode.org - - - weather - applet - notify - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/war/images/health-20to39.svg b/war/images/health-20to39.svg deleted file mode 100644 index 162d09a403..0000000000 --- a/war/images/health-20to39.svg +++ /dev/null @@ -1,6507 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - weather-scattered-showers - January 2006 - - - Ryan collier (pseudo) - - - - - http://www.tango-project.org - - - http://www.pseudocode.org - - - weather - appplet - notify - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/war/images/health-40to59.svg b/war/images/health-40to59.svg deleted file mode 100644 index 442e7d0f49..0000000000 --- a/war/images/health-40to59.svg +++ /dev/null @@ -1,3034 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - weather-overcast - January 2006 - - - Ryan Collier (pseudo) - - - - - http://www.tango-project.org - - - http://www.pseudocode.org - - - weather - applet - notify - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/war/images/health-60to79.svg b/war/images/health-60to79.svg deleted file mode 100644 index 2d32ab096b..0000000000 --- a/war/images/health-60to79.svg +++ /dev/null @@ -1,736 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - weather-clear - January 2006 - - - Ryan Collier (pseudo) - - - - - http://www.tango-project.org - - - http://www.pseudocode.org - - - weather - applet - notification - - - - - - Garrett LeSage - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/war/images/health-80plus.svg b/war/images/health-80plus.svg deleted file mode 100644 index 7cd1ce6ac7..0000000000 --- a/war/images/health-80plus.svg +++ /dev/null @@ -1,1322 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - weather-clear - January 2006 - - - Ryan Collier (pseudo) - - - - - http://www.tango-project.org - - - http://www.pseudocode.org - - - weather - applet - notification - - - - - - Garrett LeSage - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/war/images/hudson-logo.vsd b/war/images/hudson-logo.vsd deleted file mode 100644 index 80b02dc4e62e8dc62c8db5421eb61d91914fa8de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 101888 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 diff --git a/war/images/light-grey.svg b/war/images/light-grey.svg deleted file mode 100644 index 0d481ac309..0000000000 --- a/war/images/light-grey.svg +++ /dev/null @@ -1,178 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - emoticon - emote - face - plain - :| - :-| - - - - - - Steven Garrity - - - http://www.tango-project.org - - - Based on face-smile by jimmac - - - - - - - - - - - - - - - - - - - diff --git a/war/images/make.sh b/war/images/make.sh deleted file mode 100755 index 3f3c16a24b..0000000000 --- a/war/images/make.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/sh -e -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -# this script requires these tools to be installed: -# * ImageMagick (http://www.imagemagick.org/) -# * inkscape (http://inkscape.org/) - -for src in *.svg -do - echo processing $src - e=$(echo $src | sed -e s/.svg/.png/ ) - for sz in 16 24 32 48 - do - dst=${sz}x${sz}/$e - if [ ! -e $dst -o $src -nt $dst ]; - then - mkdir ${sz}x${sz} > /dev/null 2>&1 || true - ./svg2png -w $sz -h $sz < $src > $dst - ./compress_png.sh $dst - #convert t.png \( +clone -fill white -draw 'color 0,0 reset' \) \ - # -compose Dst_Over $dst - # composite -compose Dst_Over -tile xc:white t.png $dst - # rm t.png - fi - - gif=$(echo $dst | sed -e s/.png/.gif/) - if [ ! -e $gif -o $dst -nt $gif ]; - then - convert $dst -background white -flatten -transparent white $gif - fi - done -done diff --git a/war/images/makeBalls.sh b/war/images/makeBalls.sh deleted file mode 100755 index 26b49cc441..0000000000 --- a/war/images/makeBalls.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/sh -ex -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - - -# build flashing balls - -for sz in 16x16 24x24 32x32 48x48 -do - for color in light-grey dark-grey grey blue yellow red green - do - cp $sz/$color.gif ../src/main/webapp/images/$sz/$color.gif - ./makeFlash.sh $sz/$color.gif ../src/main/webapp/images/$sz/${color}_anime.gif - done -done diff --git a/war/images/makeFlash.sh b/war/images/makeFlash.sh deleted file mode 100755 index 6d7e29575b..0000000000 --- a/war/images/makeFlash.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/sh -ex -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - - -# build a flashing animation image from a still picture -# Usage -t=/tmp/flash$$ - -src=$1 -dst=$2 -for p in 20 40 60 80 100 -do - convert $src -fill white -colorize ${p}% $t.$p.gif -done -convert -delay 10 $src $t.20.gif $t.40.gif $t.60.gif $t.80.gif $t.100.gif $t.80.gif $t.60.gif $t.40.gif $t.20.gif -loop 0 $dst - -rm $t.*.gif diff --git a/war/images/nothing.svg b/war/images/nothing.svg deleted file mode 100644 index c9fca93c9e..0000000000 --- a/war/images/nothing.svg +++ /dev/null @@ -1,157 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - Delete - 2005-12-28 - - - Andreas Nilsson - - - http://tango-project.org - - - delete - remove - - - - - - - - - - - - - - - diff --git a/war/images/png2gifanime.sh b/war/images/png2gifanime.sh deleted file mode 100755 index 4db700a855..0000000000 --- a/war/images/png2gifanime.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash -e -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - - -# take multiple PNG files in the command line, and convert them to animation gif in the white background -# then send it to stdout -i=0 -tmpbase=/tmp/png2gifanime$$ - -for f in "$@" -do - convert $f \( +clone -fill white -draw 'color 0,0 reset' \) \ - -compose Dst_Over $tmpbase$i.gif - fileList[$i]=$tmpbase$i.gif - i=$((i+1)) -done - -convert -delay 10 ${fileList[@]} -loop 0 "${tmpbase}final.gif" -cat ${tmpbase}final.gif -rm ${fileList[@]} ${tmpbase}final.gif \ No newline at end of file diff --git a/war/images/readme.txt b/war/images/readme.txt deleted file mode 100644 index b009c973fd..0000000000 --- a/war/images/readme.txt +++ /dev/null @@ -1,2 +0,0 @@ -This is the workshop to tweak with images. -These images are generated into PNGs and then copied manually over to the resources/image directory diff --git a/war/images/red.svg b/war/images/red.svg deleted file mode 100644 index 6ac0c77b27..0000000000 --- a/war/images/red.svg +++ /dev/null @@ -1,265 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - Face - Plain - - - emoticon - emote - face - plain - :| - :-| - - - - - - Steven Garrity - - - http://www.tango-project.org - - - Based on face-smile by jimmac - - - - - - - - - - - - - - - - - - - diff --git a/war/images/scrollbar/make.rb b/war/images/scrollbar/make.rb deleted file mode 100755 index cc9d5dfe4a..0000000000 --- a/war/images/scrollbar/make.rb +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/ruby -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - - - -# width of the band -$u=20 -$uu=$u*2 - -def make(step,color) - system "convert -size #{$uu}x8 xc:white -fill '#{color}' -stroke '#{color}' -draw \"polyline 0,#{$u-1} #{$u-1},0 #{$uu-1},0 #{$u},#{$u-1}\" -roll +#{step}+0 screen.#{step}.gif" or fail -end - -colors = { "red" => "#cc0000", "blue" => "#3465a4" } -colors.keys.each do |name| - list = [] - color = colors[name]; - - (0...$uu).each do |i| - make(i,color) - list << "screen.#{i}.gif" - end - - system "convert -delay 10 #{list.join(" ")} -loop 0 progress-unknown-#{name}.gif" or fail - - list.each do |file| - File.delete(file) - end -end \ No newline at end of file diff --git a/war/images/scrollbar/test.html b/war/images/scrollbar/test.html deleted file mode 100644 index 7a71f76788..0000000000 --- a/war/images/scrollbar/test.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - -
    -   -
    \ No newline at end of file diff --git a/war/images/svg2png b/war/images/svg2png deleted file mode 100755 index 52bb6d7a41..0000000000 --- a/war/images/svg2png +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -e -# convert an svg file to a png file -# Usage: cat some.svg | svg2png ...options... > some.png -# -# option: -w width -h height - -tmpA=/tmp/svg2pngA$$ -tmpB=/tmp/svg2pngB$$ - -cat > $tmpA -inkscape -z -e "$tmpB" "$@" "$tmpA" 1>&2 -cat $tmpB -rm $tmpA $tmpB diff --git a/war/images/yellow.svg b/war/images/yellow.svg deleted file mode 100644 index de06f91888..0000000000 --- a/war/images/yellow.svg +++ /dev/null @@ -1,265 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - Face - Plain - - - emoticon - emote - face - plain - :| - :-| - - - - - - Steven Garrity - - - http://www.tango-project.org - - - Based on face-smile by jimmac - - - - - - - - - - - - - - - - - - - diff --git a/war/src/main/less/base/layout-commons.less b/war/src/main/less/base/layout-commons.less index 8d4b558962..b3ddcc1dd9 100644 --- a/war/src/main/less/base/layout-commons.less +++ b/war/src/main/less/base/layout-commons.less @@ -24,7 +24,7 @@ body { /* --------------- header --------------- */ #header .logo { - margin-left: 16px; + margin-left: 1.2rem; display: inline-flex; align-items: center; position: relative; diff --git a/war/images/accept.svg b/war/src/main/webapp/images/svgs/accept.svg similarity index 100% rename from war/images/accept.svg rename to war/src/main/webapp/images/svgs/accept.svg diff --git a/war/images/application-certificate.svg b/war/src/main/webapp/images/svgs/application-certificate.svg similarity index 100% rename from war/images/application-certificate.svg rename to war/src/main/webapp/images/svgs/application-certificate.svg diff --git a/war/images/attribute.svg b/war/src/main/webapp/images/svgs/attribute.svg similarity index 100% rename from war/images/attribute.svg rename to war/src/main/webapp/images/svgs/attribute.svg diff --git a/war/images/bookmark-new.svg b/war/src/main/webapp/images/svgs/bookmark-new.svg similarity index 100% rename from war/images/bookmark-new.svg rename to war/src/main/webapp/images/svgs/bookmark-new.svg diff --git a/war/images/certificate.svg b/war/src/main/webapp/images/svgs/certificate.svg similarity index 100% rename from war/images/certificate.svg rename to war/src/main/webapp/images/svgs/certificate.svg diff --git a/war/images/clipboard.svg b/war/src/main/webapp/images/svgs/clipboard.svg similarity index 100% rename from war/images/clipboard.svg rename to war/src/main/webapp/images/svgs/clipboard.svg diff --git a/war/images/clock.svg b/war/src/main/webapp/images/svgs/clock.svg similarity index 100% rename from war/images/clock.svg rename to war/src/main/webapp/images/svgs/clock.svg diff --git a/war/images/computer-user-offline.svg b/war/src/main/webapp/images/svgs/computer-user-offline.svg similarity index 100% rename from war/images/computer-user-offline.svg rename to war/src/main/webapp/images/svgs/computer-user-offline.svg diff --git a/war/images/computer-x.svg b/war/src/main/webapp/images/svgs/computer-x.svg similarity index 100% rename from war/images/computer-x.svg rename to war/src/main/webapp/images/svgs/computer-x.svg diff --git a/war/images/computer.svg b/war/src/main/webapp/images/svgs/computer.svg similarity index 100% rename from war/images/computer.svg rename to war/src/main/webapp/images/svgs/computer.svg diff --git a/war/images/delete-document.svg b/war/src/main/webapp/images/svgs/delete-document.svg similarity index 100% rename from war/images/delete-document.svg rename to war/src/main/webapp/images/svgs/delete-document.svg diff --git a/war/images/document.svg b/war/src/main/webapp/images/svgs/document.svg similarity index 100% rename from war/images/document.svg rename to war/src/main/webapp/images/svgs/document.svg diff --git a/war/images/edit-delete.svg b/war/src/main/webapp/images/svgs/edit-delete.svg similarity index 100% rename from war/images/edit-delete.svg rename to war/src/main/webapp/images/svgs/edit-delete.svg diff --git a/war/images/emblem-urgent.svg b/war/src/main/webapp/images/svgs/emblem-urgent.svg similarity index 100% rename from war/images/emblem-urgent.svg rename to war/src/main/webapp/images/svgs/emblem-urgent.svg diff --git a/war/images/error.svg b/war/src/main/webapp/images/svgs/error.svg similarity index 100% rename from war/images/error.svg rename to war/src/main/webapp/images/svgs/error.svg diff --git a/war/images/fingerprint.svg b/war/src/main/webapp/images/svgs/fingerprint.svg similarity index 100% rename from war/images/fingerprint.svg rename to war/src/main/webapp/images/svgs/fingerprint.svg diff --git a/war/images/folder-delete.svg b/war/src/main/webapp/images/svgs/folder-delete.svg similarity index 100% rename from war/images/folder-delete.svg rename to war/src/main/webapp/images/svgs/folder-delete.svg diff --git a/war/images/folder.svg b/war/src/main/webapp/images/svgs/folder.svg similarity index 100% rename from war/images/folder.svg rename to war/src/main/webapp/images/svgs/folder.svg diff --git a/war/images/gear2.svg b/war/src/main/webapp/images/svgs/gear.svg similarity index 100% rename from war/images/gear2.svg rename to war/src/main/webapp/images/svgs/gear.svg diff --git a/war/images/go-down.svg b/war/src/main/webapp/images/svgs/go-down.svg similarity index 100% rename from war/images/go-down.svg rename to war/src/main/webapp/images/svgs/go-down.svg diff --git a/war/images/go-top.svg b/war/src/main/webapp/images/svgs/go-top.svg similarity index 100% rename from war/images/go-top.svg rename to war/src/main/webapp/images/svgs/go-top.svg diff --git a/war/images/go-up.svg b/war/src/main/webapp/images/svgs/go-up.svg similarity index 100% rename from war/images/go-up.svg rename to war/src/main/webapp/images/svgs/go-up.svg diff --git a/war/images/graph.svg b/war/src/main/webapp/images/svgs/graph.svg similarity index 100% rename from war/images/graph.svg rename to war/src/main/webapp/images/svgs/graph.svg diff --git a/war/images/grey.svg b/war/src/main/webapp/images/svgs/grey.svg similarity index 100% rename from war/images/grey.svg rename to war/src/main/webapp/images/svgs/grey.svg diff --git a/war/images/headless.svg b/war/src/main/webapp/images/svgs/headless.svg similarity index 100% rename from war/images/headless.svg rename to war/src/main/webapp/images/svgs/headless.svg diff --git a/war/images/headshot.svg b/war/src/main/webapp/images/svgs/headshot.svg similarity index 100% rename from war/images/headshot.svg rename to war/src/main/webapp/images/svgs/headshot.svg diff --git a/war/images/help.svg b/war/src/main/webapp/images/svgs/help.svg similarity index 100% rename from war/images/help.svg rename to war/src/main/webapp/images/svgs/help.svg diff --git a/war/images/hourglass.svg b/war/src/main/webapp/images/svgs/hourglass.svg similarity index 100% rename from war/images/hourglass.svg rename to war/src/main/webapp/images/svgs/hourglass.svg diff --git a/war/images/installer.svg b/war/src/main/webapp/images/svgs/installer.svg similarity index 100% rename from war/images/installer.svg rename to war/src/main/webapp/images/svgs/installer.svg diff --git a/war/images/keys.svg b/war/src/main/webapp/images/svgs/keys.svg similarity index 100% rename from war/images/keys.svg rename to war/src/main/webapp/images/svgs/keys.svg diff --git a/war/images/lock.svg b/war/src/main/webapp/images/svgs/lock.svg similarity index 100% rename from war/images/lock.svg rename to war/src/main/webapp/images/svgs/lock.svg diff --git a/war/images/logo.svg b/war/src/main/webapp/images/svgs/logo.svg similarity index 100% rename from war/images/logo.svg rename to war/src/main/webapp/images/svgs/logo.svg diff --git a/war/images/monitor.svg b/war/src/main/webapp/images/svgs/monitor.svg similarity index 100% rename from war/images/monitor.svg rename to war/src/main/webapp/images/svgs/monitor.svg diff --git a/war/images/network.svg b/war/src/main/webapp/images/svgs/network.svg similarity index 100% rename from war/images/network.svg rename to war/src/main/webapp/images/svgs/network.svg diff --git a/war/images/new-computer.svg b/war/src/main/webapp/images/svgs/new-computer.svg similarity index 100% rename from war/images/new-computer.svg rename to war/src/main/webapp/images/svgs/new-computer.svg diff --git a/war/images/new-document.svg b/war/src/main/webapp/images/svgs/new-document.svg similarity index 100% rename from war/images/new-document.svg rename to war/src/main/webapp/images/svgs/new-document.svg diff --git a/war/images/new-package.svg b/war/src/main/webapp/images/svgs/new-package.svg similarity index 100% rename from war/images/new-package.svg rename to war/src/main/webapp/images/svgs/new-package.svg diff --git a/war/images/new-user.svg b/war/src/main/webapp/images/svgs/new-user.svg similarity index 100% rename from war/images/new-user.svg rename to war/src/main/webapp/images/svgs/new-user.svg diff --git a/war/images/next.svg b/war/src/main/webapp/images/svgs/next.svg similarity index 100% rename from war/images/next.svg rename to war/src/main/webapp/images/svgs/next.svg diff --git a/war/images/notepad.svg b/war/src/main/webapp/images/svgs/notepad.svg similarity index 100% rename from war/images/notepad.svg rename to war/src/main/webapp/images/svgs/notepad.svg diff --git a/war/images/orange-square.svg b/war/src/main/webapp/images/svgs/orange-square.svg similarity index 100% rename from war/images/orange-square.svg rename to war/src/main/webapp/images/svgs/orange-square.svg diff --git a/war/images/package.svg b/war/src/main/webapp/images/svgs/package.svg similarity index 100% rename from war/images/package.svg rename to war/src/main/webapp/images/svgs/package.svg diff --git a/war/images/person.svg b/war/src/main/webapp/images/svgs/person.svg similarity index 100% rename from war/images/person.svg rename to war/src/main/webapp/images/svgs/person.svg diff --git a/war/images/plugin.svg b/war/src/main/webapp/images/svgs/plugin.svg similarity index 100% rename from war/images/plugin.svg rename to war/src/main/webapp/images/svgs/plugin.svg diff --git a/war/images/previous.svg b/war/src/main/webapp/images/svgs/previous.svg similarity index 100% rename from war/images/previous.svg rename to war/src/main/webapp/images/svgs/previous.svg diff --git a/war/images/redo.svg b/war/src/main/webapp/images/svgs/redo.svg similarity index 100% rename from war/images/redo.svg rename to war/src/main/webapp/images/svgs/redo.svg diff --git a/war/images/refresh.svg b/war/src/main/webapp/images/svgs/refresh.svg similarity index 100% rename from war/images/refresh.svg rename to war/src/main/webapp/images/svgs/refresh.svg diff --git a/war/images/save-new.svg b/war/src/main/webapp/images/svgs/save-new.svg similarity index 100% rename from war/images/save-new.svg rename to war/src/main/webapp/images/svgs/save-new.svg diff --git a/war/images/save.svg b/war/src/main/webapp/images/svgs/save.svg similarity index 100% rename from war/images/save.svg rename to war/src/main/webapp/images/svgs/save.svg diff --git a/war/images/search.svg b/war/src/main/webapp/images/svgs/search.svg similarity index 100% rename from war/images/search.svg rename to war/src/main/webapp/images/svgs/search.svg diff --git a/war/images/secure.svg b/war/src/main/webapp/images/svgs/secure.svg similarity index 100% rename from war/images/secure.svg rename to war/src/main/webapp/images/svgs/secure.svg diff --git a/war/images/setting.svg b/war/src/main/webapp/images/svgs/setting.svg similarity index 100% rename from war/images/setting.svg rename to war/src/main/webapp/images/svgs/setting.svg diff --git a/war/images/shield.svg b/war/src/main/webapp/images/svgs/shield.svg similarity index 100% rename from war/images/shield.svg rename to war/src/main/webapp/images/svgs/shield.svg diff --git a/war/images/star-gold.svg b/war/src/main/webapp/images/svgs/star-gold.svg similarity index 100% rename from war/images/star-gold.svg rename to war/src/main/webapp/images/svgs/star-gold.svg diff --git a/war/images/star-large-gold.svg b/war/src/main/webapp/images/svgs/star-large-gold.svg similarity index 100% rename from war/images/star-large-gold.svg rename to war/src/main/webapp/images/svgs/star-large-gold.svg diff --git a/war/images/star-large.svg b/war/src/main/webapp/images/svgs/star-large.svg similarity index 100% rename from war/images/star-large.svg rename to war/src/main/webapp/images/svgs/star-large.svg diff --git a/war/images/star.svg b/war/src/main/webapp/images/svgs/star.svg similarity index 100% rename from war/images/star.svg rename to war/src/main/webapp/images/svgs/star.svg diff --git a/war/images/stop.svg b/war/src/main/webapp/images/svgs/stop.svg similarity index 100% rename from war/images/stop.svg rename to war/src/main/webapp/images/svgs/stop.svg diff --git a/war/images/system-log-out.svg b/war/src/main/webapp/images/svgs/system-log-out.svg similarity index 100% rename from war/images/system-log-out.svg rename to war/src/main/webapp/images/svgs/system-log-out.svg diff --git a/war/images/terminal.svg b/war/src/main/webapp/images/svgs/terminal.svg similarity index 100% rename from war/images/terminal.svg rename to war/src/main/webapp/images/svgs/terminal.svg diff --git a/war/images/undo.svg b/war/src/main/webapp/images/svgs/undo.svg similarity index 100% rename from war/images/undo.svg rename to war/src/main/webapp/images/svgs/undo.svg diff --git a/war/images/up.svg b/war/src/main/webapp/images/svgs/up.svg similarity index 100% rename from war/images/up.svg rename to war/src/main/webapp/images/svgs/up.svg diff --git a/war/images/user.svg b/war/src/main/webapp/images/svgs/user.svg similarity index 100% rename from war/images/user.svg rename to war/src/main/webapp/images/svgs/user.svg diff --git a/war/images/video.svg b/war/src/main/webapp/images/svgs/video.svg similarity index 100% rename from war/images/video.svg rename to war/src/main/webapp/images/svgs/video.svg diff --git a/war/images/warning.svg b/war/src/main/webapp/images/svgs/warning.svg similarity index 100% rename from war/images/warning.svg rename to war/src/main/webapp/images/svgs/warning.svg From 471f7c38c8835881370b70a06879863d020afecf Mon Sep 17 00:00:00 2001 From: Brady Shober <32466298+BradyShober@users.noreply.github.com> Date: Tue, 24 Aug 2021 01:59:54 -0500 Subject: [PATCH 90/98] Encode special characters for generated shell command (#5636) Co-authored-by: Wadeck Follonier --- core/src/main/java/hudson/Util.java | 33 ++++++++++++++++++- core/src/main/java/hudson/model/Computer.java | 2 +- core/src/test/java/hudson/UtilTest.java | 20 +++++++++++ 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/hudson/Util.java b/core/src/main/java/hudson/Util.java index a734a6ae8c..8a7269bfb1 100644 --- a/core/src/main/java/hudson/Util.java +++ b/core/src/main/java/hudson/Util.java @@ -890,6 +890,20 @@ public class Util { // queryMap[38] = queryMap[43] = queryMap[61] = true; } + private static final boolean[] fullUriMap = new boolean[123]; + static { + String raw = " 0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz"; + // !"#$%&'()*+,-./0123456789:;<=>?@ [\]^_` {|}~ + // ^--so these are encoded + int i; + // Encode control chars and space + for (i = 0; i < 33; i++) fullUriMap[i] = true; + for (int j = 0; j < raw.length(); i++, j++) + fullUriMap[i] = raw.charAt(j) == ' '; + // If we add encodeQuery() just add a 2nd map to encode &+= + // queryMap[38] = queryMap[43] = queryMap[61] = true; + } + /** * Encode a single path component for use in an HTTP URL. * Escapes all non-ASCII, general unsafe (space and {@code "#%<>[\]^`{|}~}) @@ -901,6 +915,23 @@ public class Util { */ @NonNull public static String rawEncode(@NonNull String s) { + return encode(s, uriMap); + } + + /** + * Encode a single path component for use in an HTTP URL. + * Escapes all special characters including those outside + * of the characters specified in RFC1738. + * All characters outside numbers and letters without diacritic are encoded. + * Note that slash ({@code /}) is encoded, so the given string should be a + * single path component used in constructing a URL. + */ + @NonNull + public static String fullEncode(@NonNull String s){ + return encode(s, fullUriMap); + } + + private static String encode(String s, boolean[] map){ boolean escaped = false; StringBuilder out = null; CharsetEncoder enc = null; @@ -910,7 +941,7 @@ public class Util { int codePoint = Character.codePointAt(s, i); if((codePoint&0xffffff80)==0) { // 1 byte c = s.charAt(i); - if (c > 122 || uriMap[c]) { + if (c > 122 || map[c]) { if (!escaped) { out = new StringBuilder(i + (m - i) * 3); out.append(s, 0, i); diff --git a/core/src/main/java/hudson/model/Computer.java b/core/src/main/java/hudson/model/Computer.java index e90b18b354..232d0c4c86 100644 --- a/core/src/main/java/hudson/model/Computer.java +++ b/core/src/main/java/hudson/model/Computer.java @@ -785,7 +785,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces } public String getUrl() { - return "computer/" + Util.rawEncode(getName()) + "/"; + return "computer/" + Util.fullEncode(getName()) + "/"; } @Exported diff --git a/core/src/test/java/hudson/UtilTest.java b/core/src/test/java/hudson/UtilTest.java index 9ab683838c..e01f735275 100644 --- a/core/src/test/java/hudson/UtilTest.java +++ b/core/src/test/java/hudson/UtilTest.java @@ -175,6 +175,26 @@ public class UtilTest { } } + /** + * Test the fullEncode() method. + */ + @Test + public void testFullEncode(){ + String[] data = { + "abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", + "01234567890!@$&*()-_=+',.", "01234567890%21%40%24%26%2A%28%29%2D%5F%3D%2B%27%2C%2E", + " \"#%/:;<>?", "%20%22%23%25%2F%3A%3B%3C%3E%3F", + "[\\]^`{|}~", "%5B%5C%5D%5E%60%7B%7C%7D%7E", + "d\u00E9velopp\u00E9s", "d%C3%A9velopp%C3%A9s", + "Foo \uD800\uDF98 Foo", "Foo%20%F0%90%8E%98%20Foo", + "\u00E9 ", "%C3%A9%20" + }; + for (int i = 0; i < data.length; i += 2) { + assertEquals("test " + i, data[i + 1], Util.fullEncode(data[i])); + } + } + /** * Test the tryParseNumber() method. */ From c9eaf345d845ec8b433120479a718a7f33e5850d Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Tue, 24 Aug 2021 00:00:15 -0700 Subject: [PATCH 91/98] Remove Java 11 `ClassNotFoundException` telemetry (#5666) --- core/src/main/java/hudson/PluginManager.java | 5 +- .../impl/InstallUncaughtExceptionHandler.java | 10 - core/src/main/java/jenkins/model/Jenkins.java | 27 +- .../impl/java11/CatcherClassLoader.java | 51 --- .../impl/java11/MissingClassEvent.java | 92 ----- .../impl/java11/MissingClassEvents.java | 134 ------ .../impl/java11/MissingClassTelemetry.java | 382 ----------------- .../java/jenkins/util/AntClassLoader.java | 50 +-- .../telemetry/MissingClassTelemetryTest.java | 151 ------- .../MissingClassTelemetryFasterTest.java | 388 ------------------ 10 files changed, 23 insertions(+), 1267 deletions(-) delete mode 100644 core/src/main/java/jenkins/telemetry/impl/java11/CatcherClassLoader.java delete mode 100644 core/src/main/java/jenkins/telemetry/impl/java11/MissingClassEvent.java delete mode 100644 core/src/main/java/jenkins/telemetry/impl/java11/MissingClassEvents.java delete mode 100644 core/src/main/java/jenkins/telemetry/impl/java11/MissingClassTelemetry.java delete mode 100644 test/src/test/java/jenkins/telemetry/MissingClassTelemetryTest.java delete mode 100644 test/src/test/java/jenkins/telemetry/impl/java11/MissingClassTelemetryFasterTest.java diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java index 5a5b613965..dd246de8d9 100644 --- a/core/src/main/java/hudson/PluginManager.java +++ b/core/src/main/java/hudson/PluginManager.java @@ -63,7 +63,6 @@ import jenkins.install.InstallUtil; import jenkins.model.Jenkins; import jenkins.plugins.DetachedPluginsUtil; import jenkins.security.CustomClassFilter; -import jenkins.telemetry.impl.java11.MissingClassTelemetry; import jenkins.util.SystemProperties; import jenkins.util.io.OnMaster; import jenkins.util.xml.RestrictiveEntityResolver; @@ -2172,9 +2171,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas loaded.put(name, null); } // not found in any of the classloader. delegate. - ClassNotFoundException cnfe = new ClassNotFoundException(name); - MissingClassTelemetry.reportException(name, cnfe); - throw cnfe; + throw new ClassNotFoundException(name); } @Override diff --git a/core/src/main/java/hudson/init/impl/InstallUncaughtExceptionHandler.java b/core/src/main/java/hudson/init/impl/InstallUncaughtExceptionHandler.java index 4241d5a5e8..36fb58addb 100644 --- a/core/src/main/java/hudson/init/impl/InstallUncaughtExceptionHandler.java +++ b/core/src/main/java/hudson/init/impl/InstallUncaughtExceptionHandler.java @@ -2,7 +2,6 @@ package hudson.init.impl; import hudson.init.Initializer; import jenkins.model.Jenkins; -import jenkins.telemetry.impl.java11.MissingClassTelemetry; import org.kohsuke.MetaInfServices; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -58,10 +57,6 @@ public class InstallUncaughtExceptionHandler { req.setAttribute("javax.servlet.error.exception",e); rsp.setStatus(code); try { - // If we have an exception, let's see if it's related with missing classes on Java 11. We reach - // here with a ClassNotFoundException in an action, for example. Setting the report here is the only - // way to catch the missing classes when the plugin uses Thread.currentThread().getContextClassLoader().loadClass - MissingClassTelemetry.reportExceptionInside(e); WebApp.get(j.servletContext).getSomeStapler().invoke(req, rsp, j, "/oops"); } catch (ServletException | IOException x) { if (!Stapler.isSocketException(x)) { @@ -108,11 +103,6 @@ public class InstallUncaughtExceptionHandler { "A thread (" + t.getName() + '/' + t.getId() + ") died unexpectedly due to an uncaught exception, this may leave your Jenkins in a bad way and is usually indicative of a bug in the code.", ex); - - // If we have an exception, let's see if it's related with missing classes on Java 11. We reach - // here with a ClassNotFoundException in an action, for example. Setting the report here is the only - // way to catch the missing classes when the plugin uses Thread.currentThread().getContextClassLoader().loadClass - MissingClassTelemetry.reportExceptionInside(ex); } } diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index 3c43012c63..1be52bf6a1 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -63,8 +63,6 @@ import jenkins.security.stapler.StaplerFilteredActionListener; import jenkins.security.stapler.StaplerDispatchable; import jenkins.security.RedactSecretJsonInErrorMessageSanitizer; import jenkins.security.stapler.TypedFilter; -import jenkins.telemetry.impl.java11.CatcherClassLoader; -import jenkins.telemetry.impl.java11.MissingClassTelemetry; import jenkins.util.SystemProperties; import hudson.cli.declarative.CLIMethod; import hudson.cli.declarative.CLIResolver; @@ -915,12 +913,6 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve } // doing this early allows InitStrategy to set environment upfront - //Telemetry: add interceptor classloader - //These lines allow the catcher to be present on Thread.currentThread().getContextClassLoader() in every plugin which - //allow us to detect failures in every plugin loading classes by this way. - if (MissingClassTelemetry.enabled() && !(Thread.currentThread().getContextClassLoader() instanceof CatcherClassLoader)) { - Thread.currentThread().setContextClassLoader(new CatcherClassLoader(Thread.currentThread().getContextClassLoader())); - } final InitStrategy is = InitStrategy.get(Thread.currentThread().getContextClassLoader()); Trigger.timer = new java.util.Timer("Jenkins cron thread"); @@ -961,20 +953,8 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve pluginManager = PluginManager.createDefault(this); this.pluginManager = pluginManager; WebApp webApp = WebApp.get(servletContext); - - //Telemetry: add interceptor classloader - //These lines allows the catcher to be present on Thread.currentThread().getContextClassLoader() in every plugin which - //allow us to detect failures in every plugin loading classes by this way. // JSON binding needs to be able to see all the classes from all the plugins - - ClassLoader classLoaderToAssign; - if (MissingClassTelemetry.enabled() && !(pluginManager.uberClassLoader instanceof CatcherClassLoader)) { - classLoaderToAssign = new CatcherClassLoader(pluginManager.uberClassLoader); - } else { - classLoaderToAssign = pluginManager.uberClassLoader; - } - webApp.setClassLoader(classLoaderToAssign); - + webApp.setClassLoader(pluginManager.uberClassLoader); webApp.setJsonInErrorMessageSanitizer(RedactSecretJsonInErrorMessageSanitizer.INSTANCE); TypedFilter typedFilter = new TypedFilter(); @@ -990,10 +970,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve webApp.setDispatchValidator(new StaplerDispatchValidator()); webApp.setFilteredDispatchTriggerListener(actionListener); - //Telemetry: add interceptor classloader - //These lines allows the catcher to be present on Thread.currentThread().getContextClassLoader() in every plugin which - //allow us to detect failures in every plugin loading classes at this way. - adjuncts = new AdjunctManager(servletContext, classLoaderToAssign, "adjuncts/" + SESSION_HASH, TimeUnit.DAYS.toMillis(365)); + adjuncts = new AdjunctManager(servletContext, pluginManager.uberClassLoader, "adjuncts/" + SESSION_HASH, TimeUnit.DAYS.toMillis(365)); ClassFilterImpl.register(); diff --git a/core/src/main/java/jenkins/telemetry/impl/java11/CatcherClassLoader.java b/core/src/main/java/jenkins/telemetry/impl/java11/CatcherClassLoader.java deleted file mode 100644 index 8a1c962f79..0000000000 --- a/core/src/main/java/jenkins/telemetry/impl/java11/CatcherClassLoader.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2019 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. - */ - -package jenkins.telemetry.impl.java11; - -import org.kohsuke.accmod.Restricted; -import org.kohsuke.accmod.restrictions.NoExternalUse; - -@Restricted(NoExternalUse.class) -public class CatcherClassLoader extends ClassLoader { - - public CatcherClassLoader(ClassLoader parent) { - super(parent); - } - - - /** - * Usually, the {@link ClassLoader} calls its parent and finally this method. So if we are here, it's the last - * element of the chain. It doesn't happen in {@link jenkins.util.AntClassLoader} so it has an special management - * on {@link hudson.ClassicPluginStrategy} - * - * - */ - @Override - protected Class findClass(String name) throws ClassNotFoundException { - ClassNotFoundException e = new ClassNotFoundException(name); - MissingClassTelemetry.reportException(name, e); - throw e; - } -} diff --git a/core/src/main/java/jenkins/telemetry/impl/java11/MissingClassEvent.java b/core/src/main/java/jenkins/telemetry/impl/java11/MissingClassEvent.java deleted file mode 100644 index ab13224f97..0000000000 --- a/core/src/main/java/jenkins/telemetry/impl/java11/MissingClassEvent.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2019 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. - */ - -package jenkins.telemetry.impl.java11; - -import edu.umd.cs.findbugs.annotations.NonNull; -import java.io.PrintWriter; -import java.io.StringWriter; - -/** - * Store an event regarding missing classes. We can already catch ClassNotFoundException and NoClassDefFoundError - */ -class MissingClassEvent { - private String time; - private long occurrences; - private String stackTrace; - private String className; - - String getStackTrace() { - return stackTrace; - } - - void setStackTrace(String stackTrace) { - this.stackTrace = stackTrace; - } - - String getClassName() { - return className; - } - - void setClassName(String className) { - this.className = className; - } - - MissingClassEvent(@NonNull String name, @NonNull Throwable t) { - this.className = name; - - StringWriter stackTrace = new StringWriter(); - t.printStackTrace(new PrintWriter(stackTrace)); - this.stackTrace = stackTrace.toString(); - - this.time = MissingClassTelemetry.clientDateString(); - this.occurrences = 1; - } - - String getTime() { - return time; - } - - long getOccurrences() { - return occurrences; - } - - void setOccurrences(long occurrences) { - this.occurrences = occurrences; - } - - void setTime(String time) { - this.time = time; - } - - @Override - public String toString() { - return "MissingClassEvent{" + - "time='" + time + '\'' + - ", occurrences=" + occurrences + - ", stackTrace='" + stackTrace + '\'' + - ", className='" + className + '\'' + - '}'; - } -} diff --git a/core/src/main/java/jenkins/telemetry/impl/java11/MissingClassEvents.java b/core/src/main/java/jenkins/telemetry/impl/java11/MissingClassEvents.java deleted file mode 100644 index 59d7ba6e47..0000000000 --- a/core/src/main/java/jenkins/telemetry/impl/java11/MissingClassEvents.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2019 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. - */ - -package jenkins.telemetry.impl.java11; - -import com.google.common.annotations.VisibleForTesting; - -import edu.umd.cs.findbugs.annotations.NonNull; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicLong; - -public class MissingClassEvents { - - /** - * Only 100 exceptions a day (period of telemetry) - */ - @VisibleForTesting - /* package */ static /* final */ int MAX_EVENTS_PER_SEND = 100; - - /** - * List of events, one per stack trace, to send to Telemetry. Only the first {@code #MAX_EVENTS_PER_SEND} are sent. - */ - private ConcurrentHashMap, MissingClassEvent> events = new ConcurrentHashMap<>(MAX_EVENTS_PER_SEND); - - /** - * List of all events registered on this execution, to avoid printing an event more than once in the log. This map - * is not limited. On every Jenkins restart is cleaned because it's not persisted, so the CNFE is printed again. The - * key is the class name not found to quickly look for it on every CNFE thrown. - */ - private ConcurrentHashMap eventsOnThisExecution = new ConcurrentHashMap<>(MAX_EVENTS_PER_SEND); - /** - * Add a new exception to the store. If the same exception already exists, it increases the occurrences. If we - * already get the maximum number of exceptions, it doesn't add any value. - * @param name name of the class not found - * @param t the exception to store - * @return the occurrences stored for this throwable. 1 the fist time it's stored. > 1 for successive stores of the - * same stack trace. 0 if we already stored MAX_EVENTS_PER_SEND (100) events for a single send. - */ - public long put(String name, @NonNull Throwable t) { - // A final object to pass it to the function - final AtomicLong occurrences = new AtomicLong(); - - // We need the key (the stack trace) to be a list and unmodifiable - List key = Collections.unmodifiableList(Arrays.asList(t.getStackTrace())); - - final MissingClassEvent newEvent = new MissingClassEvent(name, t); - events.compute(key, (stackTraceElements, missingClassEvent) -> { - - if (missingClassEvent == null) { - // It's a new element, the size will increase - if (events.size() < MAX_EVENTS_PER_SEND) { - // Create the new value - occurrences.set(1); - return newEvent; - } else { - return null; - } - } else { - // We update the occurrences and the last time it happened - occurrences.set(missingClassEvent.getOccurrences()); - missingClassEvent.setOccurrences(occurrences.incrementAndGet()); - missingClassEvent.setTime(MissingClassTelemetry.clientDateString()); - return missingClassEvent; - } - }); - - // We add the event to the list of already printed events. We used the name of the missing class instead of the - // full stack trace as a key to avoid filling the log with CNFE talking about the same class even though the - // stack traces are different. Worse scenario, if we don't put the exception on the IGNORED_PLACES correctly, - // the administrator will see the message again and we will be able to add the new one to the IGNORED_PLACES. - // In addition, the event is also sent to telemetry. - eventsOnThisExecution.putIfAbsent(name, newEvent); - return occurrences.get(); - } - - /** - * Reinitialize the events happened and return the number of events stored since last execution of this method. - * Used to send via telemetry the events and restart the events store. - * @return the number of events stored since previous call to this method. - */ - - @VisibleForTesting - /* package */ synchronized @NonNull ConcurrentHashMap, MissingClassEvent> getEventsAndClean() { - ConcurrentHashMap, MissingClassEvent> currentEvents = events; - events = new ConcurrentHashMap<>(MAX_EVENTS_PER_SEND); - return currentEvents; - } - - /** - * Returns true if the class name was already registered previously, during the current execution of this Jenkins - * instance. - * @param className the class name to check - * @return true if it was already registered - */ - public boolean alreadyRegistered(@NonNull String className) { - return eventsOnThisExecution.containsKey(className); - } - - // To clean on every test execution - void clearEventsOnThisExecution() { - eventsOnThisExecution.clear(); - } - - @Override - public String toString() { - return "MissingClassEvents{" + - "events=" + events + - '}'; - } -} diff --git a/core/src/main/java/jenkins/telemetry/impl/java11/MissingClassTelemetry.java b/core/src/main/java/jenkins/telemetry/impl/java11/MissingClassTelemetry.java deleted file mode 100644 index 4b66804793..0000000000 --- a/core/src/main/java/jenkins/telemetry/impl/java11/MissingClassTelemetry.java +++ /dev/null @@ -1,382 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2019 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. - */ - -package jenkins.telemetry.impl.java11; - -import com.google.common.annotations.VisibleForTesting; -import hudson.Extension; -import hudson.util.VersionNumber; -import io.jenkins.lib.versionnumber.JavaSpecificationVersion; -import jenkins.model.Jenkins; -import jenkins.telemetry.Telemetry; -import jenkins.util.java.JavaUtils; -import net.sf.json.JSONArray; -import net.sf.json.JSONObject; -import org.kohsuke.accmod.Restricted; -import org.kohsuke.accmod.restrictions.NoExternalUse; - -import edu.umd.cs.findbugs.annotations.CheckForNull; -import edu.umd.cs.findbugs.annotations.NonNull; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.time.LocalDate; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.HashSet; -import java.util.IdentityHashMap; -import java.util.List; -import java.util.Set; -import java.util.TimeZone; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Telemetry class to gather information about missing classes when running on java 11. This class sends classes not - * found and in packages related with Java changes from Java 8 to Java 11. See {@link #MOVED_PACKAGES}. - **/ - -@Extension -@Restricted(NoExternalUse.class) -public class MissingClassTelemetry extends Telemetry { - private static final Logger LOGGER = Logger.getLogger(MissingClassTelemetry.class.getName()); - - // Store 100 events today - private static MissingClassEvents events = new MissingClassEvents(); - - // When we begin to gather these data - private static final LocalDate START = LocalDate.of(2019, 4, 1); - // Gather for 2 years (who knows how long people will need to migrate to Java 11) - private static final LocalDate END = START.plusMonths(24); - - // The types of exceptions which can be reported - private static final Set reportableExceptions = - new HashSet(Arrays.asList(ClassNotFoundException.class, NoClassDefFoundError.class)); - - @VisibleForTesting - /* package */ static final String CIRCULAR_REFERENCE = "Circular reference found on the exception we are analysing to report via telemetry"; - - /** - * Packages removed from java8 up to java11 - * https://blog.codefx.org/java/java-11-migration-guide/ - */ - private static final String[] MOVED_PACKAGES = new String[] {"javax.activation", "javax.annotation", "javax.jws", - "javax.rmi", "javax.transaction", "javax.xml.bind", "javax.xml.soap", "javax.xml.ws", "org.omg", - "javax.activity", "com.sun", "sun"}; - - /** - * Places where a ClassNotFoundException is going to be thrown but it's ignored later in the code, so we - * don't have to send this exception, even though it might be related with java classes of moved packages - */ - private static String[][] IGNORED_PLACES = { - {"hudson.util.XStream2$AssociatedConverterImpl", "findConverter"}, - {"org.jenkinsci.plugins.workflow.cps.global.GrapeHack", "hack"}, - {"org.codehaus.groovy.runtime.callsite.CallSiteArray", "createCallStaticSite"}, - {"groovy.lang.MetaClassImpl", "addProperties"}, - // We set the reportException call directly in this method when it's appropriated - {"hudson.PluginManager.UberClassLoader", "findClass"}, - {"hudson.ExtensionFinder$GuiceFinder$FaultTolerantScope$1", "get"}, - {"hudson.ExtensionFinder$GuiceFinder$SezpozModule", "resolve"}, - {"java.beans.Introspector", "findCustomizerClass"}, - {"com.sun.beans.finder.InstanceFinder", "instantiate"}, - {"com.sun.beans.finder.ClassFinder", "findClass"}, - {"java.util.ResourceBundle$Control", "newBundle"}, - //hundreds when a job is created - {"org.codehaus.groovy.control.ClassNodeResolver", "tryAsLoaderClassOrScript"}, - {"org.kohsuke.stapler.RequestImpl$TypePair", "convertJSON"}, - {"net.bull.javamelody.FilterContext", "isMojarraAvailable"}, // JENKINS-60725 - {"hudson.remoting.RemoteClassLoader$ClassLoaderProxy", "fetch3"}, // JENKINS-61521 - //Don't add "java.base/" before sun.reflect.generics.factory.CoreReflectionFactory - {"sun.reflect.generics.factory.CoreReflectionFactory", "makeNamedType"}, // JENKINS-61920 - - }; - - @NonNull - @Override - public String getDisplayName() { - return "Missing classes related with Java updates"; - } - - @NonNull - @Override - public LocalDate getStart() { - return START; - } - - @NonNull - @Override - public LocalDate getEnd() { - return END; - } - - /** - * To allow asserting this info in tests. - * @return the events gathered. - */ - @VisibleForTesting - /* package */ static MissingClassEvents getEvents() { - return events; - } - - /** - * This telemetry is only enabled when running on Java versions newer than Java 8. - * @return true if running on a newer Java version than Java 8 - */ - public static boolean enabled() { - return JavaUtils.getCurrentJavaRuntimeVersionNumber().isNewerThan(JavaSpecificationVersion.JAVA_8); - } - - @CheckForNull - @Override - public JSONObject createContent() { - // If we are on the time window of this telemetry (checked by the Telemetry class) but we are not running on - // Java > 1.8 (checked here), we don't send anything - if (!enabled()) { - return null; - } - - // To avoid sending empty events - JSONArray events = formatEventsAndInitialize(); - if (events.size() == 0) { - return null; - } - - JSONObject info = new JSONObject(); - VersionNumber jenkinsVersion = Jenkins.getVersion(); - info.put("core", jenkinsVersion != null ? jenkinsVersion.toString() : "UNKNOWN"); - info.put("clientDate", clientDateString()); - info.put("classMissingEvents", events); - - return JSONObject.fromObject(info); - } - - /** - * Returns the events gathered as a Map ready to use in a Json object to send via telemetry and clean the map to - * gather another window of events. - * @return the map of missed classes events gathered along this window of telemetry - */ - @NonNull - private JSONArray formatEventsAndInitialize() { - // Save the current events and clean for next (not this one) telemetry send - ConcurrentHashMap, MissingClassEvent> toReport = MissingClassTelemetry.events.getEventsAndClean(); - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Cleaned events for missing classes"); - } - - return formatEvents(toReport); - } - - /** - * Format the events gathered in a map used to create the json object to send via telemetry. The events are named by - * the class not found. But a class could be repeated if it was thrown from several places. The interesting - * pieces of information we want to gather are the places where the {@link ClassNotFoundException} or the - * {@link NoClassDefFoundError} errors happens, rather than the class itself. - * @param events events collected in this telemetry window. - * @return the events formatted in a map. - */ - @NonNull - private JSONArray formatEvents(@NonNull ConcurrentHashMap, MissingClassEvent> events) { - JSONArray jsonEvents = new JSONArray(); - - events.forEach((stackTrace, event) -> { - JSONObject eventObject = new JSONObject(); - eventObject.put("className", event.getClassName()); - eventObject.put("class", event.getClassName()); - eventObject.put("time", event.getTime()); - eventObject.put("occurrences", Long.toString(event.getOccurrences())); - eventObject.put("stacktrace", event.getStackTrace()); - - jsonEvents.add(eventObject); - }); - - return jsonEvents; - } - - /** - * The current time in the same way as other telemetry implementations. - * @return the UTC time formatted with the pattern yyyy-MM-dd'T'HH:mm'Z' - */ - @NonNull - static String clientDateString() { - TimeZone tz = TimeZone.getTimeZone("UTC"); - DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'"); - df.setTimeZone(tz); // strip timezone - return df.format(new Date()); - } - - /** - * Store the exception if it's from a split package of Java. This method report this exception directly, it doesn't - * look into the causes or suppressed exceptions of the exception specified. This method tends to be used in the - * ClassLoader directly. Outside the class loaders is best to use {@link #reportExceptionInside(Throwable)} - * @param name the name of the class - * @param e the throwable to report if needed - */ - public static void reportException(@NonNull String name, @NonNull Throwable e) { - if (enabled()) { - //ClassDefFoundError uses / instead of . - name = name.replace('/', '.').trim(); - - // We call the methods in this order because if the missing class is not java related, we don't loop over the - // stack trace to look if it's not thrown from an ignored place avoiding an impact on performance. - if (isFromMovedPackage(name) && !calledFromIgnoredPlace(e)) { - if (LOGGER.isLoggable(Level.WARNING) && !wasAlreadyReported(name)) { - LOGGER.log(Level.WARNING, "Added a missed class for missing class telemetry. Class: " + name, e); - } - events.put(name, e); - } - } - } - - private static boolean wasAlreadyReported(@NonNull String className) { - return events.alreadyRegistered(className); - } - - /** - * Determine if the exception specified was thrown from an ignored place - * @param throwable The exception thrown - * @return true if in the stack trace there is an ignored method / class. - */ - private static boolean calledFromIgnoredPlace(@NonNull Throwable throwable) { - for(String[] ignoredPlace : IGNORED_PLACES) { - if (calledFrom(throwable, ignoredPlace[0], ignoredPlace[1])) { - return true; - } - } - return false; - } - - /** - * Check if the throwable was thrown by the class and the method specified. - * @param throwable stack trace to look at - * @param clazz class to look for in the stack trace - * @param method method where the throwable was thrown in the clazz - * @return true if the method of the clazz has thrown the throwable - */ - private static boolean calledFrom (@NonNull Throwable throwable, @NonNull String clazz, @NonNull String method){ - StackTraceElement[] trace = throwable.getStackTrace(); - for (StackTraceElement el : trace) { - //If the exception has the class and method searched, it's called from there - if (clazz.equals(el.getClassName()) && el.getMethodName().equals(method)) { - return true; - } - } - return false; - } - - /** - * Store the exception extracting the class name from the message of the throwable specified. This method report - * this exception directly, it doesn't look into the causes or suppressed exceptions of the exception specified. - * This method tends to be used in the ClassLoader directly. Outside the class loaders is best to use - * {@link #reportExceptionInside(Throwable)} - * @param e the exception to report if needed - */ - private static void reportException(@NonNull Throwable e) { - if (enabled()) { - String name = e.getMessage(); - - if (name == null || name.trim().isEmpty()) { - LOGGER.log(Level.INFO, "No class name could be extracted from the throwable to determine if it's reportable", e); - } else { - reportException(name, e); - } - } - } - - private static boolean isFromMovedPackage(@NonNull String clazz) { - for (String movedPackage : MOVED_PACKAGES) { - if (clazz.startsWith(movedPackage)) { - return true; - } - } - return false; - } - - /** - * Report the class not found if this exception or any of its causes or suppressed exceptions is related to missed - * classes. - * @param e the exception to look into - */ - public static void reportExceptionInside(@NonNull Throwable e) { - if (enabled()) { - // Use a Set with equity based on == instead of equal to find cycles - Set exceptionsReviewed = Collections.newSetFromMap(new IdentityHashMap<>()); - reportExceptionInside(e, exceptionsReviewed); - } - } - - /** - * Find the exception to report among the exception passed and its causes and suppressed exceptions. It does a - * recursion and uses a Set to avoid circular references. - * @param e the exception - * @param exceptionsReviewed the set of already reviewed exceptions - * @return true if a exception was reported - */ - private static boolean reportExceptionInside(@NonNull Throwable e, @NonNull Set exceptionsReviewed) { - if (exceptionsReviewed.contains(e)) { - LOGGER.log(Level.WARNING, CIRCULAR_REFERENCE, e); - // Don't go deeper, we already did - return false; - } - - // Add this exception to the list of already reviewed exceptions before going deeper in its causes or suppressed - // exceptions - exceptionsReviewed.add(e); - - // It this exception is the one searched - if (isMissedClassRelatedException(e)) { - MissingClassTelemetry.reportException(e); - return true; - } - - // We search in its cause exception - if (e.getCause() != null) { - if (reportExceptionInside(e.getCause(), exceptionsReviewed)) { - return true; - } - } - - // We search in its suppressed exceptions - for (Throwable suppressed: e.getSuppressed()) { - if (suppressed != null) { - if (reportExceptionInside(suppressed, exceptionsReviewed)) { - return true; - } - } - } - - // If this exception or its ancestors are not related with missed classes - return false; - } - - /** - * Check if the exception specified is related with a missed class, that is, defined in the - * {@link #reportableExceptions} method. - * @param e the exception to look into - * @return true if the class is related with missed classes. - */ - private static boolean isMissedClassRelatedException(Throwable e) { - return reportableExceptions.contains(e.getClass()); - } -} diff --git a/core/src/main/java/jenkins/util/AntClassLoader.java b/core/src/main/java/jenkins/util/AntClassLoader.java index f3cac15914..9bccc8fd11 100644 --- a/core/src/main/java/jenkins/util/AntClassLoader.java +++ b/core/src/main/java/jenkins/util/AntClassLoader.java @@ -17,7 +17,6 @@ */ package jenkins.util; -import jenkins.telemetry.impl.java11.MissingClassTelemetry; import org.apache.tools.ant.BuildEvent; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.MagicNames; @@ -1120,36 +1119,27 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener, Clo if (theClass != null) { return theClass; } - - //Surround the former logic with a try-catch to report missing class exceptions via Java11 telemetry - try { - if (isParentFirst(classname)) { - try { - theClass = findBaseClass(classname); - log("Class " + classname + " loaded from parent loader " + "(parentFirst)", - Project.MSG_DEBUG); - } catch (final ClassNotFoundException cnfe) { - theClass = findClass(classname); - log("Class " + classname + " loaded from ant loader " + "(parentFirst)", - Project.MSG_DEBUG); - } - } else { - try { - theClass = findClass(classname); - log("Class " + classname + " loaded from ant loader", Project.MSG_DEBUG); - } catch (final ClassNotFoundException cnfe) { - if (ignoreBase) { - throw cnfe; - } - theClass = findBaseClass(classname); - log("Class " + classname + " loaded from parent loader", Project.MSG_DEBUG); - } + if (isParentFirst(classname)) { + try { + theClass = findBaseClass(classname); + log("Class " + classname + " loaded from parent loader " + "(parentFirst)", + Project.MSG_DEBUG); + } catch (final ClassNotFoundException cnfe) { + theClass = findClass(classname); + log("Class " + classname + " loaded from ant loader " + "(parentFirst)", + Project.MSG_DEBUG); + } + } else { + try { + theClass = findClass(classname); + log("Class " + classname + " loaded from ant loader", Project.MSG_DEBUG); + } catch (final ClassNotFoundException cnfe) { + if (ignoreBase) { + throw cnfe; + } + theClass = findBaseClass(classname); + log("Class " + classname + " loaded from parent loader", Project.MSG_DEBUG); } - } catch (ClassNotFoundException cnfe) { - //To catch CNFE thrown from this.getClass().getClassLoader().loadClass(classToLoad); from a plugin step or - //a plugin page - MissingClassTelemetry.reportException(classname, cnfe); - throw cnfe; } if (resolve) { resolveClass(theClass); diff --git a/test/src/test/java/jenkins/telemetry/MissingClassTelemetryTest.java b/test/src/test/java/jenkins/telemetry/MissingClassTelemetryTest.java deleted file mode 100644 index 5f60f5de34..0000000000 --- a/test/src/test/java/jenkins/telemetry/MissingClassTelemetryTest.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2019 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. - */ -package jenkins.telemetry; - -import edu.umd.cs.findbugs.annotations.CheckForNull; -import hudson.ExtensionList; -import hudson.model.UnprotectedRootAction; -import hudson.security.csrf.CrumbExclusion; -import jenkins.telemetry.impl.java11.CatcherClassLoader; -import jenkins.telemetry.impl.java11.MissingClassTelemetry; -import net.sf.json.JSONArray; -import net.sf.json.JSONObject; -import org.apache.commons.io.IOUtils; -import org.junit.Assume; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.jvnet.hudson.test.JenkinsRule; -import org.jvnet.hudson.test.TestExtension; -import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.StaplerResponse; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.StringWriter; -import java.nio.charset.StandardCharsets; -import java.time.LocalDate; - -import static org.junit.Assert.assertEquals; - -/** - * This test needs to be here to be able to modify the {@link Telemetry#ENDPOINT} as it's package protected. - */ -public class MissingClassTelemetryTest { - private static final String TELEMETRY_ENDPOINT = "uplink"; - private CatcherClassLoader cl; - - @Rule - public JenkinsRule j = new JenkinsRule(); - - private static JSONObject received = null; - - @Before - public void prepare() throws Exception { - received = null; - cl = new CatcherClassLoader(this.getClass().getClassLoader()); - Telemetry.ENDPOINT = j.getURL().toString() + TELEMETRY_ENDPOINT + "/events"; - j.jenkins.setNoUsageStatistics(false); // tests usually don't submit this, but we need this - } - - /** - * Test if the telemetry sent works and the received data is the expected for a specific case (5 occurrences of the - * same stack trace). - * @throws InterruptedException if the thread is interrupted while sleeping - */ - @Test - public void telemetrySentWorks() throws InterruptedException { - Assume.assumeTrue("The telemetry should be enabled", MissingClassTelemetry.enabled()); - Assume.assumeTrue("The telemetry shouldn't be ended", Telemetry.all().get(MissingClassTelemetry.class).getEnd().isAfter(LocalDate.now())); - // Generate 5 events - for(int i = 0; i < 5; i++) { - try { - cl.loadClass("sun.java.MyNonExistentClass"); - } catch (ClassNotFoundException ignored) { - } - } - - // Run the telemetry sent - ExtensionList.lookupSingleton(Telemetry.TelemetryReporter.class).doRun(); - do { - - Thread.sleep(250); - } while (received == null); // this might end up being flaky due to 1 to many active telemetry trials - - - // The telemetry stuff sent is the class expected, the number of events is 1, the class not found is the - // expected and the number of occurrences is the expected - assertEquals(MissingClassTelemetry.class.getName(), received.getString("type")); - JSONArray events = received.getJSONObject("payload").getJSONArray("classMissingEvents"); - assertEquals(1, events.size()); - assertEquals("sun.java.MyNonExistentClass", ((JSONObject) events.get(0)).get("className")); - assertEquals(5, Integer.parseInt( (String) ((JSONObject) events.get(0)).get("occurrences"))); - } - - /** - * Avoid crumb checking (CSRF) - */ - @TestExtension - public static class NoCrumb extends CrumbExclusion { - @Override - public boolean process(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { - String pathInfo = request.getPathInfo(); - if (pathInfo != null && pathInfo.startsWith("/uplink")) { - chain.doFilter(request, response); - return true; - } - return false; - } - } - - @TestExtension - public static class TelemetryReceiver implements UnprotectedRootAction { - public void doEvents(StaplerRequest request, StaplerResponse response) throws IOException { - StringWriter sw = new StringWriter(); - IOUtils.copy(request.getInputStream(), sw, StandardCharsets.UTF_8); - received = JSONObject.fromObject(sw.toString()); - } - - @CheckForNull - @Override - public String getIconFileName() { - return null; - } - - @CheckForNull - @Override - public String getDisplayName() { - return null; - } - - @CheckForNull - @Override - public String getUrlName() { - return TELEMETRY_ENDPOINT; - } - } -} diff --git a/test/src/test/java/jenkins/telemetry/impl/java11/MissingClassTelemetryFasterTest.java b/test/src/test/java/jenkins/telemetry/impl/java11/MissingClassTelemetryFasterTest.java deleted file mode 100644 index 7862aa7185..0000000000 --- a/test/src/test/java/jenkins/telemetry/impl/java11/MissingClassTelemetryFasterTest.java +++ /dev/null @@ -1,388 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2019 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. - */ -package jenkins.telemetry.impl.java11; - -import org.junit.Assume; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.jvnet.hudson.test.LoggerRule; - -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Logger; - -import static org.hamcrest.Matchers.not; -import static org.hamcrest.core.StringContains.containsString; -import static org.junit.Assert.assertEquals; -import static org.hamcrest.MatcherAssert.assertThat; - -/** - * Tests without a running Jenkins for Java11 Telemetry of ClassNotFoundException. - */ -public class MissingClassTelemetryFasterTest { - final String NON_EXISTING_CLASS = "sun.java.MyNonExistentJavaClass"; - - private CatcherClassLoader cl; - - @Rule - public LoggerRule logging = new LoggerRule().record(MissingClassTelemetry.class, Logger.getLogger(MissingClassTelemetry.class.getName()).getLevel()).capture(5); - - @Before - public void cleanEvents() { - cl = new CatcherClassLoader(this.getClass().getClassLoader()); - MissingClassTelemetry.getEvents().getEventsAndClean(); - MissingClassTelemetry.getEvents().clearEventsOnThisExecution(); - } - - @Test - public void maxNumberEvents() { - Assume.assumeTrue("The telemetry should be enabled", MissingClassTelemetry.enabled()); - - // Backup to restore at the end of the test - int maxEventsBefore = MissingClassEvents.MAX_EVENTS_PER_SEND; - - try { - MissingClassEvents.MAX_EVENTS_PER_SEND = 1; - - try { - cl.loadClass(NON_EXISTING_CLASS); - } catch (ClassNotFoundException ignored) { - } - - try { - cl.loadClass("sun.java.MySecondNonExistentJavaClass"); - } catch (ClassNotFoundException ignored) { - } - - - ConcurrentHashMap, MissingClassEvent> eventsGathered = MissingClassTelemetry.getEvents().getEventsAndClean(); - - // Only one class miss gathered - assertEquals(1, eventsGathered.size()); - - // 2 log entries - assertEquals("Two missing class should be printed out in the log", 2, logging.getRecords().stream().filter(r -> r.getMessage().contains(NON_EXISTING_CLASS) || - r.getMessage().contains("sun.java.MySecondNonExistentJavaClass")).count()); - } finally { - MissingClassEvents.MAX_EVENTS_PER_SEND = maxEventsBefore; - } - } - - /** - * The same class failed to be loaded in different places ends up in two records of telemetry with one occurrence - * each. - */ - @Test - public void differentEventsAlthoughSameClass() { - Assume.assumeTrue("The telemetry should be enabled", MissingClassTelemetry.enabled()); - - try { - cl.loadClass(NON_EXISTING_CLASS); - } catch (ClassNotFoundException ignored) { - } - - try { - cl.loadClass(NON_EXISTING_CLASS); - } catch (ClassNotFoundException ignored) { - } - - // Get the events gathered - MissingClassEvents events = MissingClassTelemetry.getEvents(); - ConcurrentHashMap, MissingClassEvent> eventsGathered = events.getEventsAndClean(); - - // Only one class miss gathered with two occurrences - assertEquals(2, eventsGathered.size()); - assertEquals(1, eventsGathered.values().iterator().next().getOccurrences()); - assertEquals(1, eventsGathered.values().iterator().next().getOccurrences()); - - // The stack trace of these CNFE are different but we only look at class names when printing on logs, so - // just one log entry - assertEquals("Single missing class should be printed out in the log", 1, logging.getRecords().stream().filter(r -> r.getMessage().contains(NON_EXISTING_CLASS)).count()); - } - - /** - * The same class thrown in the same line ends up in a single event with two occurrences. - */ - @Test - public void addOccurrenceIfSameStackTrace() { - Assume.assumeTrue("The telemetry should be enabled", MissingClassTelemetry.enabled()); - - for (int i = 0; i < 2; i++) { - try { - //Exceptions thrown at the same line, with the same stack trace become occurrences of just one event - cl.loadClass(NON_EXISTING_CLASS); - } catch (ClassNotFoundException ignored) { - } - } - - // Get the events gathered - MissingClassEvents events = MissingClassTelemetry.getEvents(); - ConcurrentHashMap, MissingClassEvent> eventsGathered = events.getEventsAndClean(); - - // Only one class miss gathered with two occurrences - assertEquals(1, eventsGathered.size()); - assertEquals(2, eventsGathered.values().iterator().next().getOccurrences()); - - // The class name and also the stack trace of these CNFEs are the same, so 1 log event - assertEquals("Just one missing class should be printed out in the log", 1, logging.getRecords().stream().filter(r -> r.getMessage().contains(NON_EXISTING_CLASS)).count()); - } - - /** - * A class not from the split packages is not gathered. - */ - @Test - public void nonJavaClassesNotGathered() { - Assume.assumeTrue("The telemetry should be enabled", MissingClassTelemetry.enabled()); - - try { - cl.loadClass("jenkins.MyNonExistentClass"); - } catch (ClassNotFoundException ignored) { - } - - // Get the events gathered - MissingClassEvents events = MissingClassTelemetry.getEvents(); - ConcurrentHashMap, MissingClassEvent> eventsGathered = events.getEventsAndClean(); - - // No events gathered - assertEquals(0, eventsGathered.size()); - - // No log - assertEquals("No log if the class is not a java one", 0, logging.getRecords().stream().filter(r -> r.getMessage().contains(NON_EXISTING_CLASS)).count()); - } - - /** - * Only a max number of events is gathered. In this test, just one with two occurrences - */ - @Test - public void maxEventsLimitedSameStackTrace() { - Assume.assumeTrue("The telemetry should be enabled", MissingClassTelemetry.enabled()); - - // Backup to restore at the end of the test - int maxEventsBefore = MissingClassEvents.MAX_EVENTS_PER_SEND; - MissingClassEvents.MAX_EVENTS_PER_SEND = 1; - try { - for (int i = 0; i < 2; i++) { - try { - //Exceptions thrown at the same line, with the same stack trace become occurrences of just one event - cl.loadClass(NON_EXISTING_CLASS); - } catch (ClassNotFoundException ignored) { - } - } - - // Get the events gathered - MissingClassEvents events = MissingClassTelemetry.getEvents(); - ConcurrentHashMap, MissingClassEvent> eventsGathered = events.getEventsAndClean(); - - // Only one event gathered - assertEquals(1, eventsGathered.size()); - assertEquals(2, eventsGathered.values().iterator().next().getOccurrences()); - - assertEquals("One event should be logged", 1, logging.getRecords().stream().filter(r -> r.getMessage().contains(NON_EXISTING_CLASS)).count()); - } finally { - MissingClassEvents.MAX_EVENTS_PER_SEND = maxEventsBefore; - } - } - - /** - * Only a max number of events is gathered. In this test, just one wit one occurrence. The second one is discarded - */ - @Test - public void maxEventsLimitedDifferentStackTrace() { - Assume.assumeTrue("The telemetry should be enabled", MissingClassTelemetry.enabled()); - - // Backup to restore at the end of the test - int maxEventsBefore = MissingClassEvents.MAX_EVENTS_PER_SEND; - MissingClassEvents.MAX_EVENTS_PER_SEND = 1; - - try { - try { - cl.loadClass("sun.java.MyNonExistentClassGathered"); - } catch (ClassNotFoundException ignored) { - } - - try { - cl.loadClass("sun.java.MyNonExistentJavaClassNotGathered"); - } catch (ClassNotFoundException ignored) { - } - - // Get the events gathered - MissingClassEvents events = MissingClassTelemetry.getEvents(); - ConcurrentHashMap, MissingClassEvent> eventsGathered = events.getEventsAndClean(); - - // Only one event gathered - assertEquals(1, eventsGathered.size()); - assertEquals(1, eventsGathered.values().iterator().next().getOccurrences()); - assertThat(eventsGathered.values().iterator().next().getStackTrace(), containsString("MyNonExistentClassGathered")); - assertThat(eventsGathered.values().iterator().next().getStackTrace(), not(containsString("MyNonExistentJavaClassNotGathered"))); - - // Log has nothing to do with the limit for the sending. We print both because they have different class names - assertEquals("Two CNFE should be logged", 2, logging.getRecords().stream().filter(r -> r.getMessage().contains("MyNonExistentClassGathered") || - r.getMessage().contains("MyNonExistentJavaClassNotGathered")).count()); - } finally { - MissingClassEvents.MAX_EVENTS_PER_SEND = maxEventsBefore; - } - } - - /** - * Test the cycles in the exceptions. This specific tests shows that we first look for reportable exceptions in the - * causes and when found a reportable exception, we stop searching. So the warning because a cycle is not logged. - */ - @Test - public void cyclesNotReachedBecauseCNFEReported() { - Assume.assumeTrue("The telemetry should be enabled", MissingClassTelemetry.enabled()); - - try { - /* - parent -> child -> cnfe - \ - parent - We first look into the causes exceptions. When found, we don't look into the suppressed, so the cycle is not - found here - */ - - ClassNotFoundException cnfe = new ClassNotFoundException(NON_EXISTING_CLASS); - Exception child = new Exception("child", cnfe); - Exception parent = new Exception("parent", child); // parent -> caused by -> child - child.addSuppressed(parent); - - // Some extra wrapping - throw new Exception(new Exception (new Exception (parent))); - - } catch (Exception e) { - // Look for anything to report - MissingClassTelemetry.reportExceptionInside(e); - - // Get the events gathered - MissingClassEvents events = MissingClassTelemetry.getEvents(); - ConcurrentHashMap, MissingClassEvent> eventsGathered = events.getEventsAndClean(); - - // One event gathered - assertEquals(1, eventsGathered.size()); - - // the circular reference has not been recorded in the log because we reached a CNFE previously - assertEquals("No circular message was printed in logs", 0, logging.getRecords().stream().filter(r -> r.getMessage().contains(MissingClassTelemetry.CIRCULAR_REFERENCE)).count()); - } - } - - /** - * Test the cycles in the exceptions. This specific tests shows that we first look for reportable exceptions deep - * in the causes and suppressed exceptions in this order. We report the cycle but also the CNFE. - */ - @Test - public void cnfeFoundAfterCycle() { - Assume.assumeTrue("The telemetry should be enabled", MissingClassTelemetry.enabled()); - - try { - ClassNotFoundException cnfe = new ClassNotFoundException(NON_EXISTING_CLASS); - - /* - parent -> child - \ \ - cnfe parent - */ - Exception child = new Exception("child"); - Exception parent = new Exception("parent", child); // parent -> caused by -> child - child.addSuppressed(parent); // Boooomm!!!! The parent is a child suppressed exception -> cycle - parent.addSuppressed(cnfe); - - // Some extra wrapping - throw new Exception(new Exception (new Exception (parent))); - - } catch (Exception e) { - // Look for anything to report - MissingClassTelemetry.reportExceptionInside(e); - - // Get the events gathered - MissingClassEvents events = MissingClassTelemetry.getEvents(); - ConcurrentHashMap, MissingClassEvent> eventsGathered = events.getEventsAndClean(); - - // One event gathered - assertEquals(1, eventsGathered.size()); - - // the circular reference has been recorded in the log - assertThat(logging, LoggerRule.recorded(containsString(MissingClassTelemetry.CIRCULAR_REFERENCE))); - } - } - - /** - * Test the cycles in the exceptions. This specific tests shows that we first look for reportable exceptions deep - * in the causes and suppressed exceptions in this order. We report the cycle but also the CNFE in the parent. - */ - @Test - public void cnfeAfterCNFENotJava11AndCycle() { - Assume.assumeTrue("The telemetry should be enabled", MissingClassTelemetry.enabled()); - - try { - ClassNotFoundException cnfe = new ClassNotFoundException("sun.java.MyNonExistentClassGathered"); - ClassNotFoundException cnfeNonJava11 = new ClassNotFoundException("MyNonExistentClassGathered"); - - /* - parent -> child -> grandchild - \ \ \ - cnfe parent cnfe (non java11) - */ - Exception grandchild = new Exception("grandchild"); - Exception child = new Exception("child"); - Exception parent = new Exception("parent", child); // parent -> caused by -> child - child.addSuppressed(parent); // Boooomm!!!! The parent is a child suppressed exception -> cycle - grandchild.addSuppressed(cnfeNonJava11); - parent.addSuppressed(cnfe); - - // Some extra wrapping - throw new Exception(new Exception (new Exception (parent))); - - } catch (Exception e) { - // Look for anything to report - MissingClassTelemetry.reportExceptionInside(e); - - // Get the events gathered - MissingClassEvents events = MissingClassTelemetry.getEvents(); - ConcurrentHashMap, MissingClassEvent> eventsGathered = events.getEventsAndClean(); - - // One event gathered - assertEquals(1, eventsGathered.size()); - - // the circular reference has been recorded in the log - assertThat(logging, LoggerRule.recorded(containsString(MissingClassTelemetry.CIRCULAR_REFERENCE))); - } - } - - @Test - public void nothingGatheredWhenTelemetryDisabled() { - Assume.assumeFalse("The telemetry should not be enabled", MissingClassTelemetry.enabled()); - - try { - cl.loadClass("sun.java.MyNonExistentClass"); - } catch (ClassNotFoundException ignored) { - } - - ConcurrentHashMap, MissingClassEvent> eventsGathered = MissingClassTelemetry.getEvents().getEventsAndClean(); - - // No events gathered - assertEquals(0, eventsGathered.size()); - - assertEquals("No log if telemetry disabled", 0, logging.getRecords().size()); - } -} From 7110e181fe603f4023728631972d8f504812ea7a Mon Sep 17 00:00:00 2001 From: Tim Jacomb <21194782+timja@users.noreply.github.com> Date: Tue, 24 Aug 2021 12:08:37 +0100 Subject: [PATCH 92/98] Automate jenkins.io changelog (#5583) --- .github/release-drafter.yml | 5 ++-- .github/workflows/changelog.yml | 41 +++++++++++++++++++++++---------- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 20d33f3a6b..9013d1a824 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -6,9 +6,8 @@ name-template: $NEXT_MINOR_VERSION tag-template: jenkins-$NEXT_MINOR_VERSION template: | - **Disclaimer**: This is an automatically generated changelog draft for Jenkins weekly releases. - See https://www.jenkins.io/changelog/ for the official changelogs. - For `changelog.yaml` drafts see GitHub action artifacts attached to release commits. + _This is an automatically generated changelog draft for Jenkins weekly releases. + See https://www.jenkins.io/changelog/ for the official changelogs._ $CHANGES diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index 351260d3c2..92ab774cb0 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -3,6 +3,7 @@ name: Changelog Drafter on: + workflow_dispatch: push: # branches to consider in the event; optional, defaults to all branches: @@ -10,6 +11,7 @@ on: jobs: update_draft_release: + if: github.repository_owner == 'jenkinsci' runs-on: ubuntu-latest steps: # Drafts your next Release notes as Pull Requests are merged into "master" @@ -19,6 +21,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Generates a YAML changelog file using https://github.com/jenkinsci/jenkins-core-changelog-generator + # used by Oleg N in open graph generator experiment for now - name: Generate YAML changelog draft id: jenkins-core-changelog-generator uses: jenkinsci/jenkins-core-changelog-generator@master @@ -29,15 +32,29 @@ jobs: with: name: changelog.yaml path: changelog.yaml -#TODO(oleg-nenashev): It will not work well with Release Drafter which does not recreated releases. Asset does not get overwritten, and there is no ready-to-go API for overriding assets - # Upload YAML to the release draft assets -# - name: Upload changelog.yaml to the Release Draft -# id: upload-changelog-yaml -# uses: actions/upload-release-asset@v1.0.1 -# env: -# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -# with: -# upload_url: ${{ steps.release-drafter.outputs.upload_url }} -# asset_path: ./changelog.yaml -# asset_name: changelog.yaml -# asset_content_type: text/yaml + + jenkins_io_draft: + runs-on: ubuntu-latest + if: github.repository_owner == 'jenkinsci' + steps: + - uses: tibdex/github-app-token@v1 + id: generate-token + with: + app_id: ${{ secrets.JENKINS_CHANGELOG_UPDATER_APP_ID }} + private_key: ${{ secrets.JENKINS_CHANGELOG_UPDATER_PRIVATE_KEY }} + repository: jenkins-infra/jenkins.io + - name: Check out + uses: actions/checkout@v2.3.4 + with: + fetch-depth: 0 + - name: Publish jenkins.io changelog draft + env: + GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }} + GITHUB_AUTH: "nobody:${{ steps.generate-token.outputs.token }}" + GIT_AUTHOR_NAME: jenkins-infra-changelog-generator + GIT_AUTHOR_EMAIL: <86592549+jenkins-infra-changelog-generator[bot]@users.noreply.github.com> + GIT_COMMITTER_NAME: jenkins-infra-changelog-generator + GIT_COMMITTER_EMAIL: <86592549+jenkins-infra-changelog-generator[bot]@users.noreply.github.com> + run: | + wget --quiet https://raw.githubusercontent.com/jenkinsci/core-changelog-generator/master/generate-weekly-changelog.sh + bash generate-weekly-changelog.sh From 4c182e7120c50690a1e621deaa46fd7960480c0b Mon Sep 17 00:00:00 2001 From: Jenkins Release Bot <66998184+jenkins-release-bot@users.noreply.github.com> Date: Tue, 24 Aug 2021 12:51:25 +0000 Subject: [PATCH 93/98] [maven-release-plugin] prepare release jenkins-2.308 --- 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 ba477f0b1b..6e11a13487 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.308 jenkins-bom diff --git a/cli/pom.xml b/cli/pom.xml index 746123378f..93fa2bb409 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.308 cli diff --git a/core/pom.xml b/core/pom.xml index a617d48cd9..e208095780 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.308 jenkins-core diff --git a/pom.xml b/pom.xml index 4a303fc4a2..071868f770 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.308 pom Jenkins main module @@ -61,7 +61,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.308 diff --git a/test/pom.xml b/test/pom.xml index 4264e3531f..e3eacc7764 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.308 jenkins-test diff --git a/war/pom.xml b/war/pom.xml index a6bc9e2fa1..267818dc0d 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.308 jenkins-war From f88c67e6f1470ae3daa5a289c8c317d992b5665c Mon Sep 17 00:00:00 2001 From: Jenkins Release Bot <66998184+jenkins-release-bot@users.noreply.github.com> Date: Tue, 24 Aug 2021 12:51:43 +0000 Subject: [PATCH 94/98] [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 6e11a13487..ba477f0b1b 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.308 + ${revision}${changelist} jenkins-bom diff --git a/cli/pom.xml b/cli/pom.xml index 93fa2bb409..746123378f 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main jenkins-parent - 2.308 + ${revision}${changelist} cli diff --git a/core/pom.xml b/core/pom.xml index e208095780..a617d48cd9 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.308 + ${revision}${changelist} jenkins-core diff --git a/pom.xml b/pom.xml index 071868f770..13661e0566 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.308 + ${revision}${changelist} pom Jenkins main module @@ -61,7 +61,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.308 + ${scmTag} @@ -70,7 +70,7 @@ THE SOFTWARE. - 2.308 + 2.309 -SNAPSHOT + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + System Applications + + + Jakub Steiner + + + http://jimmac.musichall.cz/ + + + system + applications + group + category + admin + root + + + + + + + + + + + + + + + + + + + + + + + + +