提交 4e9443d4 编写于 作者: J Jesse Glick

Merge branch 'master' into Behaviour-specify-14495

...@@ -48,6 +48,7 @@ import java.util.HashMap; ...@@ -48,6 +48,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.io.FileNotFoundException;
import java.io.PrintStream; import java.io.PrintStream;
import jenkins.model.Jenkins; import jenkins.model.Jenkins;
...@@ -82,6 +83,12 @@ public class BuildCommand extends CLICommand { ...@@ -82,6 +83,12 @@ public class BuildCommand extends CLICommand {
@Option(name="-v",usage="Prints out the console output of the build. Use with -s") @Option(name="-v",usage="Prints out the console output of the build. Use with -s")
public boolean consoleOutput = false; public boolean consoleOutput = false;
@Option(name="-r", usage="Number of times to retry reading of the output log if it does not exists on first attempt. Defaults to 0. Use with -v.")
public String retryCntStr = "0";
// hold parsed retryCnt;
private int retryCnt = 0;
protected int run() throws Exception { protected int run() throws Exception {
job.checkPermission(Item.BUILD); job.checkPermission(Item.BUILD);
...@@ -114,6 +121,8 @@ public class BuildCommand extends CLICommand { ...@@ -114,6 +121,8 @@ public class BuildCommand extends CLICommand {
a = new ParametersAction(values); a = new ParametersAction(values);
} }
retryCnt = Integer.parseInt(retryCntStr);
if (checkSCM) { if (checkSCM) {
if (job.poll(new StreamTaskListener(stdout, getClientCharset())).change == Change.NONE) { if (job.poll(new StreamTaskListener(stdout, getClientCharset())).change == Change.NONE) {
return 0; return 0;
...@@ -128,7 +137,24 @@ public class BuildCommand extends CLICommand { ...@@ -128,7 +137,24 @@ public class BuildCommand extends CLICommand {
if (sync) { if (sync) {
if (consoleOutput) { if (consoleOutput) {
b.writeWholeLogTo(stdout); // read output in a retry loop, by default try only once
// writeWholeLogTo may fail with FileNotFound
// exception on a slow/busy machine, if it takes
// longish to create the log file
int retryInterval = 100;
for (int i=0;i<=retryCnt;) {
try {
b.writeWholeLogTo(stdout);
break;
}
catch (FileNotFoundException e) {
if ( i == retryCnt ) {
throw e;
}
i++;
Thread.sleep(retryInterval);
}
}
} }
// TODO: should we abort the build if the CLI is cancelled? // TODO: should we abort the build if the CLI is cancelled?
f.get(); // wait for the completion f.get(); // wait for the completion
......
...@@ -23,9 +23,12 @@ ...@@ -23,9 +23,12 @@
*/ */
package hudson.cli; package hudson.cli;
import hudson.model.ModifiableItemGroup;
import hudson.model.TopLevelItem;
import jenkins.model.Jenkins; import jenkins.model.Jenkins;
import hudson.Extension; import hudson.Extension;
import hudson.model.Item; import hudson.model.Item;
import jenkins.model.ModifiableTopLevelItemGroup;
import org.kohsuke.args4j.Argument; import org.kohsuke.args4j.Argument;
/** /**
...@@ -47,12 +50,29 @@ public class CreateJobCommand extends CLICommand { ...@@ -47,12 +50,29 @@ public class CreateJobCommand extends CLICommand {
Jenkins h = Jenkins.getInstance(); Jenkins h = Jenkins.getInstance();
h.checkPermission(Item.CREATE); h.checkPermission(Item.CREATE);
if (h.getItem(name)!=null) { if (h.getItemByFullName(name)!=null) {
stderr.println("Job '"+name+"' already exists"); stderr.println("Job '"+name+"' already exists");
return -1; return -1;
} }
h.createProjectFromXML(name,stdin); ModifiableTopLevelItemGroup ig = h;
int i = name.lastIndexOf('/');
if (i > 0) {
String group = name.substring(0, i);
Item item = h.getItemByFullName(group);
if (item == null) {
throw new IllegalArgumentException("Unknown ItemGroup " + group);
}
if (item instanceof ModifiableTopLevelItemGroup) {
ig = (ModifiableTopLevelItemGroup) item;
} else {
throw new IllegalArgumentException("Can't create job from CLI in " + group);
}
name = name.substring(i + 1);
}
ig.createProjectFromXML(name, stdin);
return 0; return 0;
} }
} }
......
...@@ -10,9 +10,12 @@ import java.util.Collection; ...@@ -10,9 +10,12 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import hudson.PluginManager; import hudson.PluginManager;
import hudson.util.DirScanner;
import hudson.util.FileVisitor;
import hudson.util.Service; import hudson.util.Service;
/** /**
...@@ -65,20 +68,31 @@ public class InitStrategy { ...@@ -65,20 +68,31 @@ public class InitStrategy {
} }
/** /**
* Lists up additional bundled plugins from the system property. * Lists up additional bundled plugins from the system property {@code hudson.bundled.plugins}.
* * Since 1.480 glob syntax is supported.
* For use in the "mvn hudson-dev:run". * For use in the "mvn hudson-dev:run".
* TODO: maven-hpi-plugin should inject its own InitStrategy instead of having this in the core. * TODO: maven-hpi-plugin should inject its own InitStrategy instead of having this in the core.
*/ */
protected void getBundledPluginsFromProperty(List<File> r) { protected void getBundledPluginsFromProperty(final List<File> r) {
String hplProperty = System.getProperty("hudson.bundled.plugins"); String hplProperty = System.getProperty("hudson.bundled.plugins");
if (hplProperty != null) { if (hplProperty != null) {
for (String hplLocation : hplProperty.split(",")) { for (String hplLocation : hplProperty.split(",")) {
File hpl = new File(hplLocation.trim()); File hpl = new File(hplLocation.trim());
if (hpl.exists()) if (hpl.exists()) {
r.add(hpl); r.add(hpl);
else } else if (hpl.getName().contains("*")) {
try {
new DirScanner.Glob(hpl.getName(), null).scan(hpl.getParentFile(), new FileVisitor() {
@Override public void visit(File f, String relativePath) throws IOException {
r.add(f);
}
});
} catch (IOException x) {
LOGGER.log(Level.WARNING, "could not expand " + hplLocation, x);
}
} else {
LOGGER.warning("bundled plugin " + hplLocation + " does not exist"); LOGGER.warning("bundled plugin " + hplLocation + " does not exist");
}
} }
} }
} }
......
...@@ -306,7 +306,7 @@ import javax.annotation.CheckForNull; ...@@ -306,7 +306,7 @@ import javax.annotation.CheckForNull;
* @author Kohsuke Kawaguchi * @author Kohsuke Kawaguchi
*/ */
@ExportedBean @ExportedBean
public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLevelItem>, StaplerProxy, StaplerFallback, ViewGroup, AccessControlled, DescriptorByNameOwner, ModelObjectWithContextMenu { public class Jenkins extends AbstractCIBase implements ModifiableTopLevelItemGroup, StaplerProxy, StaplerFallback, ViewGroup, AccessControlled, DescriptorByNameOwner, ModelObjectWithContextMenu {
private transient final Queue queue; private transient final Queue queue;
/** /**
...@@ -2259,7 +2259,7 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe ...@@ -2259,7 +2259,7 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe
* null if either such {@link Item} doesn't exist under the given full name, * null if either such {@link Item} doesn't exist under the given full name,
* or it exists but it's no an instance of the given type. * or it exists but it's no an instance of the given type.
*/ */
public <T extends Item> T getItemByFullName(String fullName, Class<T> type) { public @CheckForNull <T extends Item> T getItemByFullName(String fullName, Class<T> type) {
StringTokenizer tokens = new StringTokenizer(fullName,"/"); StringTokenizer tokens = new StringTokenizer(fullName,"/");
ItemGroup parent = this; ItemGroup parent = this;
...@@ -2284,7 +2284,7 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe ...@@ -2284,7 +2284,7 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe
} }
} }
public Item getItemByFullName(String fullName) { public @CheckForNull Item getItemByFullName(String fullName) {
return getItemByFullName(fullName,Item.class); return getItemByFullName(fullName,Item.class);
} }
...@@ -2298,24 +2298,10 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe ...@@ -2298,24 +2298,10 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe
return User.get(name,hasPermission(ADMINISTER)); return User.get(name,hasPermission(ADMINISTER));
} }
/**
* Creates a new job.
*
* @throws IllegalArgumentException
* if the project of the given name already exists.
*/
public synchronized TopLevelItem createProject( TopLevelItemDescriptor type, String name ) throws IOException { public synchronized TopLevelItem createProject( TopLevelItemDescriptor type, String name ) throws IOException {
return createProject(type, name, true); return createProject(type, name, true);
} }
/**
* Creates a new job.
* @param type Descriptor for job type
* @param name Name for job
* @param notify Whether to fire onCreated method for all ItemListeners
* @throws IllegalArgumentException
* if a project of the give name already exists.
*/
public synchronized TopLevelItem createProject( TopLevelItemDescriptor type, String name, boolean notify ) throws IOException { public synchronized TopLevelItem createProject( TopLevelItemDescriptor type, String name, boolean notify ) throws IOException {
return itemGroupMixIn.createProject(type,name,notify); return itemGroupMixIn.createProject(type,name,notify);
} }
...@@ -2805,24 +2791,13 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe ...@@ -2805,24 +2791,13 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe
} }
/** /**
* Creates a new job from its configuration XML. The type of the job created will be determined by
* what's in this XML.
* @since 1.319 * @since 1.319
*/ */
public TopLevelItem createProjectFromXML(String name, InputStream xml) throws IOException { public TopLevelItem createProjectFromXML(String name, InputStream xml) throws IOException {
return itemGroupMixIn.createProjectFromXML(name, xml); return itemGroupMixIn.createProjectFromXML(name, xml);
} }
/**
* Copys a job.
*
* @param src
* A {@link TopLevelItem} to be copied.
* @param name
* Name of the newly created project.
* @return
* Newly created {@link TopLevelItem}.
*/
@SuppressWarnings({"unchecked"}) @SuppressWarnings({"unchecked"})
public <T extends TopLevelItem> T copy(T src, String name) throws IOException { public <T extends TopLevelItem> T copy(T src, String name) throws IOException {
return itemGroupMixIn.copy(src, name); return itemGroupMixIn.copy(src, name);
......
/*
* The MIT License
*
* Copyright (c) 2011, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package jenkins.model;
import hudson.model.ModifiableItemGroup;
import hudson.model.TopLevelItem;
import hudson.model.TopLevelItemDescriptor;
import java.io.IOException;
import java.io.InputStream;
/**
* A {@link hudson.model.ModifiableItemGroup} to manage {@link hudson.model.TopLevelItem},
* including copying, creating from descriptor and from XML.
*
* @author <a href="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a>
*/
public interface ModifiableTopLevelItemGroup extends ModifiableItemGroup<TopLevelItem> {
/**
* Copys a job.
*
* @param src
* A {@link TopLevelItem} to be copied.
* @param name
* Name of the newly created project.
* @return
* Newly created {@link TopLevelItem}.
*/
<T extends TopLevelItem> T copy(T src, String name) throws IOException;
/**
* /**
* Creates a new job from its configuration XML. The type of the job created will be determined by
* what's in this XML.
* @param name
* Name of the newly created project.
* @param xml
* Item configuration as xml
* @return
* Newly created {@link TopLevelItem}.
*/
TopLevelItem createProjectFromXML(String name, InputStream xml) throws IOException;
/**
* Creates a new job.
* @param type Descriptor for job type
* @param name Name for job
* @param notify Whether to fire onCreated method for all ItemListeners
* @throws IllegalArgumentException
* if a project of the give name already exists.
*/
TopLevelItem createProject(TopLevelItemDescriptor type, String name, boolean notify) throws IOException;
}
...@@ -234,6 +234,7 @@ THE SOFTWARE. ...@@ -234,6 +234,7 @@ THE SOFTWARE.
<skip>false</skip> <skip>false</skip>
<includes> <includes>
<include>**/UseRecipesWithJenkinsRuleTest.java</include> <include>**/UseRecipesWithJenkinsRuleTest.java</include>
<include>hudson/model/CauseTest.java</include>
</includes> </includes>
</configuration> </configuration>
</plugin> </plugin>
...@@ -277,6 +278,7 @@ THE SOFTWARE. ...@@ -277,6 +278,7 @@ THE SOFTWARE.
</systemProperties> </systemProperties>
<excludes> <excludes>
<exclude>org/jvnet/hudson/main/UseRecipesWithJenkinsRuleTest.class</exclude> <exclude>org/jvnet/hudson/main/UseRecipesWithJenkinsRuleTest.class</exclude>
<exclude>hudson/model/CauseTest.java</exclude>
</excludes> </excludes>
</configuration> </configuration>
</execution> </execution>
......
...@@ -59,7 +59,7 @@ public class JellyTestSuiteBuilder { ...@@ -59,7 +59,7 @@ public class JellyTestSuiteBuilder {
if (res.isDirectory()) { if (res.isDirectory()) {
for (final File jelly : (Collection <File>)FileUtils.listFiles(res,new String[]{"jelly"},true)) for (final File jelly : (Collection <File>)FileUtils.listFiles(res,new String[]{"jelly"},true))
ts.addTest(new JellyCheck(jelly.toURI().toURL(), jct, requirePI)); ts.addTest(new JellyCheck(jelly.toURI().toURL(), jelly.getAbsolutePath().substring(0, (res.getAbsolutePath() + File.separator).length()), jct, requirePI));
} }
if (res.getName().endsWith(".jar")) { if (res.getName().endsWith(".jar")) {
String jarUrl = res.toURI().toURL().toExternalForm(); String jarUrl = res.toURI().toURL().toExternalForm();
...@@ -68,7 +68,7 @@ public class JellyTestSuiteBuilder { ...@@ -68,7 +68,7 @@ public class JellyTestSuiteBuilder {
while (e.hasMoreElements()) { while (e.hasMoreElements()) {
JarEntry ent = e.nextElement(); JarEntry ent = e.nextElement();
if (ent.getName().endsWith(".jelly")) if (ent.getName().endsWith(".jelly"))
ts.addTest(new JellyCheck(new URL("jar:"+jarUrl+"!/"+ent.getName()), jct, requirePI)); ts.addTest(new JellyCheck(new URL("jar:"+jarUrl+"!/"+ent.getName()), ent.getName(), jct, requirePI));
} }
jf.close(); jf.close();
} }
...@@ -80,8 +80,8 @@ public class JellyTestSuiteBuilder { ...@@ -80,8 +80,8 @@ public class JellyTestSuiteBuilder {
private final JellyClassLoaderTearOff jct; private final JellyClassLoaderTearOff jct;
private final boolean requirePI; private final boolean requirePI;
public JellyCheck(URL jelly, JellyClassLoaderTearOff jct, boolean requirePI) { JellyCheck(URL jelly, String name, JellyClassLoaderTearOff jct, boolean requirePI) {
super(jelly.getPath()); super(name);
this.jelly = jelly; this.jelly = jelly;
this.jct = jct; this.jct = jct;
this.requirePI = requirePI; this.requirePI = requirePI;
......
...@@ -84,6 +84,22 @@ public class BuildCommandTest extends HudsonTestCase { ...@@ -84,6 +84,22 @@ public class BuildCommandTest extends HudsonTestCase {
} }
/**
* Tests synchronous execution with retried verbose output
*/
void testSyncWOutputStreaming() {
def p = createFreeStyleProject();
p.buildersList.add(new Shell("sleep 3"));
def cli =new CLI(getURL())
try {
cli.execute(["build","-s","-v","-r","5",p.name])
assertFalse(p.getBuildByNumber(1).isBuilding())
} finally {
cli.close();
}
}
void testParameters() { void testParameters() {
def p = createFreeStyleProject(); def p = createFreeStyleProject();
p.addProperty(new ParametersDefinitionProperty([new StringParameterDefinition("key",null)])); p.addProperty(new ParametersDefinitionProperty([new StringParameterDefinition("key",null)]));
......
...@@ -420,7 +420,7 @@ THE SOFTWARE. ...@@ -420,7 +420,7 @@ THE SOFTWARE.
<value><!-- run "mvn install" once will generate the.hpl --> <value><!-- run "mvn install" once will generate the.hpl -->
${basedir}/../maven-plugin/target/test-classes/the.hpl, ${basedir}/../maven-plugin/target/test-classes/the.hpl,
${basedir}/../ui-samples-plugin/target/test-classes/the.hpl, ${basedir}/../ui-samples-plugin/target/test-classes/the.hpl,
${project.build.directory}/${project.build.finalName}/WEB-INF/plugins/javadoc.hpi ${project.build.directory}/${project.build.finalName}/WEB-INF/plugins/*.hpi
</value> </value>
</systemProperty> </systemProperty>
<systemProperty> <systemProperty>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册