From b105627b7357149f98f87ec2bf494e25bd2ed88b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20Gond=C5=BEa?= Date: Sat, 4 Oct 2014 19:02:41 +0200 Subject: [PATCH 001/272] Towards 1.580.1 --- cli/pom.xml | 2 +- core/pom.xml | 2 +- pom.xml | 2 +- test/pom.xml | 2 +- war/pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cli/pom.xml b/cli/pom.xml index aefb8624d1..1246ee67ac 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ pom org.jenkins-ci.main - 1.580 + 1.580.1-SNAPSHOT cli diff --git a/core/pom.xml b/core/pom.xml index d4237c9570..a8c3aff01f 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 1.580 + 1.580.1-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index c750099276..ec02c9834a 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 1.580 + 1.580.1-SNAPSHOT pom Jenkins main module diff --git a/test/pom.xml b/test/pom.xml index ad8ad4021e..6b17874b33 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. pom org.jenkins-ci.main - 1.580 + 1.580.1-SNAPSHOT org.jenkins-ci.main jenkins-test-harness diff --git a/war/pom.xml b/war/pom.xml index 7908cc6d8d..9d24986be1 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 1.580 + 1.580.1-SNAPSHOT ../pom.xml From 8ae29e50c7356a8e2ec9be1126bad957dab0117a Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Wed, 17 Sep 2014 15:35:22 -0700 Subject: [PATCH 002/272] [JENKINS-24458 JENKINS-11543] integrated the newer version of stapler with reduced log level (cherry picked from commit c57f9ed577b9635f01aa5affabf1f770b689a115) Conflicts: changelog.html --- core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/pom.xml b/core/pom.xml index a8c3aff01f..0b4e47b806 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -42,7 +42,7 @@ THE SOFTWARE. true - 1.228 + 1.229 2.5.6.SEC03 1.8.9 From 9c82fc42eb08b89047c544aaa586291ad1485472 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Fri, 5 Sep 2014 15:54:04 -0700 Subject: [PATCH 003/272] [JENKINS-23471 JENKINS-24050] Integrated the fix in remoting to Jenkins 1.580. (cherry picked from commit 8fc609fe0952b285d5b26a59fd5ff4c29704d33d) --- changelog.html | 7 ++++++- pom.xml | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/changelog.html b/changelog.html index 9a573c333c..e77e92e036 100644 --- a/changelog.html +++ b/changelog.html @@ -55,7 +55,12 @@ Upcoming changes diff --git a/pom.xml b/pom.xml index ec02c9834a..96343e1015 100644 --- a/pom.xml +++ b/pom.xml @@ -173,7 +173,7 @@ THE SOFTWARE. org.jenkins-ci.main remoting - 2.44 + 2.45 From 4d455fbb0d2f33bd87a8c38580f7ff2ffb78b0ac Mon Sep 17 00:00:00 2001 From: Daniel Beck Date: Sun, 14 Sep 2014 13:03:08 +0200 Subject: [PATCH 004/272] [FIXED JENKINS-24614] Broken format string in Hebrew console output (cherry picked from commit 07fd5fa45d4b818fc11de97ce99ab6b3423aca85) --- core/src/main/resources/hudson/model/Run/console_he.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/hudson/model/Run/console_he.properties b/core/src/main/resources/hudson/model/Run/console_he.properties index 36cd1812f3..03765f592e 100644 --- a/core/src/main/resources/hudson/model/Run/console_he.properties +++ b/core/src/main/resources/hudson/model/Run/console_he.properties @@ -22,4 +22,4 @@ Console\ Output=\u05E4\u05DC\u05D8 \u05DE\u05E1\u05DA View\ as\ plain\ text=\u05E6\u05E4\u05D9\u05D9\u05D4 \u05DB\u05D8\u05E7\u05E1\u05D8 -skipSome=\u05DE\u05D3\u05DC\u05D2 {0,\u05DE\u05E1\u05E4\u05E8,\u05DE\u05E1\u05E4\u05E8 \u05E9\u05DC\u05DD} \u05E7"\u05D1.. \u05D3\u05D5"\u05D7 \u05DE\u05DC\u05D0 +skipSome=\u05DE\u05D3\u05DC\u05D2 {0,number,integer} \u05E7"\u05D1.. \u05D3\u05D5"\u05D7 \u05DE\u05DC\u05D0 From efae48a451b1dbb15a13abff26b0bec269a63909 Mon Sep 17 00:00:00 2001 From: David Ruhmann Date: Fri, 15 Aug 2014 12:21:15 -0500 Subject: [PATCH 005/272] [FIXED JENKINS-7478] Create Line Ending Conversion utility to convert scripts to proper OS line ending type. Tests included. (cherry picked from commit 67ac47af0709dde6297dffeb210b03d8b65f6033) --- .../src/main/java/hudson/tasks/BatchFile.java | 10 ++- core/src/main/java/hudson/tasks/Shell.java | 34 +++------- .../tools/AbstractCommandInstaller.java | 15 +---- .../hudson/tools/BatchCommandInstaller.java | 8 ++- .../java/hudson/tools/CommandInstaller.java | 8 ++- .../hudson/util/LineEndingConversion.java | 66 +++++++++++++++++++ .../test/java/hudson/tasks/BatchFileTest.java | 30 +++++++++ .../src/test/java/hudson/tasks/ShellTest.java | 43 +++++++++--- .../tools/BatchCommandInstallerTest.java | 22 +++++++ .../hudson/tools/CommandInstallerTest.java | 22 +++++++ .../hudson/util/LineEndingConversionTest.java | 28 ++++++++ 11 files changed, 233 insertions(+), 53 deletions(-) create mode 100644 core/src/main/java/hudson/util/LineEndingConversion.java create mode 100644 test/src/test/java/hudson/tasks/BatchFileTest.java create mode 100644 test/src/test/java/hudson/tools/BatchCommandInstallerTest.java create mode 100644 test/src/test/java/hudson/tools/CommandInstallerTest.java create mode 100644 test/src/test/java/hudson/util/LineEndingConversionTest.java diff --git a/core/src/main/java/hudson/tasks/BatchFile.java b/core/src/main/java/hudson/tasks/BatchFile.java index 0943e9147f..b2506474be 100644 --- a/core/src/main/java/hudson/tasks/BatchFile.java +++ b/core/src/main/java/hudson/tasks/BatchFile.java @@ -26,9 +26,11 @@ package hudson.tasks; import hudson.FilePath; import hudson.Extension; import hudson.model.AbstractProject; +import hudson.util.LineEndingConversion; import net.sf.json.JSONObject; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerRequest; +import java.io.ObjectStreamException; /** * Executes commands by using Windows batch file. @@ -38,7 +40,7 @@ import org.kohsuke.stapler.StaplerRequest; public class BatchFile extends CommandInterpreter { @DataBoundConstructor public BatchFile(String command) { - super(command); + super(LineEndingConversion.convertEOL(command, LineEndingConversion.EOLType.Windows)); } public String[] buildCommandLine(FilePath script) { @@ -46,13 +48,17 @@ public class BatchFile extends CommandInterpreter { } protected String getContents() { - return command+"\r\nexit %ERRORLEVEL%"; + return LineEndingConversion.convertEOL(command+"\r\nexit %ERRORLEVEL%",LineEndingConversion.EOLType.Windows); } protected String getFileExtension() { return ".bat"; } + private Object readResolve() throws ObjectStreamException { + return new BatchFile(command); + } + @Extension public static final class DescriptorImpl extends BuildStepDescriptor { @Override diff --git a/core/src/main/java/hudson/tasks/Shell.java b/core/src/main/java/hudson/tasks/Shell.java index 5a3a2053db..17ce0b8819 100644 --- a/core/src/main/java/hudson/tasks/Shell.java +++ b/core/src/main/java/hudson/tasks/Shell.java @@ -32,6 +32,8 @@ import hudson.remoting.Callable; import hudson.remoting.VirtualChannel; import hudson.util.FormValidation; import java.io.IOException; +import java.io.ObjectStreamException; +import hudson.util.LineEndingConversion; import net.sf.json.JSONObject; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerRequest; @@ -50,29 +52,7 @@ import java.util.logging.Logger; public class Shell extends CommandInterpreter { @DataBoundConstructor public Shell(String command) { - super(fixCrLf(command)); - } - - /** - * Fix CR/LF and always make it Unix style. - */ - private static String fixCrLf(String s) { - // eliminate CR - int idx; - while((idx=s.indexOf("\r\n"))!=-1) - s = s.substring(0,idx)+s.substring(idx+1); - - //// add CR back if this is for Windows - //if(isWindows()) { - // idx=0; - // while(true) { - // idx = s.indexOf('\n',idx); - // if(idx==-1) break; - // s = s.substring(0,idx)+'\r'+s.substring(idx); - // idx+=2; - // } - //} - return s; + super(LineEndingConversion.convertEOL(command, LineEndingConversion.EOLType.Unix)); } /** @@ -80,7 +60,7 @@ public class Shell extends CommandInterpreter { * makes the shell think the file is a binary file and not a script. Adding * a leading line feed works around this problem. */ - private static String addCrForNonASCII(String s) { + private static String addLineFeedForNonASCII(String s) { if(!s.startsWith("#!")) { if (s.indexOf('\n')!=0) { return "\n" + s; @@ -105,7 +85,7 @@ public class Shell extends CommandInterpreter { } protected String getContents() { - return addCrForNonASCII(fixCrLf(command)); + return addLineFeedForNonASCII(LineEndingConversion.convertEOL(command,LineEndingConversion.EOLType.Unix)); } protected String getFileExtension() { @@ -117,6 +97,10 @@ public class Shell extends CommandInterpreter { return (DescriptorImpl)super.getDescriptor(); } + private Object readResolve() throws ObjectStreamException { + return new Shell(command); + } + @Extension public static class DescriptorImpl extends BuildStepDescriptor { /** diff --git a/core/src/main/java/hudson/tools/AbstractCommandInstaller.java b/core/src/main/java/hudson/tools/AbstractCommandInstaller.java index 07d91c6c0c..18431fcda1 100644 --- a/core/src/main/java/hudson/tools/AbstractCommandInstaller.java +++ b/core/src/main/java/hudson/tools/AbstractCommandInstaller.java @@ -48,7 +48,7 @@ public abstract class AbstractCommandInstaller extends ToolInstaller { public AbstractCommandInstaller(String label, String command, String toolHome) { super(label); - this.command = fixCrLf(command); + this.command = command; this.toolHome = toolHome; } @@ -84,19 +84,6 @@ public abstract class AbstractCommandInstaller extends ToolInstaller { return dir.child(getToolHome()); } - /** - * Fix CR/LF and always make it Unix style. - */ - //TODO: replace by a Windows style - private static String fixCrLf(String s) { - // eliminate CR - int idx; - while ((idx = s.indexOf("\r\n")) != -1) { - s = s.substring(0, idx) + s.substring(idx + 1); - } - return s; - } - public static abstract class Descriptor extends ToolInstallerDescriptor { diff --git a/core/src/main/java/hudson/tools/BatchCommandInstaller.java b/core/src/main/java/hudson/tools/BatchCommandInstaller.java index 415dbb700f..2085fe7fa0 100644 --- a/core/src/main/java/hudson/tools/BatchCommandInstaller.java +++ b/core/src/main/java/hudson/tools/BatchCommandInstaller.java @@ -26,7 +26,9 @@ package hudson.tools; import hudson.Extension; import hudson.FilePath; +import hudson.util.LineEndingConversion; import org.kohsuke.stapler.DataBoundConstructor; +import java.io.ObjectStreamException; /** * Installs tool via script execution of Batch script. @@ -37,7 +39,7 @@ public class BatchCommandInstaller extends AbstractCommandInstaller { @DataBoundConstructor public BatchCommandInstaller(String label, String command, String toolHome) { - super(label, command, toolHome); + super(label, LineEndingConversion.convertEOL(command, LineEndingConversion.EOLType.Windows), toolHome); } @Override @@ -50,6 +52,10 @@ public class BatchCommandInstaller extends AbstractCommandInstaller { String[] cmd = {"cmd", "/c", "call", script.getRemote()}; return cmd; } + + private Object readResolve() throws ObjectStreamException { + return new BatchCommandInstaller(getLabel(), getCommand(), getToolHome()); + } @Extension public static class DescriptorImpl extends Descriptor { diff --git a/core/src/main/java/hudson/tools/CommandInstaller.java b/core/src/main/java/hudson/tools/CommandInstaller.java index b02137f0f5..6f2506dd04 100644 --- a/core/src/main/java/hudson/tools/CommandInstaller.java +++ b/core/src/main/java/hudson/tools/CommandInstaller.java @@ -26,7 +26,9 @@ package hudson.tools; import hudson.Extension; import hudson.FilePath; +import hudson.util.LineEndingConversion; import org.kohsuke.stapler.DataBoundConstructor; +import java.io.ObjectStreamException; /** * Installs a tool by running an arbitrary shell command. @@ -36,7 +38,7 @@ public class CommandInstaller extends AbstractCommandInstaller { @DataBoundConstructor public CommandInstaller(String label, String command, String toolHome) { - super(label, command, toolHome); + super(label, LineEndingConversion.convertEOL(command, LineEndingConversion.EOLType.Unix), toolHome); } @Override @@ -50,6 +52,10 @@ public class CommandInstaller extends AbstractCommandInstaller { return cmd; } + private Object readResolve() throws ObjectStreamException { + return new CommandInstaller(getLabel(), getCommand(), getToolHome()); + } + @Extension public static class DescriptorImpl extends Descriptor { diff --git a/core/src/main/java/hudson/util/LineEndingConversion.java b/core/src/main/java/hudson/util/LineEndingConversion.java new file mode 100644 index 0000000000..744ff4a4ea --- /dev/null +++ b/core/src/main/java/hudson/util/LineEndingConversion.java @@ -0,0 +1,66 @@ +package hudson.util; + +/** + * Converts line endings of a string. + * + * @since TODO + * @author David Ruhmann + */ +public class LineEndingConversion { + + /** + * Supported line ending types for conversion + */ + public enum EOLType { + CR, + CRLF, + LF, + LFCR, + Mac, + Unix, + Windows + } + + /** + * Convert line endings of a string to the given type. Default to Unix type. + * + * @param input + * The string containing line endings to be converted. + * @param type + * Type of line endings to convert the string into. + * @return + * String updated with the new line endings or null if given null. + */ + public static String convertEOL(String input, EOLType type) { + if (null == input || 0 == input.length()) { + return input; + } + // Convert line endings to Unix LF, + // which also sets up the string for other conversions + input = input.replace("\r\n","\n"); + input = input.replace("\r","\n"); + switch (type) { + case CR: + case Mac: + // Convert line endings to CR + input = input.replace("\n", "\r"); + break; + case CRLF: + case Windows: + // Convert line endings to Windows CR/LF + input = input.replace("\n", "\r\n"); + break; + default: + case LF: + case Unix: + // Conversion already completed + return input; + case LFCR: + // Convert line endings to LF/CR + input = input.replace("\n", "\n\r"); + break; + } + return input; + } +} + diff --git a/test/src/test/java/hudson/tasks/BatchFileTest.java b/test/src/test/java/hudson/tasks/BatchFileTest.java new file mode 100644 index 0000000000..8cc16fd10a --- /dev/null +++ b/test/src/test/java/hudson/tasks/BatchFileTest.java @@ -0,0 +1,30 @@ +package hudson.tasks; + +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.Issue; +import org.jvnet.hudson.test.JenkinsRule; + +/** + * Tests for the BatchFile tasks class. + * + * @author David Ruhmann + */ +public class BatchFileTest { + + @Rule + public JenkinsRule rule = new JenkinsRule(); + + @Issue("JENKINS-7478") + @Test + public void validateBatchFileCommandEOL() throws Exception { + BatchFile obj = new BatchFile("echo A\necho B\recho C"); + rule.assertStringContains(obj.getCommand(), "echo A\r\necho B\r\necho C"); + } + + @Test + public void validateBatchFileContents() throws Exception { + BatchFile obj = new BatchFile("echo A\necho B\recho C"); + rule.assertStringContains(obj.getContents(), "echo A\r\necho B\r\necho C\r\nexit %ERRORLEVEL%"); + } +} diff --git a/test/src/test/java/hudson/tasks/ShellTest.java b/test/src/test/java/hudson/tasks/ShellTest.java index e1e0d7d867..8aadb4f4ba 100644 --- a/test/src/test/java/hudson/tasks/ShellTest.java +++ b/test/src/test/java/hudson/tasks/ShellTest.java @@ -7,7 +7,6 @@ import hudson.model.FreeStyleBuild; import hudson.model.FreeStyleProject; import org.apache.commons.io.FileUtils; import org.jvnet.hudson.test.FakeLauncher; -import org.jvnet.hudson.test.HudsonTestCase; import org.jvnet.hudson.test.PretendSlave; import java.io.File; @@ -15,23 +14,47 @@ import java.io.IOException; import java.io.PrintStream; import java.util.List; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.JenkinsRule; + /** + * Tests for the Shell tasks class + * * @author Kohsuke Kawaguchi */ -public class ShellTest extends HudsonTestCase { +public class ShellTest { + + @Rule + public JenkinsRule rule = new JenkinsRule(); + + @Test + public void validateShellCommandEOL() throws Exception { + Shell obj = new Shell("echo A\r\necho B\recho C"); + rule.assertStringContains(obj.getCommand(), "echo A\necho B\necho C"); + } + + @Test + public void validateShellContents() throws Exception { + Shell obj = new Shell("echo A\r\necho B\recho C"); + rule.assertStringContains(obj.getContents(), "\necho A\necho B\necho C"); + } + + @Test public void testBasic() throws Exception { // If we're on Windows, don't bother doing this. if (Functions.isWindows()) return; // TODO: define a FakeLauncher implementation with easymock so that this kind of assertions can be simplified. - PretendSlave s = createPretendSlave(new FakeLauncher() { + PretendSlave s = rule.createPretendSlave(new FakeLauncher() { public Proc onLaunch(ProcStarter p) throws IOException { // test the command line argument. List cmds = p.cmds(); - assertEquals("/bin/sh",cmds.get(0)); - assertEquals("-xe",cmds.get(1)); - assertTrue(new File(cmds.get(2)).exists()); + rule.assertStringContains("/bin/sh",cmds.get(0)); + rule.assertStringContains("-xe",cmds.get(1)); + Assert.assertTrue(new File(cmds.get(2)).exists()); // fake the execution PrintStream ps = new PrintStream(p.stdout()); @@ -41,13 +64,13 @@ public class ShellTest extends HudsonTestCase { return new FinishedProc(0); } }); - FreeStyleProject p = createFreeStyleProject(); + FreeStyleProject p = rule.createFreeStyleProject(); p.getBuildersList().add(new Shell("echo abc")); p.setAssignedNode(s); - FreeStyleBuild b = assertBuildStatusSuccess(p.scheduleBuild2(0).get()); + FreeStyleBuild b = rule.assertBuildStatusSuccess(p.scheduleBuild2(0).get()); - assertEquals(1,s.numLaunch); - assertTrue(FileUtils.readFileToString(b.getLogFile()).contains("Hudson was here")); + Assert.assertEquals(1,s.numLaunch); + Assert.assertTrue(FileUtils.readFileToString(b.getLogFile()).contains("Hudson was here")); } } diff --git a/test/src/test/java/hudson/tools/BatchCommandInstallerTest.java b/test/src/test/java/hudson/tools/BatchCommandInstallerTest.java new file mode 100644 index 0000000000..e14a90b405 --- /dev/null +++ b/test/src/test/java/hudson/tools/BatchCommandInstallerTest.java @@ -0,0 +1,22 @@ +package hudson.tools; + +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.JenkinsRule; + +/** + * Tests for the BatchCommandInstaller tools class. + * + * @author David Ruhmann + */ +public class BatchCommandInstallerTest { + + @Rule + public JenkinsRule rule = new JenkinsRule(); + + @Test + public void validateBatchCommandInstallerCommandEOL() throws Exception { + BatchCommandInstaller obj = new BatchCommandInstaller("", "echo A\necho B\recho C", ""); + rule.assertStringContains(obj.getCommand(), "echo A\r\necho B\r\necho C"); + } +} diff --git a/test/src/test/java/hudson/tools/CommandInstallerTest.java b/test/src/test/java/hudson/tools/CommandInstallerTest.java new file mode 100644 index 0000000000..7a58c1ec8c --- /dev/null +++ b/test/src/test/java/hudson/tools/CommandInstallerTest.java @@ -0,0 +1,22 @@ +package hudson.tools; + +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.JenkinsRule; + +/** + * Tests for the CommandInstaller tools class. + * + * @author David Ruhmann + */ +public class CommandInstallerTest { + + @Rule + public JenkinsRule rule = new JenkinsRule(); + + @Test + public void validateCommandInstallerCommandEOL() throws Exception { + CommandInstaller obj = new CommandInstaller("", "echo A\r\necho B\recho C", ""); + rule.assertStringContains(obj.getCommand(), "echo A\necho B\necho C"); + } +} diff --git a/test/src/test/java/hudson/util/LineEndingConversionTest.java b/test/src/test/java/hudson/util/LineEndingConversionTest.java new file mode 100644 index 0000000000..66773fbbb9 --- /dev/null +++ b/test/src/test/java/hudson/util/LineEndingConversionTest.java @@ -0,0 +1,28 @@ +package hudson.util; + +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.Issue; +import org.jvnet.hudson.test.JenkinsRule; + +/** + * Tests for the LineEndingConversion util class. + * + * @author David Ruhmann + */ +public class LineEndingConversionTest { + + @Rule + public JenkinsRule rule = new JenkinsRule(); + + @Issue("JENKINS-7478") + @Test + public void validateWindowsEOL() throws Exception { + rule.assertStringContains(LineEndingConversion.convertEOL("echo A\necho B\recho C", LineEndingConversion.EOLType.Windows), "echo A\r\necho B\r\necho C"); + } + + @Test + public void validateUnixEOL() throws Exception { + rule.assertStringContains(LineEndingConversion.convertEOL("echo A\r\necho B\recho C", LineEndingConversion.EOLType.Unix), "echo A\necho B\necho C"); + } +} From 20473dab36e88fca4f7826a61346a4e3a043f1ac Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Wed, 17 Sep 2014 10:01:13 -0400 Subject: [PATCH 006/272] @DataBoundConstructor makes no sense on an abstract class. (cherry picked from commit 71600d8b37581ea367937e0cda6dbc31b13c6904) --- core/src/main/java/hudson/model/Slave.java | 2 -- .../java/hudson/node_monitors/AbstractDiskSpaceMonitor.java | 2 -- 2 files changed, 4 deletions(-) diff --git a/core/src/main/java/hudson/model/Slave.java b/core/src/main/java/hudson/model/Slave.java index d29ed99a13..625cea4b05 100644 --- a/core/src/main/java/hudson/model/Slave.java +++ b/core/src/main/java/hudson/model/Slave.java @@ -62,7 +62,6 @@ import jenkins.model.Jenkins; import jenkins.slaves.WorkspaceLocator; import org.apache.commons.io.IOUtils; -import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.HttpResponse; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; @@ -134,7 +133,6 @@ public abstract class Slave extends Node implements Serializable { */ private String userId; - @DataBoundConstructor public Slave(String name, String nodeDescription, String remoteFS, String numExecutors, Mode mode, String labelString, ComputerLauncher launcher, RetentionStrategy retentionStrategy, List> nodeProperties) throws FormException, IOException { this(name,nodeDescription,remoteFS,Util.tryParseNumber(numExecutors, 1).intValue(),mode,labelString,launcher,retentionStrategy, nodeProperties); diff --git a/core/src/main/java/hudson/node_monitors/AbstractDiskSpaceMonitor.java b/core/src/main/java/hudson/node_monitors/AbstractDiskSpaceMonitor.java index be90b91d0e..384133b937 100644 --- a/core/src/main/java/hudson/node_monitors/AbstractDiskSpaceMonitor.java +++ b/core/src/main/java/hudson/node_monitors/AbstractDiskSpaceMonitor.java @@ -5,7 +5,6 @@ import hudson.node_monitors.DiskSpaceMonitorDescriptor.DiskSpace; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; -import org.kohsuke.stapler.DataBoundConstructor; import java.text.ParseException; import java.util.logging.Logger; @@ -21,7 +20,6 @@ public abstract class AbstractDiskSpaceMonitor extends NodeMonitor { */ public final String freeSpaceThreshold; - @DataBoundConstructor public AbstractDiskSpaceMonitor(String threshold) throws ParseException { this.freeSpaceThreshold = threshold; DiskSpace.parse(threshold); // make sure it parses From 21fb398bfa7068f6f72154763bdbb09baddf313d Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Mon, 22 Sep 2014 18:09:26 -0700 Subject: [PATCH 007/272] [JENKINS-24453][ZD-20503] Incorporated a newer version of winp. This version checks the critical flag of the process, as killing such a process results in BSoD. (cherry picked from commit 040b7d4047b324f17c0971f2511f954eb173fa23) Conflicts: changelog.html --- core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/pom.xml b/core/pom.xml index 0b4e47b806..214de797f6 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -484,7 +484,7 @@ THE SOFTWARE. org.jvnet.winp winp - 1.21 + 1.22 org.jenkins-ci From 66dce647d1d7df919f9707b4ce631cd49dc242f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20Gond=C5=BEa?= Date: Mon, 13 Oct 2014 22:02:57 +0200 Subject: [PATCH 008/272] [JENKINS-11543] Unit test (cherry picked from commit 75f5d2180f7340ce2efd4f41c8670afb049eb530) --- .../java/hudson/model/ParametersTest.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/src/test/java/hudson/model/ParametersTest.java b/test/src/test/java/hudson/model/ParametersTest.java index 0cc4044942..3b5610cdcb 100644 --- a/test/src/test/java/hudson/model/ParametersTest.java +++ b/test/src/test/java/hudson/model/ParametersTest.java @@ -1,13 +1,20 @@ package hudson.model; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; + +import org.junit.Test; +import org.jvnet.hudson.test.Bug; import org.jvnet.hudson.test.HudsonTestCase; import org.jvnet.hudson.test.CaptureEnvironmentBuilder; + import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.gargoylesoftware.htmlunit.html.HtmlForm; import com.gargoylesoftware.htmlunit.html.HtmlElement; import com.gargoylesoftware.htmlunit.html.HtmlTextInput; import com.gargoylesoftware.htmlunit.html.HtmlCheckBoxInput; import com.gargoylesoftware.htmlunit.html.HtmlOption; + import java.util.Set; /** @@ -157,4 +164,23 @@ public class ParametersTest extends HudsonTestCase { assertTrue(sensitiveVars.contains("password")); assertFalse(sensitiveVars.contains("string2")); } + + @Bug(11543) + public void testUnicodeParametersArePresetedCorrectly() throws Exception { + final FreeStyleProject p = createFreeStyleProject(); + ParametersDefinitionProperty pdb = new ParametersDefinitionProperty( + new StringParameterDefinition("sname:a¶‱ﻷ", "svalue:a¶‱ﻷ", "sdesc:a¶‱ﻷ"), + new FileParameterDefinition("fname:a¶‱ﻷ", "fdesc:a¶‱ﻷ") + ); + p.addProperty(pdb); + + WebClient wc = createWebClient(); + wc.setThrowExceptionOnFailingStatusCode(false); // Ignore 405 + HtmlPage page = wc.getPage(p, "build"); + + // java.lang.IllegalArgumentException: No such parameter definition: . + wc.setThrowExceptionOnFailingStatusCode(true); + final HtmlForm form = page.getFormByName("parameters"); + form.submit(form.getButtonByCaption("Build")); + } } From 96b973c4082569d4ed386aca5edf21205bfb7314 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Wed, 8 Oct 2014 13:46:33 -0700 Subject: [PATCH 009/272] Integrated the new version. This version includes a change from christ66 in https://github.com/stapler/stapler/pull/40 (cherry picked from commit 65c2d378814ebd3fe8c43f428a1408dd6354c9bb) Conflicts: core/pom.xml --- core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/pom.xml b/core/pom.xml index 214de797f6..fe7fcf64ee 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -42,7 +42,7 @@ THE SOFTWARE. true - 1.229 + 1.231 2.5.6.SEC03 1.8.9 From 69c1492fa09c89456d69a804d2560c443405b662 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Thu, 16 Oct 2014 19:41:26 -0400 Subject: [PATCH 010/272] maven-plugin SECURITY-144 is now 2.7.1-SNAPSHOT. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7649134c7b..30f92d3fc6 100644 --- a/pom.xml +++ b/pom.xml @@ -95,7 +95,7 @@ THE SOFTWARE. jenkins-jira 1.7.7 - 2.6.1-SNAPSHOT + 2.7.1-SNAPSHOT ${skipTests} 6 From 69a4969a988f993eebbfecb65c3e1dfb13fcfdb1 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Fri, 17 Oct 2014 07:40:47 -0400 Subject: [PATCH 011/272] b105627 failed to move completely away from 1.580. --- plugins/pom.xml | 9 ++++----- pom.xml | 1 - 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/plugins/pom.xml b/plugins/pom.xml index 14e8099395..fba5031f78 100644 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -11,7 +11,7 @@ org.jenkins-ci.plugins plugin Jenkins plugin POM - 1.580 + 1.580.1-SNAPSHOT pom org.jenkins-ci winstone - 2.3 + 2.8 test From e0952e94ea7ae5cdc072a579ad13216df2cfb56c Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Wed, 29 Oct 2014 17:43:29 -0700 Subject: [PATCH 013/272] Switch to a released version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b4dc341c62..eaf120d3df 100644 --- a/pom.xml +++ b/pom.xml @@ -172,7 +172,7 @@ THE SOFTWARE. org.jenkins-ci.main remoting - 2.47-SNAPSHOT + 2.47 From 388fa0b44a56cdc1d56596ab5b6415da8e1de04f Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Wed, 29 Oct 2014 21:06:06 -0700 Subject: [PATCH 014/272] [maven-release-plugin] prepare release jenkins-1.580.1 --- cli/pom.xml | 2 +- core/pom.xml | 2 +- plugins/pom.xml | 9 +++++---- pom.xml | 3 ++- test/pom.xml | 2 +- war/pom.xml | 2 +- 6 files changed, 11 insertions(+), 9 deletions(-) diff --git a/cli/pom.xml b/cli/pom.xml index 1246ee67ac..1926e5a75e 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ pom org.jenkins-ci.main - 1.580.1-SNAPSHOT + 1.580.1 cli diff --git a/core/pom.xml b/core/pom.xml index a676e18c9e..8043fe488c 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 1.580.1-SNAPSHOT + 1.580.1 ../pom.xml diff --git a/plugins/pom.xml b/plugins/pom.xml index fba5031f78..ab79ca6f2e 100644 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -11,7 +11,7 @@ org.jenkins-ci.plugins plugin Jenkins plugin POM - 1.580.1-SNAPSHOT + 1.580.1 pom + + + + + Generates an anchor element with the ability to send POST requests and/or + asynchronous requests. It depends the combination of post and async attributes. + + @since TODO + + Additional CSS classes. + + + Link destination URL. + + + If this must send a POST request. + + + If this must send an asynchronous request. + + + + + + + diff --git a/core/src/main/resources/lib/form/link/link.js b/core/src/main/resources/lib/form/link/link.js new file mode 100644 index 0000000000..bb6e332115 --- /dev/null +++ b/core/src/main/resources/lib/form/link/link.js @@ -0,0 +1,27 @@ +Behaviour.specify('A.post', 'link.post', 0, function(element) { + element.onclick = function(evt) { + var form = document.createElement('form'); + form.setAttribute('method', 'POST'); + form.setAttribute('action', element.getAttribute('href')); + crumb.appendToForm(form); + document.body.appendChild(form); + form.submit(); + return false; + } +}); + +Behaviour.specify('A.post-async', 'link.post-async', 0, function(element) { + element.onclick = function(evt) { + new Ajax.Request(element.getAttribute('href')); + return false; + } +}); + +Behaviour.specify('A.async', 'link.async', 0, function(element) { + element.onclick = function(evt) { + new Ajax.Request(element.getAttribute('href'), { + method : 'get' + }); + return false; + } +}); diff --git a/core/src/main/resources/lib/hudson/queue.jelly b/core/src/main/resources/lib/hudson/queue.jelly index 1b9996a9e5..2a48bf96a8 100644 --- a/core/src/main/resources/lib/hudson/queue.jelly +++ b/core/src/main/resources/lib/hudson/queue.jelly @@ -51,7 +51,7 @@ THE SOFTWARE. ${%Jenkins is going to shut down. No further builds will be performed.} - (${%cancel}) + (${%cancel}) From fff888cc822f235dde8ebdd01152c626e85a12b2 Mon Sep 17 00:00:00 2001 From: Oleg Nenashev Date: Sun, 28 Sep 2014 18:11:57 +0400 Subject: [PATCH 023/272] [FIXED JENKINS-19017] - FileParameter - Handle non-null file parameters as different values The change prevents the issue when Jenkins merges builds with different files coming from one source (It may happen!). Signed-off-by: Oleg Nenashev (cherry picked from commit 7c4f6b7ef433f4a3d85707096f9e5f8b366f232a) --- .../java/hudson/model/FileParameterValue.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/hudson/model/FileParameterValue.java b/core/src/main/java/hudson/model/FileParameterValue.java index c9303fabe5..6669895bc4 100644 --- a/core/src/main/java/hudson/model/FileParameterValue.java +++ b/core/src/main/java/hudson/model/FileParameterValue.java @@ -162,7 +162,8 @@ public class FileParameterValue extends ParameterValue { } /** - * In practice this will always be false, since location should be unique. + * Compares file parameters (existing files will be considered as different). + * Function has been modified in order to avoid JENKINS-19017 issue (wrong merge of builds in the queue). */ @Override public boolean equals(Object obj) { @@ -173,12 +174,13 @@ public class FileParameterValue extends ParameterValue { if (getClass() != obj.getClass()) return false; FileParameterValue other = (FileParameterValue) obj; - if (location == null) { - if (other.location != null) - return false; - } else if (!location.equals(other.location)) - return false; - return true; + + if (location == null && other.location == null) + return true; // Consider null parameters as equal + + //TODO: check fingerprints or checksums to improve the behavior + // Return false even if files are equal + return false; } @Override From be835bfcfb172ecde489ad3598750b7fdc88a965 Mon Sep 17 00:00:00 2001 From: Oleg Nenashev Date: Sat, 18 Oct 2014 22:03:28 +0400 Subject: [PATCH 024/272] [JENKINS-19017] - Unit tests Signed-off-by: Oleg Nenashev (cherry picked from commit cc06ae78e3955746176f64ff3f36d7f0e3cc80ab) --- .../hudson/model/FileParameterValueTest.java | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 core/src/test/java/hudson/model/FileParameterValueTest.java diff --git a/core/src/test/java/hudson/model/FileParameterValueTest.java b/core/src/test/java/hudson/model/FileParameterValueTest.java new file mode 100644 index 0000000000..cd03b68409 --- /dev/null +++ b/core/src/test/java/hudson/model/FileParameterValueTest.java @@ -0,0 +1,64 @@ +/* + * The MIT License + * + * Copyright 2014 Oleg Nenashev . + * + * 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 java.io.File; +import org.junit.Assert; +import org.junit.Test; +import org.jvnet.hudson.test.Issue; + +/** + * Test for {@link FileParameterValue}. + * @author Oleg Nenashev + */ +public class FileParameterValueTest { + + @Issue("JENKINS-19017") + @Test public void compareParamsWithSameName() { + final String paramName = "MY_FILE_PARAM"; // Same paramName (location) reproduces the bug + final FileParameterValue param1 = new FileParameterValue(paramName, new File("ws_param1.txt"), "param1.txt"); + final FileParameterValue param2 = new FileParameterValue(paramName, new File("ws_param2.txt"), "param2.txt"); + + Assert.assertNotEquals("Files with same locations shoud be considered as different", param1, param2); + Assert.assertNotEquals("Files with same locations shoud be considered as different", param2, param1); + } + + @Test public void compareNullParams() { + final String paramName = "MY_FILE_PARAM"; + FileParameterValue nonNullParam = new FileParameterValue(paramName, new File("ws_param1.txt"), "param1.txt"); + FileParameterValue nullParam1 = new FileParameterValue(null, new File("null_param1.txt"), "null_param1.txt"); + FileParameterValue nullParam2 = new FileParameterValue(null, new File("null_param2.txt"), "null_param2.txt"); + + // Combine nulls + Assert.assertEquals(nullParam1, nullParam1); + Assert.assertEquals(nullParam1, nullParam2); + Assert.assertEquals(nullParam2, nullParam1); + Assert.assertEquals(nullParam2, nullParam2); + + // Compare with non-null + Assert.assertNotEquals(nullParam1, nonNullParam); + Assert.assertNotEquals(nonNullParam, nullParam1); + } +} From 8fbd44db2b2ebf81947a6c3c5861bc30b48cc775 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20Gond=C5=BEa?= Date: Sat, 8 Nov 2014 11:12:30 +0100 Subject: [PATCH 025/272] Revert "[FIXED JENKINS-20108]" This reverts commit 68eb26633b1c042d90a426efac7b6e60dd4b2ede. --- core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/pom.xml b/core/pom.xml index 59610a927c..22f4764fa0 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -117,7 +117,7 @@ THE SOFTWARE. org.jenkins-ci trilead-ssh2 - build217-jenkins-7 + build217-jenkins-5 org.kohsuke.stapler From 0efd811adb89769c8a6180e33e3d06d755bca4b5 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Wed, 24 Sep 2014 11:06:00 -0400 Subject: [PATCH 026/272] [FIXED JENKINS-22769] ItemListener callbacks should run as SYSTEM since they sometimes do ACL-checked calls. (cherry picked from commit c04cdcd9f717ddcd3e8c9dbe86cb353c14ae511e) Conflicts: changelog.html --- .../hudson/model/listeners/ItemListener.java | 56 ++++++++++++++----- .../test/java/hudson/model/ListViewTest.java | 42 ++++++++++++++ 2 files changed, 83 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/hudson/model/listeners/ItemListener.java b/core/src/main/java/hudson/model/listeners/ItemListener.java index b1787f4b47..91a55c23eb 100644 --- a/core/src/main/java/hudson/model/listeners/ItemListener.java +++ b/core/src/main/java/hudson/model/listeners/ItemListener.java @@ -26,10 +26,10 @@ package hudson.model.listeners; import hudson.ExtensionPoint; import hudson.ExtensionList; import hudson.Extension; -import jenkins.model.Jenkins; import hudson.model.Item; import hudson.model.ItemGroup; import hudson.model.Items; +import hudson.security.ACL; /** * Receives notifications about CRUD operations of {@link Item}. @@ -151,26 +151,45 @@ public class ItemListener implements ExtensionPoint { return ExtensionList.lookup(ItemListener.class); } - public static void fireOnCopied(Item src, Item result) { - for (ItemListener l : all()) - l.onCopied(src,result); + public static void fireOnCopied(final Item src, final Item result) { + ACL.impersonate(ACL.SYSTEM, new Runnable() { + @Override public void run() { + for (ItemListener l : all()) { + l.onCopied(src, result); + } + } + }); } - public static void fireOnCreated(Item item) { - for (ItemListener l : all()) - l.onCreated(item); + public static void fireOnCreated(final Item item) { + ACL.impersonate(ACL.SYSTEM, new Runnable() { + @Override public void run() { + for (ItemListener l : all()) { + l.onCreated(item); + } + } + }); } - public static void fireOnUpdated(Item item) { - for (ItemListener l : all()) - l.onUpdated(item); + public static void fireOnUpdated(final Item item) { + ACL.impersonate(ACL.SYSTEM, new Runnable() { + @Override public void run() { + for (ItemListener l : all()) { + l.onUpdated(item); + } + } + }); } /** @since 1.548 */ - public static void fireOnDeleted(Item item) { - for (ItemListener l : all()) { - l.onDeleted(item); - } + public static void fireOnDeleted(final Item item) { + ACL.impersonate(ACL.SYSTEM, new Runnable() { + @Override public void run() { + for (ItemListener l : all()) { + l.onDeleted(item); + } + } + }); } /** @@ -179,7 +198,14 @@ public class ItemListener implements ExtensionPoint { * @param oldFullName the previous {@link Item#getFullName} * @since 1.548 */ - public static void fireLocationChange(Item rootItem, String oldFullName) { + public static void fireLocationChange(final Item rootItem, final String oldFullName) { + ACL.impersonate(ACL.SYSTEM, new Runnable() { + @Override public void run() { + doFireLocationChange(rootItem, oldFullName); + } + }); + } + private static void doFireLocationChange(Item rootItem, String oldFullName) { String prefix = rootItem.getParent().getFullName(); if (!prefix.isEmpty()) { prefix += '/'; diff --git a/test/src/test/java/hudson/model/ListViewTest.java b/test/src/test/java/hudson/model/ListViewTest.java index 6bcb51715d..a4d9a06049 100644 --- a/test/src/test/java/hudson/model/ListViewTest.java +++ b/test/src/test/java/hudson/model/ListViewTest.java @@ -31,17 +31,23 @@ import hudson.Functions; import hudson.matrix.AxisList; import hudson.matrix.MatrixProject; import hudson.matrix.TextAxis; +import hudson.security.ACL; +import hudson.security.AuthorizationStrategy; +import hudson.security.Permission; import java.io.IOException; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashSet; +import org.acegisecurity.Authentication; import static org.junit.Assert.*; import org.junit.Rule; import org.junit.Test; import org.jvnet.hudson.test.Bug; +import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.JenkinsRule; import org.jvnet.hudson.test.JenkinsRule.WebClient; import org.jvnet.hudson.test.MockFolder; @@ -205,4 +211,40 @@ public class ListViewTest { assertFalse(view.contains(job)); assertFalse(view.jobNamesContains(job)); } + + @Issue("JENKINS-22769") + @Test public void renameJobInViewYouCannotSee() throws Exception { + j.jenkins.setSecurityRealm(j.createDummySecurityRealm()); + j.jenkins.setAuthorizationStrategy(new AllButViewsAuthorizationStrategy()); + final FreeStyleProject p = j.createFreeStyleProject("p1"); + ListView v = new ListView("v", j.jenkins); + v.add(p); + j.jenkins.addView(v); + ACL.impersonate(User.get("alice").impersonate(), new Runnable() { + @Override public void run() { + try { + p.renameTo("p2"); + } catch (IOException x) { + throw new RuntimeException(x); + } + } + }); + assertEquals(Collections.singletonList(p), v.getItems()); + } + private static class AllButViewsAuthorizationStrategy extends AuthorizationStrategy { + @Override public ACL getRootACL() { + return UNSECURED.getRootACL(); + } + @Override public Collection getGroups() { + return Collections.emptyList(); + } + @Override public ACL getACL(View item) { + return new ACL() { + @Override public boolean hasPermission(Authentication a, Permission permission) { + return a.equals(SYSTEM); + } + }; + } + } + } From b70313476a86813417b9197555780622cf5170b8 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Mon, 6 Oct 2014 10:18:16 -0400 Subject: [PATCH 027/272] =?UTF-8?q?[JENKINS-21224]=20Defend=20against=20ex?= =?UTF-8?q?ceptions=20in=20listeners,=20for=20now=20just=20for=20ItemListe?= =?UTF-8?q?ner.=20java.lang.RuntimeException:=20Unable=20to=20copy=20/?= =?UTF-8?q?=E2=80=A6/jobs/=E2=80=A6/config.xml=20=09at=20hudson.plugins.jo?= =?UTF-8?q?bConfigHistory.FileHistoryDao.createNewHistoryEntryAndCopyConfi?= =?UTF-8?q?g(FileHistoryDao.java:252)=20=09at=20hudson.plugins.jobConfigHi?= =?UTF-8?q?story.FileHistoryDao.createNewItem(FileHistoryDao.java:238)=20?= =?UTF-8?q?=09at=20hudson.plugins.jobConfigHistory.JobConfigHistoryJobList?= =?UTF-8?q?ener.onCreated(JobConfigHistoryJobListener.java:31)=20=09at=20h?= =?UTF-8?q?udson.model.listeners.ItemListener.fireOnCreated(ItemListener.j?= =?UTF-8?q?ava:161)=20=09at=20jenkins.model.Jenkins.putItem(Jenkins.java:2?= =?UTF-8?q?483)=20=09at=20=E2=80=A6=20Caused=20by:=20java.io.FileNotFoundE?= =?UTF-8?q?xception:=20/=E2=80=A6/jobs/=E2=80=A6/config.xml=20(No=20such?= =?UTF-8?q?=20file=20or=20directory)=20=09at=20java.io.FileInputStream.ope?= =?UTF-8?q?n(Native=20Method)=20=09at=20java.io.FileInputStream.(Fil?= =?UTF-8?q?eInputStream.java:120)=20=09at=20hudson.plugins.jobConfigHistor?= =?UTF-8?q?y.FileHistoryDao.copyConfigFile(FileHistoryDao.java:178)=20=09a?= =?UTF-8?q?t=20hudson.plugins.jobConfigHistory.FileHistoryDao.createNewHis?= =?UTF-8?q?toryEntryAndCopyConfig(FileHistoryDao.java:250)=20=09...=20108?= =?UTF-8?q?=20more=20(cherry=20picked=20from=20commit=206825121f06d6427304?= =?UTF-8?q?fd5149531216ecae6fdf93)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hudson/model/listeners/ItemListener.java | 107 +++++++++++------- 1 file changed, 69 insertions(+), 38 deletions(-) diff --git a/core/src/main/java/hudson/model/listeners/ItemListener.java b/core/src/main/java/hudson/model/listeners/ItemListener.java index 91a55c23eb..3babd001a9 100644 --- a/core/src/main/java/hudson/model/listeners/ItemListener.java +++ b/core/src/main/java/hudson/model/listeners/ItemListener.java @@ -23,6 +23,7 @@ */ package hudson.model.listeners; +import com.google.common.base.Function; import hudson.ExtensionPoint; import hudson.ExtensionList; import hudson.Extension; @@ -30,6 +31,8 @@ import hudson.model.Item; import hudson.model.ItemGroup; import hudson.model.Items; import hudson.security.ACL; +import java.util.logging.Level; +import java.util.logging.Logger; /** * Receives notifications about CRUD operations of {@link Item}. @@ -38,6 +41,9 @@ import hudson.security.ACL; * @author Kohsuke Kawaguchi */ public class ItemListener implements ExtensionPoint { + + private static final Logger LOGGER = Logger.getLogger(ItemListener.class.getName()); + /** * Called after a new job is created and added to {@link jenkins.model.Jenkins}, * before the initial configuration page is provided. @@ -151,45 +157,61 @@ public class ItemListener implements ExtensionPoint { return ExtensionList.lookup(ItemListener.class); } - public static void fireOnCopied(final Item src, final Item result) { - ACL.impersonate(ACL.SYSTEM, new Runnable() { + // TODO JENKINS-21224 generalize this to a method perhaps in ExtensionList and use consistently from all listeners + private static void forAll(final /* java.util.function.Consumer */Function consumer, boolean asSystem) { + Runnable r = new Runnable() { @Override public void run() { for (ItemListener l : all()) { - l.onCopied(src, result); + try { + consumer.apply(l); + } catch (RuntimeException x) { + LOGGER.log(Level.WARNING, "failed to send event to listener of " + l.getClass(), x); + } } } - }); + }; + if (asSystem) { + ACL.impersonate(ACL.SYSTEM, r); + } else { + r.run(); + } + } + + public static void fireOnCopied(final Item src, final Item result) { + forAll(new Function() { + @Override public Void apply(ItemListener l) { + l.onCopied(src, result); + return null; + } + }, true); } public static void fireOnCreated(final Item item) { - ACL.impersonate(ACL.SYSTEM, new Runnable() { - @Override public void run() { - for (ItemListener l : all()) { - l.onCreated(item); - } + forAll(new Function() { + @Override public Void apply(ItemListener l) { + l.onCreated(item); + return null; } - }); + }, true); } public static void fireOnUpdated(final Item item) { - ACL.impersonate(ACL.SYSTEM, new Runnable() { - @Override public void run() { - for (ItemListener l : all()) { - l.onUpdated(item); - } + forAll(new Function() { + @Override public Void apply(ItemListener l) { + l.onUpdated(item); + return null; } - }); + }, true); } /** @since 1.548 */ public static void fireOnDeleted(final Item item) { - ACL.impersonate(ACL.SYSTEM, new Runnable() { - @Override public void run() { - for (ItemListener l : all()) { - l.onDeleted(item); - } + forAll(new Function() { + @Override public Void apply(ItemListener l) { + l.onDeleted(item); + return null; } - }); + }, true); } /** @@ -205,34 +227,43 @@ public class ItemListener implements ExtensionPoint { } }); } - private static void doFireLocationChange(Item rootItem, String oldFullName) { + private static void doFireLocationChange(final Item rootItem, final String oldFullName) { String prefix = rootItem.getParent().getFullName(); if (!prefix.isEmpty()) { prefix += '/'; } - String newFullName = rootItem.getFullName(); + final String newFullName = rootItem.getFullName(); assert newFullName.startsWith(prefix); int prefixS = prefix.length(); if (oldFullName.startsWith(prefix) && oldFullName.indexOf('/', prefixS) == -1) { - String oldName = oldFullName.substring(prefixS); - String newName = rootItem.getName(); + final String oldName = oldFullName.substring(prefixS); + final String newName = rootItem.getName(); assert newName.equals(newFullName.substring(prefixS)); - for (ItemListener l : all()) { - l.onRenamed(rootItem, oldName, newName); + forAll(new Function() { + @Override public Void apply(ItemListener l) { + l.onRenamed(rootItem, oldName, newName); + return null; + } + }, false); + } + forAll(new Function() { + @Override public Void apply(ItemListener l) { + l.onLocationChanged(rootItem, oldFullName, newFullName); + return null; } - } - for (ItemListener l : all()) { - l.onLocationChanged(rootItem, oldFullName, newFullName); - } + }, false); if (rootItem instanceof ItemGroup) { - for (Item child : Items.getAllItems((ItemGroup) rootItem, Item.class)) { - String childNew = child.getFullName(); + for (final Item child : Items.getAllItems((ItemGroup) rootItem, Item.class)) { + final String childNew = child.getFullName(); assert childNew.startsWith(newFullName); assert childNew.charAt(newFullName.length()) == '/'; - String childOld = oldFullName + childNew.substring(newFullName.length()); - for (ItemListener l : all()) { - l.onLocationChanged(child, childOld, childNew); - } + final String childOld = oldFullName + childNew.substring(newFullName.length()); + forAll(new Function() { + @Override public Void apply(ItemListener l) { + l.onLocationChanged(child, childOld, childNew); + return null; + } + }, false); } } } From 8478e24609d407268bd579609bf0ce3ad395a046 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 4 Nov 2014 15:36:01 -0500 Subject: [PATCH 028/272] [FIXED JENKINS-25400] Rework fix of JENKINS-22769 (c04cdcd) to put the burden on each listener to impersonate ACL.SYSTEM if it needs to. (cherry picked from commit a6a3d5e1660735edc18d331500f7ce9850fbc724) Conflicts: changelog.html --- .../main/java/hudson/model/Fingerprint.java | 10 +++- core/src/main/java/hudson/model/ListView.java | 19 ++++++- .../hudson/model/listeners/ItemListener.java | 50 ++++++++----------- .../main/java/hudson/tasks/BuildTrigger.java | 9 +++- 4 files changed, 54 insertions(+), 34 deletions(-) diff --git a/core/src/main/java/hudson/model/Fingerprint.java b/core/src/main/java/hudson/model/Fingerprint.java index 1dd598c36e..22fac5bb8d 100644 --- a/core/src/main/java/hudson/model/Fingerprint.java +++ b/core/src/main/java/hudson/model/Fingerprint.java @@ -39,6 +39,7 @@ import hudson.BulkChange; import hudson.Extension; import hudson.model.listeners.ItemListener; import hudson.model.listeners.SaveableListener; +import hudson.security.ACL; import hudson.util.AtomicFileWriter; import hudson.util.HexBinaryConverter; import hudson.util.Iterators; @@ -729,7 +730,14 @@ public class Fingerprint implements ModelObject, Saveable { @Extension public static final class ProjectRenameListener extends ItemListener { @Override - public void onLocationChanged(Item item, String oldName, String newName) { + public void onLocationChanged(final Item item, final String oldName, final String newName) { + ACL.impersonate(ACL.SYSTEM, new Runnable() { + @Override public void run() { + locationChanged(item, oldName, newName); + } + }); + } + private void locationChanged(Item item, String oldName, String newName) { if (item instanceof AbstractProject) { AbstractProject p = Jenkins.getInstance().getItemByFullName(newName, AbstractProject.class); if (p != null) { diff --git a/core/src/main/java/hudson/model/ListView.java b/core/src/main/java/hudson/model/ListView.java index 652f69868d..73b63b4915 100644 --- a/core/src/main/java/hudson/model/ListView.java +++ b/core/src/main/java/hudson/model/ListView.java @@ -29,6 +29,7 @@ import hudson.Util; import hudson.diagnosis.OldDataMonitor; import hudson.model.Descriptor.FormException; import hudson.model.listeners.ItemListener; +import hudson.security.ACL; import hudson.util.CaseInsensitiveComparator; import hudson.util.DescribableList; import hudson.util.FormValidation; @@ -431,7 +432,14 @@ public class ListView extends View implements DirectlyModifiableView { @Restricted(NoExternalUse.class) @Extension public static final class Listener extends ItemListener { - @Override public void onLocationChanged(Item item, String oldFullName, String newFullName) { + @Override public void onLocationChanged(final Item item, final String oldFullName, final String newFullName) { + ACL.impersonate(ACL.SYSTEM, new Runnable() { + @Override public void run() { + locationChanged(item, oldFullName, newFullName); + } + }); + } + private void locationChanged(Item item, String oldFullName, String newFullName) { final Jenkins jenkins = Jenkins.getInstance(); for (View view: jenkins.getViews()) { if (view instanceof ListView) { @@ -469,7 +477,14 @@ public class ListView extends View implements DirectlyModifiableView { } } - @Override public void onDeleted(Item item) { + @Override public void onDeleted(final Item item) { + ACL.impersonate(ACL.SYSTEM, new Runnable() { + @Override public void run() { + deleted(item); + } + }); + } + private void deleted(Item item) { final Jenkins jenkins = Jenkins.getInstance(); for (View view: jenkins.getViews()) { if (view instanceof ListView) { diff --git a/core/src/main/java/hudson/model/listeners/ItemListener.java b/core/src/main/java/hudson/model/listeners/ItemListener.java index 3babd001a9..4f3d235f51 100644 --- a/core/src/main/java/hudson/model/listeners/ItemListener.java +++ b/core/src/main/java/hudson/model/listeners/ItemListener.java @@ -31,8 +31,10 @@ import hudson.model.Item; import hudson.model.ItemGroup; import hudson.model.Items; import hudson.security.ACL; +import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; +import jenkins.security.NotReallyRoleSensitiveCallable; /** * Receives notifications about CRUD operations of {@link Item}. @@ -158,22 +160,13 @@ public class ItemListener implements ExtensionPoint { } // TODO JENKINS-21224 generalize this to a method perhaps in ExtensionList and use consistently from all listeners - private static void forAll(final /* java.util.function.Consumer */Function consumer, boolean asSystem) { - Runnable r = new Runnable() { - @Override public void run() { - for (ItemListener l : all()) { - try { - consumer.apply(l); - } catch (RuntimeException x) { - LOGGER.log(Level.WARNING, "failed to send event to listener of " + l.getClass(), x); - } - } + private static void forAll(final /* java.util.function.Consumer */Function consumer) { + for (ItemListener l : all()) { + try { + consumer.apply(l); + } catch (RuntimeException x) { + LOGGER.log(Level.WARNING, "failed to send event to listener of " + l.getClass(), x); } - }; - if (asSystem) { - ACL.impersonate(ACL.SYSTEM, r); - } else { - r.run(); } } @@ -183,7 +176,7 @@ public class ItemListener implements ExtensionPoint { l.onCopied(src, result); return null; } - }, true); + }); } public static void fireOnCreated(final Item item) { @@ -192,7 +185,7 @@ public class ItemListener implements ExtensionPoint { l.onCreated(item); return null; } - }, true); + }); } public static void fireOnUpdated(final Item item) { @@ -201,7 +194,7 @@ public class ItemListener implements ExtensionPoint { l.onUpdated(item); return null; } - }, true); + }); } /** @since 1.548 */ @@ -211,7 +204,7 @@ public class ItemListener implements ExtensionPoint { l.onDeleted(item); return null; } - }, true); + }); } /** @@ -221,13 +214,6 @@ public class ItemListener implements ExtensionPoint { * @since 1.548 */ public static void fireLocationChange(final Item rootItem, final String oldFullName) { - ACL.impersonate(ACL.SYSTEM, new Runnable() { - @Override public void run() { - doFireLocationChange(rootItem, oldFullName); - } - }); - } - private static void doFireLocationChange(final Item rootItem, final String oldFullName) { String prefix = rootItem.getParent().getFullName(); if (!prefix.isEmpty()) { prefix += '/'; @@ -244,16 +230,20 @@ public class ItemListener implements ExtensionPoint { l.onRenamed(rootItem, oldName, newName); return null; } - }, false); + }); } forAll(new Function() { @Override public Void apply(ItemListener l) { l.onLocationChanged(rootItem, oldFullName, newFullName); return null; } - }, false); + }); if (rootItem instanceof ItemGroup) { - for (final Item child : Items.getAllItems((ItemGroup) rootItem, Item.class)) { + for (final Item child : ACL.impersonate(ACL.SYSTEM, new NotReallyRoleSensitiveCallable,RuntimeException>() { + @Override public List call() { + return Items.getAllItems((ItemGroup) rootItem, Item.class); + } + })) { final String childNew = child.getFullName(); assert childNew.startsWith(newFullName); assert childNew.charAt(newFullName.length()) == '/'; @@ -263,7 +253,7 @@ public class ItemListener implements ExtensionPoint { l.onLocationChanged(child, childOld, childNew); return null; } - }, false); + }); } } } diff --git a/core/src/main/java/hudson/tasks/BuildTrigger.java b/core/src/main/java/hudson/tasks/BuildTrigger.java index 40533373fd..ee121ca195 100644 --- a/core/src/main/java/hudson/tasks/BuildTrigger.java +++ b/core/src/main/java/hudson/tasks/BuildTrigger.java @@ -406,7 +406,14 @@ public class BuildTrigger extends Recorder implements DependencyDeclarer { @Extension public static class ItemListenerImpl extends ItemListener { @Override - public void onLocationChanged(Item item, String oldFullName, String newFullName) { + public void onLocationChanged(final Item item, final String oldFullName, final String newFullName) { + ACL.impersonate(ACL.SYSTEM, new Runnable() { + @Override public void run() { + locationChanged(item, oldFullName, newFullName); + } + }); + } + private void locationChanged(Item item, String oldFullName, String newFullName) { // update BuildTrigger of other projects that point to this object. // can't we generalize this? for( Project p : Jenkins.getInstance().getAllItems(Project.class) ) { From 03600e925c3d674b204981a140031a4ae3571609 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Mon, 15 Dec 2014 11:15:16 -0800 Subject: [PATCH 029/272] [maven-release-plugin] prepare release jenkins-1.580.2 --- cli/pom.xml | 2 +- core/pom.xml | 2 +- plugins/pom.xml | 10 +++++----- pom.xml | 4 ++-- test/pom.xml | 2 +- war/pom.xml | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/cli/pom.xml b/cli/pom.xml index 43e07fb442..8c397c0667 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ pom org.jenkins-ci.main - 1.580.2-SNAPSHOT + 1.580.2 cli diff --git a/core/pom.xml b/core/pom.xml index 22f4764fa0..adbec441ff 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 1.580.2-SNAPSHOT + 1.580.2 ../pom.xml diff --git a/plugins/pom.xml b/plugins/pom.xml index 5aacf0d292..c088374650 100644 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -11,7 +11,7 @@ org.jenkins-ci.plugins plugin Jenkins plugin POM - 1.580.2-SNAPSHOT + 1.580.2 pom - + + ${buildHealth.score} @@ -74,7 +75,7 @@ THE SOFTWARE. - + From cca17cc4f6106304c954a0bf3942ad0d7be4c7e6 Mon Sep 17 00:00:00 2001 From: N Nachiappan Date: Fri, 24 Oct 2014 14:06:42 -0700 Subject: [PATCH 033/272] [FIXED JENKINS-25473] move config file check as part of loading job to do it in multithread fashion than in a single thread (cherry picked from commit dc547cf2974365a6b355de9981b7183f3d4fc17f) --- core/src/main/java/jenkins/model/Jenkins.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index ffd693e6fd..ec73d6a6e5 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -2603,11 +2603,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve throw new IOException(projectsDir+" is not a directory"); throw new IOException("Unable to create "+projectsDir+"\nPermission issue? Please create this directory manually."); } - File[] subdirs = projectsDir.listFiles(new FileFilter() { - public boolean accept(File child) { - return child.isDirectory() && Items.getConfigFile(child).exists(); - } - }); + File[] subdirs = projectsDir.listFiles(); final Set loadedNames = Collections.synchronizedSet(new HashSet()); @@ -2653,6 +2649,10 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve for (final File subdir : subdirs) { g.requires(loadHudson).attains(JOB_LOADED).notFatal().add("Loading job "+subdir.getName(),new Executable() { public void run(Reactor session) throws Exception { + if(!Items.getConfigFile(subdir).exists()) { + //Does not have job config file, so it is not a jenkins job hence skip it + return; + } TopLevelItem item = (TopLevelItem) Items.load(Jenkins.this, subdir); items.put(item.getName(), item); loadedNames.add(item.getName()); From 7169a3916528ac95eada2cf13e3fbd7e50ae6387 Mon Sep 17 00:00:00 2001 From: Christof Schoell Date: Tue, 14 Oct 2014 18:00:10 +0200 Subject: [PATCH 034/272] [JENKINS-25144] return authentication object instead of null if authentication is not required - otherwise valid login fails with basic authentication (cherry picked from commit 0176b6d902faeec7bff63eb34ce16e2f70062035) --- .../security/BasicHeaderRealPasswordAuthenticator.java | 2 +- .../java/jenkins/security/BasicHeaderProcessorTest.java | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/jenkins/security/BasicHeaderRealPasswordAuthenticator.java b/core/src/main/java/jenkins/security/BasicHeaderRealPasswordAuthenticator.java index 4d3e5554e0..1e640014ef 100644 --- a/core/src/main/java/jenkins/security/BasicHeaderRealPasswordAuthenticator.java +++ b/core/src/main/java/jenkins/security/BasicHeaderRealPasswordAuthenticator.java @@ -50,7 +50,7 @@ public class BasicHeaderRealPasswordAuthenticator extends BasicHeaderAuthenticat return null; if (!authenticationIsRequired(username)) - return null; + return SecurityContextHolder.getContext().getAuthentication(); UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); diff --git a/test/src/test/java/jenkins/security/BasicHeaderProcessorTest.java b/test/src/test/java/jenkins/security/BasicHeaderProcessorTest.java index 803767f9a4..c11726fd93 100644 --- a/test/src/test/java/jenkins/security/BasicHeaderProcessorTest.java +++ b/test/src/test/java/jenkins/security/BasicHeaderProcessorTest.java @@ -56,10 +56,15 @@ public class BasicHeaderProcessorTest extends Assert { // call with incorrect password makeRequestAndFail("foo:bar"); - // if the session cookie is valid, then basic header won't be needed + wc.login("bar"); + + // if the session cookie is valid, then basic header won't be needed makeRequestWithAuthAndVerify(null, "bar"); + // if the session cookie is valid, and basic header is set anyway login should not fail either + makeRequestWithAuthAndVerify("bar:bar", "bar"); + // but if the password is incorrect, it should fail, instead of silently logging in as the user indicated by session makeRequestAndFail("foo:bar"); } From be34b675f0f9cb59a67c09dbe42364d34c3eaff1 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Fri, 7 Nov 2014 09:40:07 -0800 Subject: [PATCH 035/272] [FIXED JENKINS-25144] Revisiting the attempted fix in the previous commit. IIUC, the issue here is that the request in question contains both a valid session cookie AND basic authentication header, and that path results in a failure because BasicHeaderProcessor expects one of BasicHeaderAuthenticators to validate the basic authentication header (without knowing that there's already a valid Authentication object that came from the HTTP session, yet no BasicHeaderAuthenticator actually processes this because BasicHeaderRealPasswordAuthenticator backs away from doing that. I think the corect fix is for BasicHeaderRealPasswordAuthenticator to get rid of authenticationIsRequired check. This check instead belongs to BasicHeaderProcessor, where it should be used to check if any BasicHeaderAuthenticator should be consulted or not. The problem with having this logic in BasicHeaderRealPasswordAuthenticator is that this is just an implementation of an extension point, and thus it needs to be removable. As it stands right now in this fix, if this impl is removed, JENKINS-25144 will be back again. (cherry picked from commit 9e81b8e4feebceef94d117b757952c965bf91c61) --- .../security/BasicHeaderProcessor.java | 47 ++++++++++++++++++- .../BasicHeaderRealPasswordAuthenticator.java | 36 -------------- 2 files changed, 46 insertions(+), 37 deletions(-) diff --git a/core/src/main/java/jenkins/security/BasicHeaderProcessor.java b/core/src/main/java/jenkins/security/BasicHeaderProcessor.java index 60d217a67e..6bec1a2e4a 100644 --- a/core/src/main/java/jenkins/security/BasicHeaderProcessor.java +++ b/core/src/main/java/jenkins/security/BasicHeaderProcessor.java @@ -1,12 +1,14 @@ package jenkins.security; import hudson.security.ACL; +import hudson.security.SecurityRealm; import hudson.util.Scrambler; import org.acegisecurity.Authentication; -import org.acegisecurity.AuthenticationManager; import org.acegisecurity.BadCredentialsException; import org.acegisecurity.context.SecurityContext; import org.acegisecurity.context.SecurityContextHolder; +import org.acegisecurity.providers.UsernamePasswordAuthenticationToken; +import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken; import org.acegisecurity.ui.AuthenticationEntryPoint; import org.acegisecurity.ui.rememberme.NullRememberMeServices; import org.acegisecurity.ui.rememberme.RememberMeServices; @@ -67,6 +69,11 @@ public class BasicHeaderProcessor implements Filter { String username = uidpassword.substring(0, idx); String password = uidpassword.substring(idx+1); + if (!authenticationIsRequired(username)) { + chain.doFilter(request, response); + return; + } + for (BasicHeaderAuthenticator a : all()) { LOGGER.log(FINER, "Attempting to authenticate with {0}", a); Authentication auth = a.authenticate(req, rsp, username, password); @@ -87,6 +94,44 @@ public class BasicHeaderProcessor implements Filter { } } + /** + * If the request is already authenticated to the same user that the Authorization header claims, + * for example through the HTTP session, then there's no need to re-authenticate the Authorization header, + * so we skip that. This avoids stressing {@link SecurityRealm}. + * + * This method returns false if we can take this short-cut. + */ + // taken from BasicProcessingFilter.java + protected boolean authenticationIsRequired(String username) { + // Only reauthenticate if username doesn't match SecurityContextHolder and user isn't authenticated + // (see SEC-53) + Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication(); + + if(existingAuth == null || !existingAuth.isAuthenticated()) { + return true; + } + + // Limit username comparison to providers which use usernames (ie UsernamePasswordAuthenticationToken) + // (see SEC-348) + + if (existingAuth instanceof UsernamePasswordAuthenticationToken && !existingAuth.getName().equals(username)) { + return true; + } + + // Handle unusual condition where an AnonymousAuthenticationToken is already present + // This shouldn't happen very often, as BasicProcessingFitler is meant to be earlier in the filter + // chain than AnonymousProcessingFilter. Nevertheless, presence of both an AnonymousAuthenticationToken + // together with a BASIC authentication request header should indicate reauthentication using the + // BASIC protocol is desirable. This behaviour is also consistent with that provided by form and digest, + // both of which force re-authentication if the respective header is detected (and in doing so replace + // any existing AnonymousAuthenticationToken). See SEC-610. + if (existingAuth instanceof AnonymousAuthenticationToken) { + return true; + } + + return false; + } + protected void success(HttpServletRequest req, HttpServletResponse rsp, FilterChain chain, Authentication auth) throws IOException, ServletException { rememberMeServices.loginSuccess(req, rsp, auth); diff --git a/core/src/main/java/jenkins/security/BasicHeaderRealPasswordAuthenticator.java b/core/src/main/java/jenkins/security/BasicHeaderRealPasswordAuthenticator.java index 1e640014ef..3b41c006dd 100644 --- a/core/src/main/java/jenkins/security/BasicHeaderRealPasswordAuthenticator.java +++ b/core/src/main/java/jenkins/security/BasicHeaderRealPasswordAuthenticator.java @@ -19,9 +19,7 @@ import jenkins.ExtensionFilter; import jenkins.model.Jenkins; import org.acegisecurity.Authentication; import org.acegisecurity.AuthenticationException; -import org.acegisecurity.context.SecurityContextHolder; import org.acegisecurity.providers.UsernamePasswordAuthenticationToken; -import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken; import org.acegisecurity.ui.AuthenticationDetailsSource; import org.acegisecurity.ui.AuthenticationDetailsSourceImpl; @@ -49,9 +47,6 @@ public class BasicHeaderRealPasswordAuthenticator extends BasicHeaderAuthenticat if (DISABLE) return null; - if (!authenticationIsRequired(username)) - return SecurityContextHolder.getContext().getAuthentication(); - UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); authRequest.setDetails(authenticationDetailsSource.buildDetails(req)); @@ -68,37 +63,6 @@ public class BasicHeaderRealPasswordAuthenticator extends BasicHeaderAuthenticat } } - // taken from BasicProcessingFilter.java - protected boolean authenticationIsRequired(String username) { - // Only reauthenticate if username doesn't match SecurityContextHolder and user isn't authenticated - // (see SEC-53) - Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication(); - - if(existingAuth == null || !existingAuth.isAuthenticated()) { - return true; - } - - // Limit username comparison to providers which use usernames (ie UsernamePasswordAuthenticationToken) - // (see SEC-348) - - if (existingAuth instanceof UsernamePasswordAuthenticationToken && !existingAuth.getName().equals(username)) { - return true; - } - - // Handle unusual condition where an AnonymousAuthenticationToken is already present - // This shouldn't happen very often, as BasicProcessingFitler is meant to be earlier in the filter - // chain than AnonymousProcessingFilter. Nevertheless, presence of both an AnonymousAuthenticationToken - // together with a BASIC authentication request header should indicate reauthentication using the - // BASIC protocol is desirable. This behaviour is also consistent with that provided by form and digest, - // both of which force re-authentication if the respective header is detected (and in doing so replace - // any existing AnonymousAuthenticationToken). See SEC-610. - if (existingAuth instanceof AnonymousAuthenticationToken) { - return true; - } - - return false; - } - private static final Logger LOGGER = Logger.getLogger(BasicHeaderRealPasswordAuthenticator.class.getName()); /** From bd0453fe98e33cd99df9920c822210e49ad5cb6e Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Mon, 17 Nov 2014 17:48:48 -0500 Subject: [PATCH 036/272] [FIXED JENKINS-25655] Incorrect caching from entrySet sometimes caused incorrect results. Caused a failure in hudson.plugins.promoted_builds.conditions.ManualConditionTest.testManualPromotionProcessViaWebClient when run against 1.580.1. (cherry picked from commit b76e7bf9988ebf5b3b7e2c5e588d5ce6dbde957e) --- .../java/jenkins/model/lazy/AbstractLazyLoadRunMap.java | 4 ++++ .../java/jenkins/model/lazy/LazyLoadRunMapEntrySet.java | 4 ++++ .../jenkins/model/lazy/AbstractLazyLoadRunMapTest.java | 7 +++++++ 3 files changed, 15 insertions(+) diff --git a/core/src/main/java/jenkins/model/lazy/AbstractLazyLoadRunMap.java b/core/src/main/java/jenkins/model/lazy/AbstractLazyLoadRunMap.java index 33ba98c598..f299d598d0 100644 --- a/core/src/main/java/jenkins/model/lazy/AbstractLazyLoadRunMap.java +++ b/core/src/main/java/jenkins/model/lazy/AbstractLazyLoadRunMap.java @@ -585,6 +585,8 @@ public abstract class AbstractLazyLoadRunMap extends AbstractMap i numberOnDisk = a; } + entrySet.clearCache(); + return unwrap(old); } @@ -742,6 +744,8 @@ public abstract class AbstractLazyLoadRunMap extends AbstractMap i BuildReference old = copy.byId.remove(getIdOf(run)); this.index = copy; + entrySet.clearCache(); + return unwrap(old)!=null; } diff --git a/core/src/main/java/jenkins/model/lazy/LazyLoadRunMapEntrySet.java b/core/src/main/java/jenkins/model/lazy/LazyLoadRunMapEntrySet.java index b3910b51ae..48aada93e0 100644 --- a/core/src/main/java/jenkins/model/lazy/LazyLoadRunMapEntrySet.java +++ b/core/src/main/java/jenkins/model/lazy/LazyLoadRunMapEntrySet.java @@ -32,6 +32,10 @@ class LazyLoadRunMapEntrySet extends AbstractSet> { return all; } + synchronized void clearCache() { + all = null; + } + @Override public int size() { return all().size(); diff --git a/core/src/test/java/jenkins/model/lazy/AbstractLazyLoadRunMapTest.java b/core/src/test/java/jenkins/model/lazy/AbstractLazyLoadRunMapTest.java index 55bd0bdfde..6cb55b6e7d 100644 --- a/core/src/test/java/jenkins/model/lazy/AbstractLazyLoadRunMapTest.java +++ b/core/src/test/java/jenkins/model/lazy/AbstractLazyLoadRunMapTest.java @@ -414,6 +414,13 @@ public class AbstractLazyLoadRunMapTest extends Assert { assertEquals(0, b.entrySet().size()); } + @Issue("JENKINS-25655") + @Test public void entrySetChanges() { + assertEquals(3, a.entrySet().size()); + a.put(new Build(7, "D")); + assertEquals(4, a.entrySet().size()); + } + @Issue("JENKINS-18065") @Test public void entrySetContains() { From 23e0d6ad1964e0efdd1e0f1c90360321acc8a073 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Mon, 24 Nov 2014 10:37:58 -0500 Subject: [PATCH 037/272] [FIXED JENKINS-25759] Avoid consuming too much memory while running validateAntFileMask. Not fully solved, since the scannedDirs field can still grow to be large, but at least clearing files/dirsNotIncluded. Also imposing a 5s timeout on the scan regardless of file count, and defining a user-customizable bound. (cherry picked from commit 534328b264f9338e48418d2bcc0d28daaf48b3a0) Conflicts: changelog.html --- core/src/main/java/hudson/FilePath.java | 18 +++++++++++++++--- .../java/hudson/tasks/ArtifactArchiver.java | 2 +- .../java/hudson/util/FormFieldValidator.java | 2 +- core/src/test/java/hudson/FilePathTest.java | 5 ++++- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index 1fcb6a5763..b39d036231 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -2236,11 +2236,18 @@ public final class FilePath implements Serializable { * null if no error was found. Otherwise returns a human readable error message. * @since 1.90 * @see #validateFileMask(FilePath, String) + * @deprecated use {@link #validateAntFileMask(String, int)} instead */ public String validateAntFileMask(final String fileMasks) throws IOException, InterruptedException { return validateAntFileMask(fileMasks, Integer.MAX_VALUE); } + /** + * Default bound for {@link #validateAntFileMask(String, int)}. + * @since 1.592 + */ + public static int VALIDATE_ANT_FILE_MASK_BOUND = Integer.getInteger(FilePath.class.getName() + ".VALIDATE_ANT_FILE_MASK_BOUND", 10000); + /** * Like {@link #validateAntFileMask(String)} but performing only a bounded number of operations. *

Whereas the unbounded overload is appropriate for calling from cancelable, long-running tasks such as build steps, @@ -2250,7 +2257,7 @@ public final class FilePath implements Serializable { * A message is returned in case the file pattern can definitely be determined to not match anything in the directory within the alloted time. * If the time runs out without finding a match but without ruling out the possibility that there might be one, {@link InterruptedException} is thrown, * in which case the calling code should give the user the benefit of the doubt and use {@link hudson.util.FormValidation.Kind#OK} (with or without a message). - * @param bound a maximum number of negative operations (deliberately left vague) to perform before giving up on a precise answer; 10_000 is a reasonable pick + * @param bound a maximum number of negative operations (deliberately left vague) to perform before giving up on a precise answer; try {@link #VALIDATE_ANT_FILE_MASK_BOUND} * @throws InterruptedException not only in case of a channel failure, but also if too many operations were performed without finding any matches * @since 1.484 */ @@ -2358,10 +2365,15 @@ public final class FilePath implements Serializable { class Cancel extends RuntimeException {} DirectoryScanner ds = bound == Integer.MAX_VALUE ? new DirectoryScanner() : new DirectoryScanner() { int ticks; + long start = System.currentTimeMillis(); @Override public synchronized boolean isCaseSensitive() { - if (!filesIncluded.isEmpty() || !dirsIncluded.isEmpty() || ticks++ > bound) { + if (!filesIncluded.isEmpty() || !dirsIncluded.isEmpty() || ticks++ > bound || System.currentTimeMillis() - start > 5000) { throw new Cancel(); } + filesNotIncluded.clear(); + dirsNotIncluded.clear(); + // notFollowedSymlinks might be large, but probably unusual + // scannedDirs will typically be largish, but seems to be needed return super.isCaseSensitive(); } }; @@ -2424,7 +2436,7 @@ public final class FilePath implements Serializable { if(!exists()) // no workspace. can't check return FormValidation.ok(); - String msg = validateAntFileMask(value, 10000); + String msg = validateAntFileMask(value, VALIDATE_ANT_FILE_MASK_BOUND); if(errorIfNotExist) return FormValidation.error(msg); else return FormValidation.warning(msg); } catch (InterruptedException e) { diff --git a/core/src/main/java/hudson/tasks/ArtifactArchiver.java b/core/src/main/java/hudson/tasks/ArtifactArchiver.java index 43839ce936..383481f93d 100644 --- a/core/src/main/java/hudson/tasks/ArtifactArchiver.java +++ b/core/src/main/java/hudson/tasks/ArtifactArchiver.java @@ -227,7 +227,7 @@ public class ArtifactArchiver extends Recorder implements SimpleBuildStep { listenerWarnOrError(listener, Messages.ArtifactArchiver_NoMatchFound(artifacts)); String msg = null; try { - msg = ws.validateAntFileMask(artifacts); + msg = ws.validateAntFileMask(artifacts, FilePath.VALIDATE_ANT_FILE_MASK_BOUND); } catch (Exception e) { listenerWarnOrError(listener, e.getMessage()); } diff --git a/core/src/main/java/hudson/util/FormFieldValidator.java b/core/src/main/java/hudson/util/FormFieldValidator.java index 9558090186..d344dd43d5 100644 --- a/core/src/main/java/hudson/util/FormFieldValidator.java +++ b/core/src/main/java/hudson/util/FormFieldValidator.java @@ -378,7 +378,7 @@ public abstract class FormFieldValidator { return; } - String msg = ws.validateAntFileMask(value, 10000); + String msg = ws.validateAntFileMask(value, FilePath.VALIDATE_ANT_FILE_MASK_BOUND); if(errorIfNotExist) error(msg); else warning(msg); } catch (InterruptedException e) { diff --git a/core/src/test/java/hudson/FilePathTest.java b/core/src/test/java/hudson/FilePathTest.java index 529fcd937e..3fe2788f20 100644 --- a/core/src/test/java/hudson/FilePathTest.java +++ b/core/src/test/java/hudson/FilePathTest.java @@ -56,6 +56,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; +import org.jvnet.hudson.test.Issue; import static org.mockito.Mockito.*; @@ -496,11 +497,13 @@ public class FilePathTest extends ChannelTestCase { } } + @SuppressWarnings("deprecation") private static void assertValidateAntFileMask(String expected, FilePath d, String fileMasks) throws Exception { assertEquals(expected, d.validateAntFileMask(fileMasks)); } - @Bug(7214) + @Issue("JENKINS-7214") + @SuppressWarnings("deprecation") public void testValidateAntFileMaskBounded() throws Exception { File tmp = Util.createTempDir(); try { From 5e02457709e72c1577cf9a6a52a6bd9450acd590 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Fri, 5 Dec 2014 11:32:51 -0500 Subject: [PATCH 038/272] Misleading error message. (cherry picked from commit e4c5c7104206536c80379ce18ec640f56b7dba74) --- .../main/java/jenkins/security/DefaultConfidentialStore.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/jenkins/security/DefaultConfidentialStore.java b/core/src/main/java/jenkins/security/DefaultConfidentialStore.java index 88eb84fc1c..989769b32a 100644 --- a/core/src/main/java/jenkins/security/DefaultConfidentialStore.java +++ b/core/src/main/java/jenkins/security/DefaultConfidentialStore.java @@ -106,7 +106,7 @@ public class DefaultConfidentialStore extends ConfidentialStore { byte[] bytes = IOUtils.toByteArray(cis); return verifyMagic(bytes); } catch (GeneralSecurityException e) { - throw new IOException("Failed to persist the key: "+key.getId(),e); + throw new IOException("Failed to load the key: "+key.getId(),e); } finally { IOUtils.closeQuietly(cis); IOUtils.closeQuietly(fis); From 5ded56a8288dfcfb9e389ea85edc13cf49981a2a Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Fri, 5 Dec 2014 12:07:50 -0500 Subject: [PATCH 039/272] [FIXED JENKINS-25937] Treat BadPaddingException as an unloadable key and continue. (cherry picked from commit 6318b8d800abdf8b280f4e1b8ce8bf22e49242ad) Conflicts: changelog.html --- .../java/jenkins/security/DefaultConfidentialStore.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/src/main/java/jenkins/security/DefaultConfidentialStore.java b/core/src/main/java/jenkins/security/DefaultConfidentialStore.java index 989769b32a..7821f492fd 100644 --- a/core/src/main/java/jenkins/security/DefaultConfidentialStore.java +++ b/core/src/main/java/jenkins/security/DefaultConfidentialStore.java @@ -16,6 +16,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.security.GeneralSecurityException; import java.security.SecureRandom; +import javax.crypto.BadPaddingException; import org.apache.commons.io.IOUtils; /** @@ -107,6 +108,12 @@ public class DefaultConfidentialStore extends ConfidentialStore { return verifyMagic(bytes); } catch (GeneralSecurityException e) { throw new IOException("Failed to load the key: "+key.getId(),e); + } catch (IOException x) { + if (x.getCause() instanceof BadPaddingException) { + return null; // broken somehow + } else { + throw x; + } } finally { IOUtils.closeQuietly(cis); IOUtils.closeQuietly(fis); From ea6baf9a103b841ec99dd1c2a9aac85fe6d8d29f Mon Sep 17 00:00:00 2001 From: Richard Mortimer Date: Fri, 15 Aug 2014 20:28:44 +0100 Subject: [PATCH 040/272] [FIXES JENKINS-24272] jnlp slaves fail to reconnect when master is restarted During master restart only attempt to reconnect the slave after the master has finished restarting. (cherry picked from commit 48e19c58f9e2caa998d0942417d58679f5ce47f0) --- .../jenkins/slaves/restarter/JnlpSlaveRestarterInstaller.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/jenkins/slaves/restarter/JnlpSlaveRestarterInstaller.java b/core/src/main/java/jenkins/slaves/restarter/JnlpSlaveRestarterInstaller.java index aee8ffcd45..61e21c4b18 100644 --- a/core/src/main/java/jenkins/slaves/restarter/JnlpSlaveRestarterInstaller.java +++ b/core/src/main/java/jenkins/slaves/restarter/JnlpSlaveRestarterInstaller.java @@ -69,7 +69,7 @@ public class JnlpSlaveRestarterInstaller extends ComputerListener implements Ser e.addListener(new EngineListenerAdapter() { @Override - public void onDisconnect() { + public void onReconnect() { try { for (SlaveRestarter r : restarters) { try { From 23152a6da4212b65398650faed91146364343b2f Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 9 Dec 2014 15:07:15 -0500 Subject: [PATCH 041/272] For RunMapTest to be Groovy was pointless and aggravating. (cherry picked from commit 30f00b111ad7e6ef689ae018969b2b9dc5ce1571) --- .../groovy/hudson/model/RunMapTest.groovy | 74 ------------------- .../test/java/hudson/model/RunMapTest.java | 74 +++++++++++++++++++ 2 files changed, 74 insertions(+), 74 deletions(-) delete mode 100644 test/src/test/groovy/hudson/model/RunMapTest.groovy create mode 100644 test/src/test/java/hudson/model/RunMapTest.java diff --git a/test/src/test/groovy/hudson/model/RunMapTest.groovy b/test/src/test/groovy/hudson/model/RunMapTest.groovy deleted file mode 100644 index 63529cf668..0000000000 --- a/test/src/test/groovy/hudson/model/RunMapTest.groovy +++ /dev/null @@ -1,74 +0,0 @@ -package hudson.model - -import org.jvnet.hudson.test.Bug -import org.jvnet.hudson.test.HudsonTestCase -import org.jvnet.hudson.test.SleepBuilder -import org.jvnet.hudson.test.TestEnvironment - -import javax.xml.transform.stream.StreamSource - -/** - * - * - * @author Kohsuke Kawaguchi - */ -class RunMapTest extends HudsonTestCase { - /** - * Makes sure that reloading the project while a build is in progress won't clobber that in-progress build. - */ - @Bug(12318) - public void testReloadWhileBuildIsInProgress() { - def p = createFreeStyleProject() - - // want some completed build records - def b1 = assertBuildStatusSuccess(p.scheduleBuild2(0)) - - // now create a build that hangs until we signal the OneShotEvent - p.buildersList.add(new SleepBuilder(9999999)); - def b2 = p.scheduleBuild2(0).waitForStart() - assertEquals 2,b2.number - - // now reload - p.updateByXml(new StreamSource(p.configFile.file)) - - // we should still see the same object for #2 because that's in progress - assertSame p.getBuildByNumber(b2.number),b2 - // build #1 should be reloaded - assertNotSame b1,p.getBuildByNumber(1) - - // and reference gets fixed up - b1 = p.getBuildByNumber(1) - assertSame b1.nextBuild,b2 - assertSame b2.previousBuild,b1 - } - - /** - * Testing if the lazy loading can gracefully tolerate a RuntimeException during unmarshalling. - */ - @Bug(15533) - public void testRuntimeExceptionInUnmarshalling() { - def p = createFreeStyleProject() - def b = assertBuildStatusSuccess(p.scheduleBuild2(0)) - b.addAction(new BombAction()); - b.save() - - p._getRuns().purgeCache() - b = p.getBuildByNumber(b.number) - // Original test assumed that b == null, but after JENKINS-21024 this is no longer true, - // so this may not really be testing anything interesting: - assert b != null - assert b.getAction(BombAction.class) == null - assert bombed - } - - public static class BombAction extends InvisibleAction { - public Object readResolve() { - TestEnvironment.get().testCase.bombed = true - throw new NullPointerException(); - } - } - - boolean bombed; - - -} diff --git a/test/src/test/java/hudson/model/RunMapTest.java b/test/src/test/java/hudson/model/RunMapTest.java new file mode 100644 index 0000000000..c1ad3833ec --- /dev/null +++ b/test/src/test/java/hudson/model/RunMapTest.java @@ -0,0 +1,74 @@ +package hudson.model; + +import org.jvnet.hudson.test.Bug; +import org.jvnet.hudson.test.HudsonTestCase; +import org.jvnet.hudson.test.SleepBuilder; +import org.jvnet.hudson.test.TestEnvironment; + +import javax.xml.transform.stream.StreamSource; + +/** + * + * + * @author Kohsuke Kawaguchi + */ +public class RunMapTest extends HudsonTestCase { + /** + * Makes sure that reloading the project while a build is in progress won't clobber that in-progress build. + */ + @Bug(12318) + public void testReloadWhileBuildIsInProgress() throws Exception { + FreeStyleProject p = createFreeStyleProject(); + + // want some completed build records + FreeStyleBuild b1 = assertBuildStatusSuccess(p.scheduleBuild2(0)); + + // now create a build that hangs until we signal the OneShotEvent + p.getBuildersList().add(new SleepBuilder(9999999)); + FreeStyleBuild b2 = p.scheduleBuild2(0).waitForStart(); + assertEquals(2, b2.number); + + // now reload + p.updateByXml(new StreamSource(p.getConfigFile().getFile())); + + // we should still see the same object for #2 because that's in progress + assertSame(p.getBuildByNumber(b2.number), b2); + // build #1 should be reloaded + assertNotSame(b1, p.getBuildByNumber(1)); + + // and reference gets fixed up + b1 = p.getBuildByNumber(1); + assertSame(b1.getNextBuild(), b2); + assertSame(b2.getPreviousBuild(), b1); + } + + /** + * Testing if the lazy loading can gracefully tolerate a RuntimeException during unmarshalling. + */ + @Bug(15533) + public void testRuntimeExceptionInUnmarshalling() throws Exception { + FreeStyleProject p = createFreeStyleProject(); + FreeStyleBuild b = assertBuildStatusSuccess(p.scheduleBuild2(0)); + b.addAction(new BombAction()); + b.save(); + + p._getRuns().purgeCache(); + b = p.getBuildByNumber(b.number); + // Original test assumed that b == null, but after JENKINS-21024 this is no longer true, + // so this may not really be testing anything interesting: + assertNotNull(b); + assertNull(b.getAction(BombAction.class)); + assertTrue(bombed); + } + + public static class BombAction extends InvisibleAction { + public Object readResolve() { + ((RunMapTest) TestEnvironment.get().testCase).bombed = true; + throw new NullPointerException(); + } + } + + private boolean bombed; + + +} From 21a619f2e0ad1658435c9edf5e461d9309ff26af Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 9 Dec 2014 15:12:19 -0500 Subject: [PATCH 042/272] JUnit 4. (cherry picked from commit 96ecabedb0a6bc77f7f838bdaa5482f4f2c10190) --- .../test/java/hudson/model/RunMapTest.java | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/test/src/test/java/hudson/model/RunMapTest.java b/test/src/test/java/hudson/model/RunMapTest.java index c1ad3833ec..a6b45fe602 100644 --- a/test/src/test/java/hudson/model/RunMapTest.java +++ b/test/src/test/java/hudson/model/RunMapTest.java @@ -1,27 +1,30 @@ package hudson.model; -import org.jvnet.hudson.test.Bug; -import org.jvnet.hudson.test.HudsonTestCase; -import org.jvnet.hudson.test.SleepBuilder; -import org.jvnet.hudson.test.TestEnvironment; - +import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; +import static org.junit.Assert.*; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.Issue; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.SleepBuilder; /** - * - * * @author Kohsuke Kawaguchi */ -public class RunMapTest extends HudsonTestCase { +public class RunMapTest { + + @Rule public JenkinsRule r = new JenkinsRule(); + /** * Makes sure that reloading the project while a build is in progress won't clobber that in-progress build. */ - @Bug(12318) - public void testReloadWhileBuildIsInProgress() throws Exception { - FreeStyleProject p = createFreeStyleProject(); + @Issue("JENKNS-12318") + @Test public void reloadWhileBuildIsInProgress() throws Exception { + FreeStyleProject p = r.createFreeStyleProject(); // want some completed build records - FreeStyleBuild b1 = assertBuildStatusSuccess(p.scheduleBuild2(0)); + FreeStyleBuild b1 = r.assertBuildStatusSuccess(p.scheduleBuild2(0)); // now create a build that hangs until we signal the OneShotEvent p.getBuildersList().add(new SleepBuilder(9999999)); @@ -29,7 +32,7 @@ public class RunMapTest extends HudsonTestCase { assertEquals(2, b2.number); // now reload - p.updateByXml(new StreamSource(p.getConfigFile().getFile())); + p.updateByXml((Source) new StreamSource(p.getConfigFile().getFile())); // we should still see the same object for #2 because that's in progress assertSame(p.getBuildByNumber(b2.number), b2); @@ -45,10 +48,10 @@ public class RunMapTest extends HudsonTestCase { /** * Testing if the lazy loading can gracefully tolerate a RuntimeException during unmarshalling. */ - @Bug(15533) - public void testRuntimeExceptionInUnmarshalling() throws Exception { - FreeStyleProject p = createFreeStyleProject(); - FreeStyleBuild b = assertBuildStatusSuccess(p.scheduleBuild2(0)); + @Issue("JENKINS-15533") + @Test public void runtimeExceptionInUnmarshalling() throws Exception { + FreeStyleProject p = r.createFreeStyleProject(); + FreeStyleBuild b = r.assertBuildStatusSuccess(p.scheduleBuild2(0)); b.addAction(new BombAction()); b.save(); @@ -63,12 +66,11 @@ public class RunMapTest extends HudsonTestCase { public static class BombAction extends InvisibleAction { public Object readResolve() { - ((RunMapTest) TestEnvironment.get().testCase).bombed = true; + bombed = true; throw new NullPointerException(); } } - private boolean bombed; - + private static boolean bombed; } From 14d676bb15dbf8ddc3208bd8a80033e831756d49 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 9 Dec 2014 15:24:06 -0500 Subject: [PATCH 043/272] [FIXED JENKINS-25788] RunMap.remove was always returning false, even when the Run was in fact removed. (cherry picked from commit ce94579c50621990517c86120236ee9a1d0490da) Conflicts: changelog.html --- .../model/lazy/AbstractLazyLoadRunMap.java | 2 +- .../test/java/hudson/model/RunMapTest.java | 20 ++++++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/jenkins/model/lazy/AbstractLazyLoadRunMap.java b/core/src/main/java/jenkins/model/lazy/AbstractLazyLoadRunMap.java index f299d598d0..3085d8fb82 100644 --- a/core/src/main/java/jenkins/model/lazy/AbstractLazyLoadRunMap.java +++ b/core/src/main/java/jenkins/model/lazy/AbstractLazyLoadRunMap.java @@ -746,7 +746,7 @@ public abstract class AbstractLazyLoadRunMap extends AbstractMap i entrySet.clearCache(); - return unwrap(old)!=null; + return old != null; } /** diff --git a/test/src/test/java/hudson/model/RunMapTest.java b/test/src/test/java/hudson/model/RunMapTest.java index a6b45fe602..13d49dcd60 100644 --- a/test/src/test/java/hudson/model/RunMapTest.java +++ b/test/src/test/java/hudson/model/RunMapTest.java @@ -9,9 +9,6 @@ import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.JenkinsRule; import org.jvnet.hudson.test.SleepBuilder; -/** - * @author Kohsuke Kawaguchi - */ public class RunMapTest { @Rule public JenkinsRule r = new JenkinsRule(); @@ -73,4 +70,21 @@ public class RunMapTest { private static boolean bombed; + @Issue("JENKINS-25788") + @Test public void remove() throws Exception { + FreeStyleProject p = r.createFreeStyleProject(); + FreeStyleBuild b1 = r.buildAndAssertSuccess(p); + FreeStyleBuild b2 = r.buildAndAssertSuccess(p); + RunMap runs = p._getRuns(); + assertEquals(2, runs.size()); + assertTrue(runs.remove(b1)); + assertEquals(1, runs.size()); + assertFalse(runs.remove(b1)); + assertEquals(1, runs.size()); + assertTrue(runs.remove(b2)); + assertEquals(0, runs.size()); + assertFalse(runs.remove(b2)); + assertEquals(0, runs.size()); + } + } From 2172503f3ce2acf1dd9fb95891e41441bff5197f Mon Sep 17 00:00:00 2001 From: Daniel Beck Date: Sun, 21 Dec 2014 03:29:39 +0100 Subject: [PATCH 044/272] [FIXED JENKINS-25989] Allow breaking label and node lists (cherry picked from commit 2820e59202975ce8ad302e4a0b0e17c07e4c40cf) --- core/src/main/resources/hudson/model/Computer/index.jelly | 1 + core/src/main/resources/hudson/model/Label/index.jelly | 1 + 2 files changed, 2 insertions(+) diff --git a/core/src/main/resources/hudson/model/Computer/index.jelly b/core/src/main/resources/hudson/model/Computer/index.jelly index 9adde755c6..ecd9dbf11e 100644 --- a/core/src/main/resources/hudson/model/Computer/index.jelly +++ b/core/src/main/resources/hudson/model/Computer/index.jelly @@ -94,6 +94,7 @@ THE SOFTWARE. ${entry.item.name} + diff --git a/core/src/main/resources/hudson/model/Label/index.jelly b/core/src/main/resources/hudson/model/Label/index.jelly index 22cd6d0ac3..931d7932df 100644 --- a/core/src/main/resources/hudson/model/Label/index.jelly +++ b/core/src/main/resources/hudson/model/Label/index.jelly @@ -50,6 +50,7 @@ THE SOFTWARE. ${c.displayName} + From 3603b69938e72bd7a03dd431cd1ac7066a5646ef Mon Sep 17 00:00:00 2001 From: tfennelly Date: Mon, 24 Nov 2014 22:58:25 +0000 Subject: [PATCH 045/272] Single/Multiline Build History (cherry picked from commit cb9036c45527223525cc1b48020a8ed777020c1a) --- .../widgets/BuildHistoryWidget/entries.jelly | 54 ++- .../hudson/widgets/HistoryWidget/entry.jelly | 69 +-- .../hudson/widgets/HistoryWidget/index.jelly | 1 + war/src/main/webapp/css/style.css | 132 +++++- .../main/webapp/scripts/hudson-behavior.js | 436 +++++++++++++++++- 5 files changed, 620 insertions(+), 72 deletions(-) diff --git a/core/src/main/resources/hudson/widgets/BuildHistoryWidget/entries.jelly b/core/src/main/resources/hudson/widgets/BuildHistoryWidget/entries.jelly index 80eaf3cbfd..f84e541e08 100644 --- a/core/src/main/resources/hudson/widgets/BuildHistoryWidget/entries.jelly +++ b/core/src/main/resources/hudson/widgets/BuildHistoryWidget/entries.jelly @@ -33,30 +33,38 @@ THE SOFTWARE. - - - - - - #${queuedItems.size()==1 ? it.owner.nextBuildNumber - : it.owner.nextBuildNumber+queuedItems.size()-i-1} - - -

- - - + + +
+
+ +
+ +
+ #${queuedItems.size()==1 ? it.owner.nextBuildNumber + : it.owner.nextBuildNumber+queuedItems.size()-i-1} +
- - - - (${%pending}—) - - - (${%pending}) - - - ${item.params} +
+ + + + (${%pending}—) + + + (${%pending}) + + + ${item.params} +
+
+
+ + + +
+
+
diff --git a/core/src/main/resources/hudson/widgets/HistoryWidget/entry.jelly b/core/src/main/resources/hudson/widgets/HistoryWidget/entry.jelly index 8ef23a9da0..2283a6603a 100644 --- a/core/src/main/resources/hudson/widgets/HistoryWidget/entry.jelly +++ b/core/src/main/resources/hudson/widgets/HistoryWidget/entry.jelly @@ -29,41 +29,48 @@ THE SOFTWARE. - - - - ${build.displayName} - - - - - - - - - + + + +
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + +
+
+
-
+
- - - - - - - - - - - - - - - - - +
diff --git a/core/src/main/resources/hudson/widgets/HistoryWidget/index.jelly b/core/src/main/resources/hudson/widgets/HistoryWidget/index.jelly index e545de38d0..f43ba94068 100644 --- a/core/src/main/resources/hudson/widgets/HistoryWidget/index.jelly +++ b/core/src/main/resources/hudson/widgets/HistoryWidget/index.jelly @@ -88,6 +88,7 @@ THE SOFTWARE. newhist.headers = hist.headers replace(hist, newhist); + fireBuildHistoryChanged(); } }); } diff --git a/war/src/main/webapp/css/style.css b/war/src/main/webapp/css/style.css index 9947dabaca..a01d3c945a 100644 --- a/war/src/main/webapp/css/style.css +++ b/war/src/main/webapp/css/style.css @@ -431,10 +431,6 @@ pre.console { width: 480px; } -.build-row { - padding: 3px 4px 3px 4px; -} - .build-keep { font-weight: bold; } @@ -924,18 +920,140 @@ table.parameters > tbody:hover { } #buildHistory .desc { + position: relative; padding: 0; margin-top: 5px; white-space: normal; - max-width: 200px; + opacity: 0.6; + font-style: italic; +} + +#buildHistory .build-row-cell { + position: relative; } #buildHistory .build-rss-links { float: right; } -#buildHistory .build-name { - max-width: 120px; +.build-row { + padding: 3px 4px 3px 4px; +} + +.build-row:hover { + background: #e8e8e8 !important; +} + +.build-row-cell { + font-size: 12px; +} + +.build-row-cell .pane.build-name { + width: 20%; + font-weight: bold; +} +.build-row-cell .pane.build-details { + width: 50%; +} +.build-row-cell .pane.build-controls { + width: 30%; + text-align: right; +} +.build-row-cell .pane.build-details.block { + width: 100%; +} + +.pane.build-name a, +.pane.build-name a:visited { + color: black; + text-decoration: underline; +} + +.pane.build-details a, +.pane.build-details a:visited { + color: black; + opacity: 0.6; + text-decoration: none; +} +.pane.build-details a:hover { + opacity: 1.0; + text-decoration: underline; +} + +.build-row.multi-line .build-row-cell .pane.build-name.block { + width: 100%; +} +.build-row-cell .pane.build-controls.block { + width: 100%; +} + +.build-row-cell .pane.build-name .build-icon, +.build-row-cell .pane.build-name .display-name { + display: inline-block; +} +.build-row-cell .pane.build-name .build-icon { + position: absolute; +} + +.build-row-cell .build-stop { + display: inline-block; + width: 30%; +} +.build-row-cell .build-badge { + display: inline-block; + padding: 2px; + width: 70%; + text-align: right; +} +.build-row .build-name-controls .pane.build-name, +.build-row .build-details-controls .pane.build-details { + width: 70%; +} + +.build-row .build-row-cell .pane { + padding: 0px 2px; /* Sync changes with func expandControlsTo50Percent in hudson-behavior.js */ + display: inline-block; + overflow: hidden; +} + +.build-row.multi-line .build-row-cell .block { + display: block; + overflow: auto; +} + +.build-row.multi-line .build-row-cell .indent-multiline { + margin-top: 5px; +} + +.build-row.multi-line .build-row-cell .left-bar { + position: absolute; + top: 19px; + bottom: 3px; + left: 14px; + border-left: 1px solid #cdcdcd; +} + +.build-row-cell .pane.build-name .display-name, +.build-row-cell .indent-multiline { + padding-left: 20px !important; /* Sync changes with func expandControlsTo50Percent in hudson-behavior.js */ +} + +.build-row .build-row-cell { + visibility: hidden; +} +.build-row.overflow-checked .build-row-cell { + visibility: visible; +} + +/* ================ Element overflow calculation helper styles ================ */ + +.force-wrap, .force-wrap a { + white-space: normal !important; + overflow: visible !important; +} +.force-nowrap, .force-nowrap a { + white-space: nowrap !important; + overflow: hidden !important; } /* ========================= editable combobox style ========================= */ diff --git a/war/src/main/webapp/scripts/hudson-behavior.js b/war/src/main/webapp/scripts/hudson-behavior.js index 35d61270bf..f7983edbd1 100644 --- a/war/src/main/webapp/scripts/hudson-behavior.js +++ b/war/src/main/webapp/scripts/hudson-behavior.js @@ -1557,19 +1557,322 @@ Form.findMatchingInput = function(base, name) { return null; // not found } +function onBuildHistoryChange(handler) { + Event.observe(window, 'jenkins:buildHistoryChanged', handler); +} +function fireBuildHistoryChanged() { + Event.fire(window, 'jenkins:buildHistoryChanged'); +} + function updateBuildHistory(ajaxUrl,nBuild) { if(isRunAsTest) return; - $('buildHistory').headers = ["n",nBuild]; + var bh = $('buildHistory'); - function updateBuilds() { - if(isPageVisible()){ - var bh = $('buildHistory'); - if (bh.headers == null) { - // Yahoo.log("Missing headers in buildHistory element"); + bh.headers = ["n",nBuild]; + + function getDataTable(buildHistoryDiv) { + return $(buildHistoryDiv).getElementsBySelector('table.pane')[0]; + } + + var leftRightPadding = 4; + function checkRowCellOverflows(row) { + if (!row) { + return; + } + + if (Element.hasClassName(row, "overflow-checked")) { + // already done. + return; + } + + function markSingleline() { + Element.addClassName(row, "single-line"); + Element.removeClassName(row, "multi-line"); + } + function markMultiline() { + Element.removeClassName(row, "single-line"); + Element.addClassName(row, "multi-line"); + } + function indentMultiline(element) { + Element.addClassName(element, "indent-multiline"); + } + + function blockWrap(el1, el2) { + var div = document.createElement('div'); + + Element.addClassName(div, "block"); + Element.addClassName(div, "wrap"); + Element.addClassName(el1, "wrapped"); + Element.addClassName(el2, "wrapped"); + + el1.parentNode.insertBefore(div, el1); + el1.parentNode.removeChild(el1); + el2.parentNode.removeChild(el2); + div.appendChild(el1); + div.appendChild(el2); + + return div; + } + function blockUnwrap(element) { + var wrapped = $(element).getElementsBySelector('.wrapped'); + for (var i = 0; i < wrapped.length; i++) { + var wrappedEl = wrapped[i]; + wrappedEl.parentNode.removeChild(wrappedEl); + element.parentNode.insertBefore(wrappedEl, element); + Element.removeClassName(wrappedEl, "wrapped"); + } + element.parentNode.removeChild(element); + } + + var buildName = $(row).getElementsBySelector('.build-name')[0]; + var buildDetails = $(row).getElementsBySelector('.build-details')[0]; + + if (!buildName || !buildDetails) { + return; + } + + var displayName = $(buildName).getElementsBySelector('.display-name')[0]; + var buildControls = $(row).getElementsBySelector('.build-controls')[0]; + var desc; + + var descElements = $(row).getElementsBySelector('.desc'); + if (descElements.length > 0) { + desc = descElements[0]; + } + + function resetCellOverflows() { + markSingleline(); + + // undo block wraps + var blockWraps = $(row).getElementsBySelector('.block.wrap'); + for (var i = 0; i < blockWraps.length; i++) { + blockUnwrap(blockWraps[i]); } - function getDataTable(buildHistoryDiv) { - return $(buildHistoryDiv).getElementsBySelector('table.pane')[0]; + removeZeroWidthSpaces(displayName); + removeZeroWidthSpaces(desc); + Element.removeClassName(buildName, "block"); + buildName.removeAttribute('style'); + Element.removeClassName(buildDetails, "block"); + buildDetails.removeAttribute('style'); + if (buildControls) { + Element.removeClassName(buildControls, "block"); + buildDetails.removeAttribute('style'); + } + } + + // Undo everything from the previous poll. + resetCellOverflows(); + + // Insert zero-width spaces so as to allow text to wrap, allowing us to get the true clientWidth. + insertZeroWidthSpaces(displayName, 2); + if (desc) { + insertZeroWidthSpaces(desc, 30); + markMultiline(); + } + + var rowWidth = bh.clientWidth; + var usableRowWidth = rowWidth - (leftRightPadding * 2); + var nameOverflowParams = getElementOverflowParams(buildName); + var detailsOverflowParams = getElementOverflowParams(buildDetails); + + var controlsOverflowParams; + if (buildControls) { + controlsOverflowParams = getElementOverflowParams(buildControls); + } + + if (nameOverflowParams.isOverflowed) { + // If the name is overflowed, lets remove the zero-width spaces we added above and + // re-add zero-width spaces with a bigger max word sizes. + removeZeroWidthSpaces(displayName); + insertZeroWidthSpaces(displayName, 20); + } + + function fitToControlsHeight(element) { + if (buildControls) { + if (element.clientHeight < buildControls.clientHeight) { + $(element).setStyle({height: buildControls.clientHeight.toString() + 'px'}); + } + } + } + + function setBuildControlWidths() { + if (buildControls) { + var buildBadge = $(buildControls).getElementsBySelector('.build-badge')[0]; + + if (buildBadge) { + var buildControlsWidth = buildControls.clientWidth; + var buildBadgeWidth; + + var buildStop = $(buildControls).getElementsBySelector('.build-stop')[0]; + if (buildStop) { + $(buildStop).setStyle({width: '24px'}); + // Minus 24 for the buildStop width, + // minus 4 for left+right padding in the controls container + buildBadgeWidth = (buildControlsWidth - 24 - leftRightPadding); + if (Element.hasClassName(buildControls, "indent-multiline")) { + buildBadgeWidth = buildBadgeWidth - 20; + } + $(buildBadge).setStyle({width: (buildBadgeWidth) + 'px'}); + } else { + $(buildBadge).setStyle({width: '100%'}); + } + } + controlsOverflowParams = getElementOverflowParams(buildControls); + } + } + setBuildControlWidths(); + + var controlsRepositioned = false; + + if (nameOverflowParams.isOverflowed || detailsOverflowParams.isOverflowed) { + // At least one of the cells (name or details) needs to move to a row of its own. + + markMultiline(); + + if (buildControls) { + + // We have build controls. Lets see can we find a combination that allows the build controls + // to sit beside either the build name or the build details. + + var badgesOverflowing = false; + var nameLessThanHalf = true; + var detailsLessThanHalf = true; + var buildBadge = $(buildControls).getElementsBySelector('.build-badge')[0]; + if (buildBadge) { + var badgeOverflowParams = getElementOverflowParams(buildBadge); + + if (badgeOverflowParams.isOverflowed) { + // The badges are also overflowing. In this case, we will only attempt to + // put the controls on the same line as the name or details (see below) + // if the name or details is using less than half the width of the build history + // widget. + badgesOverflowing = true; + nameLessThanHalf = (nameOverflowParams.scrollWidth < usableRowWidth/2); + detailsLessThanHalf = (detailsOverflowParams.scrollWidth < usableRowWidth/2); + } + } + function expandLeftWithRight(leftCellOverFlowParams, rightCellOverflowParams) { + // Float them left and right... + $(leftCellOverFlowParams.element).setStyle({float: 'left'}); + $(rightCellOverflowParams.element).setStyle({float: 'right'}); + + if (!leftCellOverFlowParams.isOverflowed && !rightCellOverflowParams.isOverflowed) { + // If neither left nor right are overflowed, just leave as is and let them float left and right. + return; + } + if (leftCellOverFlowParams.isOverflowed && !rightCellOverflowParams.isOverflowed) { + $(leftCellOverFlowParams.element).setStyle({width: leftCellOverFlowParams.scrollWidth + 'px'}); + return; + } + if (!leftCellOverFlowParams.isOverflowed && rightCellOverflowParams.isOverflowed) { + $(rightCellOverflowParams.element).setStyle({width: rightCellOverflowParams.scrollWidth + 'px'}); + return; + } + } + + if ((!badgesOverflowing || nameLessThanHalf) && + (nameOverflowParams.scrollWidth + controlsOverflowParams.scrollWidth <= usableRowWidth)) { + // Build name and controls can go on one row (first row). Need to move build details down + // to a row of its own (second row) by making it a block element, forcing it to wrap. If there + // are controls, we move them up to position them after the build name by inserting before the + // build details. + Element.addClassName(buildDetails, "block"); + buildControls.parentNode.removeChild(buildControls); + buildDetails.parentNode.insertBefore(buildControls, buildDetails); + var wrap = blockWrap(buildName, buildControls); + Element.addClassName(wrap, "build-name-controls"); + indentMultiline(buildDetails); + nameOverflowParams = getElementOverflowParams(buildName); // recalculate + expandLeftWithRight(nameOverflowParams, controlsOverflowParams); + setBuildControlWidths(); + fitToControlsHeight(buildName); + } else if ((!badgesOverflowing || detailsLessThanHalf) && + (detailsOverflowParams.scrollWidth + controlsOverflowParams.scrollWidth <= usableRowWidth)) { + // Build details and controls can go on one row. Need to make the + // build name (first field) a block element, forcing the details and controls to wrap + // onto the next row (creating a second row). + Element.addClassName(buildName, "block"); + var wrap = blockWrap(buildDetails, buildControls); + indentMultiline(wrap); + Element.addClassName(wrap, "build-details-controls"); + $(displayName).setStyle({width: '100%'}); + detailsOverflowParams = getElementOverflowParams(buildDetails); // recalculate + expandLeftWithRight(detailsOverflowParams, controlsOverflowParams); + setBuildControlWidths(); + fitToControlsHeight(buildDetails); + } else { + // No suitable combo fits on a row. All need to go on rows of their own. + Element.addClassName(buildName, "block"); + Element.addClassName(buildDetails, "block"); + Element.addClassName(buildControls, "block"); + indentMultiline(buildDetails); + indentMultiline(buildControls); + nameOverflowParams = getElementOverflowParams(buildName); // recalculate + detailsOverflowParams = getElementOverflowParams(buildDetails); // recalculate + setBuildControlWidths(); + } + controlsRepositioned = true; + } else { + Element.addClassName(buildName, "block"); + Element.addClassName(buildDetails, "block"); + indentMultiline(buildDetails); + } + } + + if (buildControls && !controlsRepositioned) { + var buildBadge = $(buildControls).getElementsBySelector('.build-badge')[0]; + if (buildBadge) { + var badgeOverflowParams = getElementOverflowParams(buildBadge); + + if (badgeOverflowParams.isOverflowed) { + markMultiline(); + indentMultiline(buildControls); + Element.addClassName(buildControls, "block"); + controlsRepositioned = true; + setBuildControlWidths(); + } + } + } + + if (!nameOverflowParams.isOverflowed && !detailsOverflowParams.isOverflowed && !controlsRepositioned) { + fitToControlsHeight(buildName); + fitToControlsHeight(buildDetails); + } + + Element.addClassName(row, "overflow-checked"); + } + + function checkAllRowCellOverflows() { + if(isRunAsTest) { + return; + } + + var bh = $('buildHistory'); + var dataTable = getDataTable(bh); + var rows = dataTable.rows; + + // Insert zero-width spaces in text that may cause overflow distortions. + var displayNames = $(bh).getElementsBySelector('.display-name'); + for (var i = 0; i < displayNames.length; i++) { + insertZeroWidthSpaces(displayNames[i], 2); + } + var descriptions = $(bh).getElementsBySelector('.desc'); + for (var i = 0; i < descriptions.length; i++) { + insertZeroWidthSpaces(descriptions[i], 30); + } + + for (var i = 0; i < rows.length; i++) { + var row = rows[i]; + checkRowCellOverflows(row); + } + } + + var updateBuildsRefreshInterval = 5000; + function updateBuilds() { + if(isPageVisible()){ + if (bh.headers == null) { + // Yahoo.log("Missing headers in buildHistory element"); } new Ajax.Request(ajaxUrl, { @@ -1603,17 +1906,128 @@ function updateBuildHistory(ajaxUrl,nBuild) { // next update bh.headers = ["n",rsp.getResponseHeader("n")]; - window.setTimeout(updateBuilds, 5000); + window.setTimeout(updateBuilds, updateBuildsRefreshInterval); + + checkAllRowCellOverflows(); } }); } else { // Reschedule again - window.setTimeout(updateBuilds, 5000); + window.setTimeout(updateBuilds, updateBuildsRefreshInterval); } } - window.setTimeout(updateBuilds, 5000); + window.setTimeout(updateBuilds, updateBuildsRefreshInterval); + + onPanelResize(function() { + checkAllRowCellOverflows(); + }); + onBuildHistoryChange(function() { + checkAllRowCellOverflows(); + }); } +function getElementOverflowParams(element) { + // First we force it to wrap so we can get those dimension. + // Then we force it to "nowrap", so we can get those dimension. + // We can then compare the two sets, which will indicate if + // wrapping is potentially happening, or not. + + // Force it to wrap. + Element.addClassName(element, "force-wrap"); + var wrappedClientWidth = element.clientWidth; + var wrappedClientHeight = element.clientHeight; + Element.removeClassName(element, "force-wrap"); + + // Force it to nowrap. Return the comparisons. + Element.addClassName(element, "force-nowrap"); + var nowrapClientHeight = element.clientHeight; + try { + var overflowParams = { + element: element, + clientWidth: wrappedClientWidth, + scrollWidth: element.scrollWidth, + isOverflowed: wrappedClientHeight > nowrapClientHeight + }; + return overflowParams; + } finally { + Element.removeClassName(element, "force-nowrap"); + } +} + +var zeroWidthSpace = String.fromCharCode(8203); +function insertZeroWidthSpaces(element, maxWordSize) { + if (Element.hasClassName(element, 'zws-inserted')) { + // already done. + return; + } + + var words = element.textContent.split(/\s+/); + var newTextContent = ''; + + var splitRegex = new RegExp('.{1,' + maxWordSize + '}', 'g'); + for (var i = 0; i < words.length; i++) { + var word = words[i]; + var wordTokens = word.match(splitRegex); + if (wordTokens) { + for (var ii = 0; ii < wordTokens.length; ii++) { + if (newTextContent.length === 0) { + newTextContent += wordTokens[ii]; + } else { + newTextContent += zeroWidthSpace + wordTokens[ii]; + } + } + } else { + newTextContent += word; + } + newTextContent += ' '; + } + + element.textContent = newTextContent; + Element.addClassName(element, 'zws-inserted'); +} +function removeZeroWidthSpaces(element) { + if (element) { + element.textContent = element.textContent.replace(zeroWidthSpace, ''); + Element.removeClassName(element, 'zws-inserted'); + } +} + +function onPanelResize(handler) { + Event.observe(window, 'jenkins:panelResized', handler); +} +function firePanelResized() { + Event.fire(window, 'jenkins:panelResized'); +} + +Element.observe(document, 'dom:loaded', function(){ + if(isRunAsTest) { + return; + } + + var fixedSidePanelWidth = 360; + var pageBody = $('page-body'); + var sidePanel = $(pageBody).getElementsBySelector('#side-panel')[0]; + var mainPanel = $(pageBody).getElementsBySelector('#main-panel')[0]; + + function doPanelResize() { + var pageBodyWidth = Element.getWidth(pageBody); + if (pageBodyWidth > 768) { + $(sidePanel).setAttribute('style', 'width: ' + fixedSidePanelWidth + 'px;'); + $(mainPanel).setAttribute('style', 'width: ' + (pageBodyWidth - fixedSidePanelWidth - 5) + 'px;'); + } else { + $(sidePanel).removeAttribute('style'); + $(mainPanel).removeAttribute('style'); + } + + firePanelResized(); + } + doPanelResize(); + + Event.observe(window, 'resize', function() { + doPanelResize(); + }); +}); + // get the cascaded computed style value. 'a' is the style name like 'backgroundColor' function getStyle(e,a){ if(document.defaultView && document.defaultView.getComputedStyle) From a82b5e195b5ffa501eeb236d15dbb506421435aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20Gond=C5=BEa?= Date: Wed, 21 Jan 2015 00:05:54 +0100 Subject: [PATCH 046/272] Revert "Single/Multiline Build History" This reverts commit 3603b69938e72bd7a03dd431cd1ac7066a5646ef. --- .../widgets/BuildHistoryWidget/entries.jelly | 54 +-- .../hudson/widgets/HistoryWidget/entry.jelly | 69 ++- .../hudson/widgets/HistoryWidget/index.jelly | 1 - war/src/main/webapp/css/style.css | 132 +----- .../main/webapp/scripts/hudson-behavior.js | 432 +----------------- 5 files changed, 70 insertions(+), 618 deletions(-) diff --git a/core/src/main/resources/hudson/widgets/BuildHistoryWidget/entries.jelly b/core/src/main/resources/hudson/widgets/BuildHistoryWidget/entries.jelly index f84e541e08..80eaf3cbfd 100644 --- a/core/src/main/resources/hudson/widgets/BuildHistoryWidget/entries.jelly +++ b/core/src/main/resources/hudson/widgets/BuildHistoryWidget/entries.jelly @@ -33,38 +33,30 @@ THE SOFTWARE. - - -
-
- -
- -
- #${queuedItems.size()==1 ? it.owner.nextBuildNumber - : it.owner.nextBuildNumber+queuedItems.size()-i-1} -
+ + + + + + #${queuedItems.size()==1 ? it.owner.nextBuildNumber + : it.owner.nextBuildNumber+queuedItems.size()-i-1} + + +
+ + +
-
- - - - (${%pending}—) - - - (${%pending}) - - - ${item.params} -
-
-
- - - -
-
-
+ + + + (${%pending}—) + + + (${%pending}) + + + ${item.params} diff --git a/core/src/main/resources/hudson/widgets/HistoryWidget/entry.jelly b/core/src/main/resources/hudson/widgets/HistoryWidget/entry.jelly index 2283a6603a..8ef23a9da0 100644 --- a/core/src/main/resources/hudson/widgets/HistoryWidget/entry.jelly +++ b/core/src/main/resources/hudson/widgets/HistoryWidget/entry.jelly @@ -29,48 +29,41 @@ THE SOFTWARE. - - - -
- - - - - - - -
-
-
- - - - - - - -
- -
- - - - -
-
-
+ + + + ${build.displayName} + + + + + + + + + -
+
-
+ + + + + + + + + + + + + + + + + diff --git a/core/src/main/resources/hudson/widgets/HistoryWidget/index.jelly b/core/src/main/resources/hudson/widgets/HistoryWidget/index.jelly index f43ba94068..e545de38d0 100644 --- a/core/src/main/resources/hudson/widgets/HistoryWidget/index.jelly +++ b/core/src/main/resources/hudson/widgets/HistoryWidget/index.jelly @@ -88,7 +88,6 @@ THE SOFTWARE. newhist.headers = hist.headers replace(hist, newhist); - fireBuildHistoryChanged(); } }); } diff --git a/war/src/main/webapp/css/style.css b/war/src/main/webapp/css/style.css index a01d3c945a..9947dabaca 100644 --- a/war/src/main/webapp/css/style.css +++ b/war/src/main/webapp/css/style.css @@ -431,6 +431,10 @@ pre.console { width: 480px; } +.build-row { + padding: 3px 4px 3px 4px; +} + .build-keep { font-weight: bold; } @@ -920,140 +924,18 @@ table.parameters > tbody:hover { } #buildHistory .desc { - position: relative; padding: 0; margin-top: 5px; white-space: normal; - opacity: 0.6; - font-style: italic; -} - -#buildHistory .build-row-cell { - position: relative; + max-width: 200px; } #buildHistory .build-rss-links { float: right; } -.build-row { - padding: 3px 4px 3px 4px; -} - -.build-row:hover { - background: #e8e8e8 !important; -} - -.build-row-cell { - font-size: 12px; -} - -.build-row-cell .pane.build-name { - width: 20%; - font-weight: bold; -} -.build-row-cell .pane.build-details { - width: 50%; -} -.build-row-cell .pane.build-controls { - width: 30%; - text-align: right; -} -.build-row-cell .pane.build-details.block { - width: 100%; -} - -.pane.build-name a, -.pane.build-name a:visited { - color: black; - text-decoration: underline; -} - -.pane.build-details a, -.pane.build-details a:visited { - color: black; - opacity: 0.6; - text-decoration: none; -} -.pane.build-details a:hover { - opacity: 1.0; - text-decoration: underline; -} - -.build-row.multi-line .build-row-cell .pane.build-name.block { - width: 100%; -} -.build-row-cell .pane.build-controls.block { - width: 100%; -} - -.build-row-cell .pane.build-name .build-icon, -.build-row-cell .pane.build-name .display-name { - display: inline-block; -} -.build-row-cell .pane.build-name .build-icon { - position: absolute; -} - -.build-row-cell .build-stop { - display: inline-block; - width: 30%; -} -.build-row-cell .build-badge { - display: inline-block; - padding: 2px; - width: 70%; - text-align: right; -} -.build-row .build-name-controls .pane.build-name, -.build-row .build-details-controls .pane.build-details { - width: 70%; -} - -.build-row .build-row-cell .pane { - padding: 0px 2px; /* Sync changes with func expandControlsTo50Percent in hudson-behavior.js */ - display: inline-block; - overflow: hidden; -} - -.build-row.multi-line .build-row-cell .block { - display: block; - overflow: auto; -} - -.build-row.multi-line .build-row-cell .indent-multiline { - margin-top: 5px; -} - -.build-row.multi-line .build-row-cell .left-bar { - position: absolute; - top: 19px; - bottom: 3px; - left: 14px; - border-left: 1px solid #cdcdcd; -} - -.build-row-cell .pane.build-name .display-name, -.build-row-cell .indent-multiline { - padding-left: 20px !important; /* Sync changes with func expandControlsTo50Percent in hudson-behavior.js */ -} - -.build-row .build-row-cell { - visibility: hidden; -} -.build-row.overflow-checked .build-row-cell { - visibility: visible; -} - -/* ================ Element overflow calculation helper styles ================ */ - -.force-wrap, .force-wrap a { - white-space: normal !important; - overflow: visible !important; -} -.force-nowrap, .force-nowrap a { - white-space: nowrap !important; - overflow: hidden !important; +#buildHistory .build-name { + max-width: 120px; } /* ========================= editable combobox style ========================= */ diff --git a/war/src/main/webapp/scripts/hudson-behavior.js b/war/src/main/webapp/scripts/hudson-behavior.js index f7983edbd1..35d61270bf 100644 --- a/war/src/main/webapp/scripts/hudson-behavior.js +++ b/war/src/main/webapp/scripts/hudson-behavior.js @@ -1557,324 +1557,21 @@ Form.findMatchingInput = function(base, name) { return null; // not found } -function onBuildHistoryChange(handler) { - Event.observe(window, 'jenkins:buildHistoryChanged', handler); -} -function fireBuildHistoryChanged() { - Event.fire(window, 'jenkins:buildHistoryChanged'); -} - function updateBuildHistory(ajaxUrl,nBuild) { if(isRunAsTest) return; - var bh = $('buildHistory'); + $('buildHistory').headers = ["n",nBuild]; - bh.headers = ["n",nBuild]; - - function getDataTable(buildHistoryDiv) { - return $(buildHistoryDiv).getElementsBySelector('table.pane')[0]; - } - - var leftRightPadding = 4; - function checkRowCellOverflows(row) { - if (!row) { - return; - } - - if (Element.hasClassName(row, "overflow-checked")) { - // already done. - return; - } - - function markSingleline() { - Element.addClassName(row, "single-line"); - Element.removeClassName(row, "multi-line"); - } - function markMultiline() { - Element.removeClassName(row, "single-line"); - Element.addClassName(row, "multi-line"); - } - function indentMultiline(element) { - Element.addClassName(element, "indent-multiline"); - } - - function blockWrap(el1, el2) { - var div = document.createElement('div'); - - Element.addClassName(div, "block"); - Element.addClassName(div, "wrap"); - Element.addClassName(el1, "wrapped"); - Element.addClassName(el2, "wrapped"); - - el1.parentNode.insertBefore(div, el1); - el1.parentNode.removeChild(el1); - el2.parentNode.removeChild(el2); - div.appendChild(el1); - div.appendChild(el2); - - return div; - } - function blockUnwrap(element) { - var wrapped = $(element).getElementsBySelector('.wrapped'); - for (var i = 0; i < wrapped.length; i++) { - var wrappedEl = wrapped[i]; - wrappedEl.parentNode.removeChild(wrappedEl); - element.parentNode.insertBefore(wrappedEl, element); - Element.removeClassName(wrappedEl, "wrapped"); - } - element.parentNode.removeChild(element); - } - - var buildName = $(row).getElementsBySelector('.build-name')[0]; - var buildDetails = $(row).getElementsBySelector('.build-details')[0]; - - if (!buildName || !buildDetails) { - return; - } - - var displayName = $(buildName).getElementsBySelector('.display-name')[0]; - var buildControls = $(row).getElementsBySelector('.build-controls')[0]; - var desc; - - var descElements = $(row).getElementsBySelector('.desc'); - if (descElements.length > 0) { - desc = descElements[0]; - } - - function resetCellOverflows() { - markSingleline(); - - // undo block wraps - var blockWraps = $(row).getElementsBySelector('.block.wrap'); - for (var i = 0; i < blockWraps.length; i++) { - blockUnwrap(blockWraps[i]); - } - - removeZeroWidthSpaces(displayName); - removeZeroWidthSpaces(desc); - Element.removeClassName(buildName, "block"); - buildName.removeAttribute('style'); - Element.removeClassName(buildDetails, "block"); - buildDetails.removeAttribute('style'); - if (buildControls) { - Element.removeClassName(buildControls, "block"); - buildDetails.removeAttribute('style'); - } - } - - // Undo everything from the previous poll. - resetCellOverflows(); - - // Insert zero-width spaces so as to allow text to wrap, allowing us to get the true clientWidth. - insertZeroWidthSpaces(displayName, 2); - if (desc) { - insertZeroWidthSpaces(desc, 30); - markMultiline(); - } - - var rowWidth = bh.clientWidth; - var usableRowWidth = rowWidth - (leftRightPadding * 2); - var nameOverflowParams = getElementOverflowParams(buildName); - var detailsOverflowParams = getElementOverflowParams(buildDetails); - - var controlsOverflowParams; - if (buildControls) { - controlsOverflowParams = getElementOverflowParams(buildControls); - } - - if (nameOverflowParams.isOverflowed) { - // If the name is overflowed, lets remove the zero-width spaces we added above and - // re-add zero-width spaces with a bigger max word sizes. - removeZeroWidthSpaces(displayName); - insertZeroWidthSpaces(displayName, 20); - } - - function fitToControlsHeight(element) { - if (buildControls) { - if (element.clientHeight < buildControls.clientHeight) { - $(element).setStyle({height: buildControls.clientHeight.toString() + 'px'}); - } - } - } - - function setBuildControlWidths() { - if (buildControls) { - var buildBadge = $(buildControls).getElementsBySelector('.build-badge')[0]; - - if (buildBadge) { - var buildControlsWidth = buildControls.clientWidth; - var buildBadgeWidth; - - var buildStop = $(buildControls).getElementsBySelector('.build-stop')[0]; - if (buildStop) { - $(buildStop).setStyle({width: '24px'}); - // Minus 24 for the buildStop width, - // minus 4 for left+right padding in the controls container - buildBadgeWidth = (buildControlsWidth - 24 - leftRightPadding); - if (Element.hasClassName(buildControls, "indent-multiline")) { - buildBadgeWidth = buildBadgeWidth - 20; - } - $(buildBadge).setStyle({width: (buildBadgeWidth) + 'px'}); - } else { - $(buildBadge).setStyle({width: '100%'}); - } - } - controlsOverflowParams = getElementOverflowParams(buildControls); - } - } - setBuildControlWidths(); - - var controlsRepositioned = false; - - if (nameOverflowParams.isOverflowed || detailsOverflowParams.isOverflowed) { - // At least one of the cells (name or details) needs to move to a row of its own. - - markMultiline(); - - if (buildControls) { - - // We have build controls. Lets see can we find a combination that allows the build controls - // to sit beside either the build name or the build details. - - var badgesOverflowing = false; - var nameLessThanHalf = true; - var detailsLessThanHalf = true; - var buildBadge = $(buildControls).getElementsBySelector('.build-badge')[0]; - if (buildBadge) { - var badgeOverflowParams = getElementOverflowParams(buildBadge); - - if (badgeOverflowParams.isOverflowed) { - // The badges are also overflowing. In this case, we will only attempt to - // put the controls on the same line as the name or details (see below) - // if the name or details is using less than half the width of the build history - // widget. - badgesOverflowing = true; - nameLessThanHalf = (nameOverflowParams.scrollWidth < usableRowWidth/2); - detailsLessThanHalf = (detailsOverflowParams.scrollWidth < usableRowWidth/2); - } - } - function expandLeftWithRight(leftCellOverFlowParams, rightCellOverflowParams) { - // Float them left and right... - $(leftCellOverFlowParams.element).setStyle({float: 'left'}); - $(rightCellOverflowParams.element).setStyle({float: 'right'}); - - if (!leftCellOverFlowParams.isOverflowed && !rightCellOverflowParams.isOverflowed) { - // If neither left nor right are overflowed, just leave as is and let them float left and right. - return; - } - if (leftCellOverFlowParams.isOverflowed && !rightCellOverflowParams.isOverflowed) { - $(leftCellOverFlowParams.element).setStyle({width: leftCellOverFlowParams.scrollWidth + 'px'}); - return; - } - if (!leftCellOverFlowParams.isOverflowed && rightCellOverflowParams.isOverflowed) { - $(rightCellOverflowParams.element).setStyle({width: rightCellOverflowParams.scrollWidth + 'px'}); - return; - } - } - - if ((!badgesOverflowing || nameLessThanHalf) && - (nameOverflowParams.scrollWidth + controlsOverflowParams.scrollWidth <= usableRowWidth)) { - // Build name and controls can go on one row (first row). Need to move build details down - // to a row of its own (second row) by making it a block element, forcing it to wrap. If there - // are controls, we move them up to position them after the build name by inserting before the - // build details. - Element.addClassName(buildDetails, "block"); - buildControls.parentNode.removeChild(buildControls); - buildDetails.parentNode.insertBefore(buildControls, buildDetails); - var wrap = blockWrap(buildName, buildControls); - Element.addClassName(wrap, "build-name-controls"); - indentMultiline(buildDetails); - nameOverflowParams = getElementOverflowParams(buildName); // recalculate - expandLeftWithRight(nameOverflowParams, controlsOverflowParams); - setBuildControlWidths(); - fitToControlsHeight(buildName); - } else if ((!badgesOverflowing || detailsLessThanHalf) && - (detailsOverflowParams.scrollWidth + controlsOverflowParams.scrollWidth <= usableRowWidth)) { - // Build details and controls can go on one row. Need to make the - // build name (first field) a block element, forcing the details and controls to wrap - // onto the next row (creating a second row). - Element.addClassName(buildName, "block"); - var wrap = blockWrap(buildDetails, buildControls); - indentMultiline(wrap); - Element.addClassName(wrap, "build-details-controls"); - $(displayName).setStyle({width: '100%'}); - detailsOverflowParams = getElementOverflowParams(buildDetails); // recalculate - expandLeftWithRight(detailsOverflowParams, controlsOverflowParams); - setBuildControlWidths(); - fitToControlsHeight(buildDetails); - } else { - // No suitable combo fits on a row. All need to go on rows of their own. - Element.addClassName(buildName, "block"); - Element.addClassName(buildDetails, "block"); - Element.addClassName(buildControls, "block"); - indentMultiline(buildDetails); - indentMultiline(buildControls); - nameOverflowParams = getElementOverflowParams(buildName); // recalculate - detailsOverflowParams = getElementOverflowParams(buildDetails); // recalculate - setBuildControlWidths(); - } - controlsRepositioned = true; - } else { - Element.addClassName(buildName, "block"); - Element.addClassName(buildDetails, "block"); - indentMultiline(buildDetails); - } - } - - if (buildControls && !controlsRepositioned) { - var buildBadge = $(buildControls).getElementsBySelector('.build-badge')[0]; - if (buildBadge) { - var badgeOverflowParams = getElementOverflowParams(buildBadge); - - if (badgeOverflowParams.isOverflowed) { - markMultiline(); - indentMultiline(buildControls); - Element.addClassName(buildControls, "block"); - controlsRepositioned = true; - setBuildControlWidths(); - } - } - } - - if (!nameOverflowParams.isOverflowed && !detailsOverflowParams.isOverflowed && !controlsRepositioned) { - fitToControlsHeight(buildName); - fitToControlsHeight(buildDetails); - } - - Element.addClassName(row, "overflow-checked"); - } - - function checkAllRowCellOverflows() { - if(isRunAsTest) { - return; - } - - var bh = $('buildHistory'); - var dataTable = getDataTable(bh); - var rows = dataTable.rows; - - // Insert zero-width spaces in text that may cause overflow distortions. - var displayNames = $(bh).getElementsBySelector('.display-name'); - for (var i = 0; i < displayNames.length; i++) { - insertZeroWidthSpaces(displayNames[i], 2); - } - var descriptions = $(bh).getElementsBySelector('.desc'); - for (var i = 0; i < descriptions.length; i++) { - insertZeroWidthSpaces(descriptions[i], 30); - } - - for (var i = 0; i < rows.length; i++) { - var row = rows[i]; - checkRowCellOverflows(row); - } - } - - var updateBuildsRefreshInterval = 5000; function updateBuilds() { if(isPageVisible()){ + var bh = $('buildHistory'); if (bh.headers == null) { // Yahoo.log("Missing headers in buildHistory element"); } + function getDataTable(buildHistoryDiv) { + return $(buildHistoryDiv).getElementsBySelector('table.pane')[0]; + } + new Ajax.Request(ajaxUrl, { requestHeaders: bh.headers, onSuccess: function(rsp) { @@ -1906,128 +1603,17 @@ function updateBuildHistory(ajaxUrl,nBuild) { // next update bh.headers = ["n",rsp.getResponseHeader("n")]; - window.setTimeout(updateBuilds, updateBuildsRefreshInterval); - - checkAllRowCellOverflows(); + window.setTimeout(updateBuilds, 5000); } }); } else { // Reschedule again - window.setTimeout(updateBuilds, updateBuildsRefreshInterval); + window.setTimeout(updateBuilds, 5000); } } - window.setTimeout(updateBuilds, updateBuildsRefreshInterval); - - onPanelResize(function() { - checkAllRowCellOverflows(); - }); - onBuildHistoryChange(function() { - checkAllRowCellOverflows(); - }); + window.setTimeout(updateBuilds, 5000); } -function getElementOverflowParams(element) { - // First we force it to wrap so we can get those dimension. - // Then we force it to "nowrap", so we can get those dimension. - // We can then compare the two sets, which will indicate if - // wrapping is potentially happening, or not. - - // Force it to wrap. - Element.addClassName(element, "force-wrap"); - var wrappedClientWidth = element.clientWidth; - var wrappedClientHeight = element.clientHeight; - Element.removeClassName(element, "force-wrap"); - - // Force it to nowrap. Return the comparisons. - Element.addClassName(element, "force-nowrap"); - var nowrapClientHeight = element.clientHeight; - try { - var overflowParams = { - element: element, - clientWidth: wrappedClientWidth, - scrollWidth: element.scrollWidth, - isOverflowed: wrappedClientHeight > nowrapClientHeight - }; - return overflowParams; - } finally { - Element.removeClassName(element, "force-nowrap"); - } -} - -var zeroWidthSpace = String.fromCharCode(8203); -function insertZeroWidthSpaces(element, maxWordSize) { - if (Element.hasClassName(element, 'zws-inserted')) { - // already done. - return; - } - - var words = element.textContent.split(/\s+/); - var newTextContent = ''; - - var splitRegex = new RegExp('.{1,' + maxWordSize + '}', 'g'); - for (var i = 0; i < words.length; i++) { - var word = words[i]; - var wordTokens = word.match(splitRegex); - if (wordTokens) { - for (var ii = 0; ii < wordTokens.length; ii++) { - if (newTextContent.length === 0) { - newTextContent += wordTokens[ii]; - } else { - newTextContent += zeroWidthSpace + wordTokens[ii]; - } - } - } else { - newTextContent += word; - } - newTextContent += ' '; - } - - element.textContent = newTextContent; - Element.addClassName(element, 'zws-inserted'); -} -function removeZeroWidthSpaces(element) { - if (element) { - element.textContent = element.textContent.replace(zeroWidthSpace, ''); - Element.removeClassName(element, 'zws-inserted'); - } -} - -function onPanelResize(handler) { - Event.observe(window, 'jenkins:panelResized', handler); -} -function firePanelResized() { - Event.fire(window, 'jenkins:panelResized'); -} - -Element.observe(document, 'dom:loaded', function(){ - if(isRunAsTest) { - return; - } - - var fixedSidePanelWidth = 360; - var pageBody = $('page-body'); - var sidePanel = $(pageBody).getElementsBySelector('#side-panel')[0]; - var mainPanel = $(pageBody).getElementsBySelector('#main-panel')[0]; - - function doPanelResize() { - var pageBodyWidth = Element.getWidth(pageBody); - if (pageBodyWidth > 768) { - $(sidePanel).setAttribute('style', 'width: ' + fixedSidePanelWidth + 'px;'); - $(mainPanel).setAttribute('style', 'width: ' + (pageBodyWidth - fixedSidePanelWidth - 5) + 'px;'); - } else { - $(sidePanel).removeAttribute('style'); - $(mainPanel).removeAttribute('style'); - } - - firePanelResized(); - } - doPanelResize(); - - Event.observe(window, 'resize', function() { - doPanelResize(); - }); -}); - // get the cascaded computed style value. 'a' is the style name like 'backgroundColor' function getStyle(e,a){ if(document.defaultView && document.defaultView.getComputedStyle) From 49ffccaf030ec85a83f22a86e76a4e94ec314fa2 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Tue, 27 Jan 2015 17:38:15 -0800 Subject: [PATCH 047/272] [maven-release-plugin] prepare release jenkins-1.580.3 --- cli/pom.xml | 2 +- core/pom.xml | 2 +- plugins/pom.xml | 10 +++++----- pom.xml | 4 ++-- test/pom.xml | 2 +- war/pom.xml | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/cli/pom.xml b/cli/pom.xml index d9a61ed571..5afd781d3d 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ pom org.jenkins-ci.main - 1.580.3-SNAPSHOT + 1.580.3 cli diff --git a/core/pom.xml b/core/pom.xml index ce4a07d044..26ed10edcf 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 1.580.3-SNAPSHOT + 1.580.3 ../pom.xml diff --git a/plugins/pom.xml b/plugins/pom.xml index 463ad48e7e..48ec9035c4 100644 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -11,7 +11,7 @@ org.jenkins-ci.plugins plugin Jenkins plugin POM - 1.580.3-SNAPSHOT + 1.580.3 pom -