提交 4c6319fe 编写于 作者: K kohsuke

- CLI "build" command now supports passing parameters.

- introduced SimpleParameterDefinition to eliminate the common code

git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@23543 71c3de6d-444a-0410-be80-ed276b4c234a
上级 50fca252
......@@ -26,11 +26,22 @@ package hudson.cli;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.Cause;
import hudson.model.ParametersAction;
import hudson.model.ParameterValue;
import hudson.model.ParametersDefinitionProperty;
import hudson.model.ParameterDefinition;
import hudson.Extension;
import hudson.AbortException;
import hudson.util.EditDistance;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
import java.util.concurrent.Future;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Map.Entry;
import java.io.PrintStream;
/**
......@@ -51,8 +62,32 @@ public class BuildCommand extends CLICommand {
@Option(name="-s",usage="Wait until the completion/abortion of the command")
public boolean sync = false;
@Option(name="-p",usage="Specify the build parameters in the key=value format.")
public Map<String,String> parameters = new HashMap<String, String>();
protected int run() throws Exception {
Future<? extends AbstractBuild> f = job.scheduleBuild2(0, new CLICause());
ParametersAction a = null;
if (!parameters.isEmpty()) {
ParametersDefinitionProperty pdp = job.getProperty(ParametersDefinitionProperty.class);
if (pdp==null)
throw new AbortException(job.getFullDisplayName()+" is not parameterized but the -p option was specified");
List<ParameterValue> values = new ArrayList<ParameterValue>();
for (Entry<String, String> e : parameters.entrySet()) {
String name = e.getKey();
ParameterDefinition pd = pdp.getParameterDefinition(name);
if (pd==null)
throw new AbortException(String.format("\'%s\' is not a valid parameter. Did you mean %s?",
name, EditDistance.findNearest(name, pdp.getParameterDefinitionNames())));
values.add(pd.createValue(this,e.getValue()));
}
a = new ParametersAction(values);
}
Future<? extends AbstractBuild> f = job.scheduleBuild2(0, new CLICause(), a);
if (!sync) return 0;
AbstractBuild b = f.get(); // wait for the completion
......
......@@ -92,7 +92,7 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable {
* (In contrast, calling {@code System.out.println(...)} would print out
* the message to the server log file, which is probably not what you want.
*/
protected transient PrintStream stdout,stderr;
public transient PrintStream stdout,stderr;
/**
* Connected to stdin of the CLI agent.
......@@ -100,18 +100,18 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable {
* <p>
* This input stream is buffered to hide the latency in the remoting.
*/
protected transient InputStream stdin;
public transient InputStream stdin;
/**
* {@link Channel} that represents the CLI JVM. You can use this to
* execute {@link Callable} on the CLI JVM, among other things.
*/
protected transient Channel channel;
public transient Channel channel;
/**
* The locale of the client. Messages should be formatted with this resource.
*/
protected transient Locale locale;
public transient Locale locale;
/**
......@@ -208,6 +208,8 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable {
}
}
/**
* Returns all the registered {@link CLICommand}s.
*/
......
......@@ -638,6 +638,9 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
/**
* Schedules a build of this project, and returns a {@link Future} object
* to wait for the completion of the build.
*
* @param actions
* For the convenience of the caller, this array can contain null, and those will be silently ignored.
*/
public Future<R> scheduleBuild2(int quietPeriod, Cause c, Action... actions) {
if (isDisabled())
......@@ -696,7 +699,7 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
* Schedules a build of this project, and returns a {@link Future} object
* to wait for the completion of the build.
*/
public Future<R> scheduleBuild2(int quietPeriod, Cause c) {
public Future<R> PascheduleBuild2(int quietPeriod, Cause c) {
return scheduleBuild2(quietPeriod, c, new Action[0]);
}
......
......@@ -29,9 +29,11 @@ import net.sf.json.JSONObject;
import hudson.Extension;
/**
* {@link ParameterDefinition} that is either 'true' or 'false'.
*
* @author huybrechts
*/
public class BooleanParameterDefinition extends ParameterDefinition {
public class BooleanParameterDefinition extends SimpleParameterDefinition {
private final boolean defaultValue;
@DataBoundConstructor
......@@ -51,17 +53,8 @@ public class BooleanParameterDefinition extends ParameterDefinition {
return value;
}
@Override
public ParameterValue createValue(StaplerRequest req) {
String[] value = req.getParameterValues(getName());
if (value == null) {
return getDefaultParameterValue();
} else if (value.length != 1) {
throw new IllegalArgumentException("Illegal number of parameter values for " + getName() + ": " + value.length);
} else {
boolean booleanValue = Boolean.parseBoolean(value[0]);
return new BooleanParameterValue(getName(), booleanValue, getDescription());
}
public ParameterValue createValue(String value) {
return new BooleanParameterValue(getName(),Boolean.valueOf(value),getDescription());
}
@Override
......
......@@ -31,7 +31,7 @@ import java.util.Map;
import hudson.util.VariableResolver;
/**
* {@link hudson.model.ParameterValue} created from {@link hudson.model.StringParameterDefinition}.
* {@link ParameterValue} created from {@link BooleanParameterDefinition}.
*/
public class BooleanParameterValue extends ParameterValue {
@Exported(visibility=4)
......
......@@ -6,15 +6,17 @@ import org.kohsuke.stapler.export.Exported;
import org.apache.commons.lang.StringUtils;
import net.sf.json.JSONObject;
import hudson.Extension;
import hudson.cli.CLICommand;
import java.util.ArrayList;
import java.util.List;
import java.util.Arrays;
import java.io.IOException;
/**
* @author huybrechts
*/
public class ChoiceParameterDefinition extends ParameterDefinition {
public class ChoiceParameterDefinition extends SimpleParameterDefinition {
private final List<String> choices;
@DataBoundConstructor
......@@ -49,37 +51,22 @@ public class ChoiceParameterDefinition extends ParameterDefinition {
}
private void checkValue(StringParameterValue value) {
if (!choices.contains(value.value)) {
private StringParameterValue checkValue(StringParameterValue value) {
if (!choices.contains(value.value))
throw new IllegalArgumentException("Illegal choice: " + value.value);
}
return value;
}
@Override
public ParameterValue createValue(StaplerRequest req, JSONObject jo) {
StringParameterValue value = req.bindJSON(StringParameterValue.class, jo);
value.setDescription(getDescription());
checkValue(value);
return value;
return checkValue(value);
}
@Override
public ParameterValue createValue(StaplerRequest req) {
StringParameterValue result;
String[] value = req.getParameterValues(getName());
if (value == null) {
result = getDefaultParameterValue();
} else if (value.length != 1) {
throw new IllegalArgumentException("Illegal number of parameter values for " + getName() + ": " + value.length);
} else {
result = new StringParameterValue(getName(), value[0], getDescription());
}
checkValue(result);
return result;
}
public StringParameterValue createValue(String value) {
return checkValue(new StringParameterValue(getName(), value, getDescription()));
}
@Extension
public static class DescriptorImpl extends ParameterDescriptor {
......
......@@ -27,6 +27,11 @@ import net.sf.json.JSONObject;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.StaplerRequest;
import hudson.Extension;
import hudson.FilePath;
import hudson.cli.CLICommand;
import java.io.IOException;
import java.io.File;
/**
* {@link ParameterDefinition} for doing file upload.
......@@ -66,4 +71,14 @@ public class FileParameterDefinition extends ParameterDefinition {
public ParameterValue createValue(StaplerRequest req) {
throw new UnsupportedOperationException();
}
@Override
public ParameterValue createValue(CLICommand command, String value) throws IOException, InterruptedException {
// capture the file to the server
FilePath src = new FilePath(command.channel,value);
File local = File.createTempFile("hudson","parameter");
src.copyTo(new FilePath(local));
return new FileParameterValue(getName(), local, getDescription());
}
}
......@@ -27,11 +27,19 @@ import org.kohsuke.stapler.DataBoundConstructor;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.io.IOUtils;
import hudson.tasks.BuildWrapper;
import hudson.Launcher;
import hudson.FilePath;
import java.io.IOException;
import java.io.File;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.io.OutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
/**
* {@link ParameterValue} for {@link FileParameterDefinition}.
......@@ -59,6 +67,10 @@ public class FileParameterValue extends ParameterValue {
this.file = file;
}
public FileParameterValue(String name, File file, String desc) {
this(name,new FileItemImpl(file),desc);
}
// post initialization hook
/*package*/ void setLocation(String location) {
this.location = location;
......@@ -111,5 +123,77 @@ public class FileParameterValue extends ParameterValue {
public String getShortDescription() {
return "(FileParameterValue) " + getName() + "='" + file.getName() + "'";
}
/**
* Default implementation from {@link File}.
*/
public static final class FileItemImpl implements FileItem {
private final File file;
public FileItemImpl(File file) {
this.file = file;
}
public InputStream getInputStream() throws IOException {
return new FileInputStream(file);
}
public String getContentType() {
return null;
}
public String getName() {
return file.getName();
}
public boolean isInMemory() {
return false;
}
public long getSize() {
return file.length();
}
public byte[] get() {
try {
return IOUtils.toByteArray(new FileInputStream(file));
} catch (IOException e) {
throw new Error(e);
}
}
public String getString(String encoding) throws UnsupportedEncodingException {
return new String(get(), encoding);
}
public String getString() {
return new String(get());
}
public void write(File to) throws Exception {
new FilePath(file).copyTo(new FilePath(to));
}
public void delete() {
file.delete();
}
public String getFieldName() {
return null;
}
public void setFieldName(String name) {
}
public boolean isFormField() {
return false;
}
public void setFormField(boolean state) {
}
public OutputStream getOutputStream() throws IOException {
return new FileOutputStream(file);
}
}
}
......@@ -28,7 +28,7 @@ import net.sf.json.JSONObject;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.StaplerRequest;
public class JobParameterDefinition extends ParameterDefinition {
public class JobParameterDefinition extends SimpleParameterDefinition {
@DataBoundConstructor
public JobParameterDefinition(String name) {
......@@ -54,9 +54,7 @@ public class JobParameterDefinition extends ParameterDefinition {
return req.bindJSON(JobParameterValue.class, jo);
}
@Override
public ParameterValue createValue(StaplerRequest req) {
throw new UnsupportedOperationException();
}
public ParameterValue createValue(String value) {
return new JobParameterValue(getName(),Hudson.getInstance().getItemByFullName(value,Job.class));
}
}
......@@ -26,9 +26,13 @@ package hudson.model;
import hudson.DescriptorExtensionList;
import hudson.Extension;
import hudson.ExtensionPoint;
import hudson.AbortException;
import hudson.cli.BuildCommand;
import hudson.cli.CLICommand;
import hudson.util.DescriptorList;
import java.io.Serializable;
import java.io.IOException;
import net.sf.json.JSONObject;
......@@ -140,6 +144,25 @@ public abstract class ParameterDefinition implements
*/
public abstract ParameterValue createValue(StaplerRequest req);
/**
* Create a parameter value from the string given in the CLI.
*
* @param command
* This is the command that got the parameter. You can use its {@link CLICommand#channel}
* for interacting with the CLI JVM.
* @throws AbortException
* If the CLI processing should be aborted. Hudson will report the error message
* without stack trace, and then exits this command. Useful for graceful termination.
* @throws Exception
* All the other exceptions cause the stack trace to be dumped, and then
* the command exits with an error code.
* @since 1.334
*/
public ParameterValue createValue(CLICommand command, String value) throws IOException, InterruptedException {
throw new AbortException("CLI parameter submission is not supported for the "+getClass()+" type. Please file a bug report for this");
}
/**
* Returns default parameter value for this definition.
*
......
......@@ -111,6 +111,13 @@ public class ParametersAction implements Action, Iterable<ParameterValue>, Queue
return Collections.unmodifiableList(parameters);
}
public ParameterValue getParameter(String name) {
for (ParameterValue p : parameters)
if (p.getName().equals(name))
return p;
return null;
}
public String getDisplayName() {
return Messages.ParameterAction_DisplayName();
}
......
......@@ -27,6 +27,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.AbstractList;
import javax.servlet.ServletException;
......@@ -70,6 +71,21 @@ public class ParametersDefinitionProperty extends JobProperty<AbstractProject<?,
return parameterDefinitions;
}
/**
* Gets the names of all the parameter definitions.
*/
public List<String> getParameterDefinitionNames() {
return new AbstractList<String>() {
public String get(int index) {
return parameterDefinitions.get(index).getName();
}
public int size() {
return parameterDefinitions.size();
}
};
}
@Override
public Action getJobAction(AbstractProject<?, ?> job) {
return this;
......
......@@ -361,6 +361,8 @@ public class Queue extends ResourceController implements Saveable {
/**
* Schedules an execution of a task.
*
* @param actions
* For the convenience of the caller, this list can contain null, and those will be silently ignored.
* @since 1.311
* @return
* null if this task is already in the queue and therefore the add operation was no-op.
......
......@@ -28,8 +28,11 @@ import net.sf.json.JSONObject;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.StaplerRequest;
import hudson.Extension;
import hudson.cli.CLICommand;
public class RunParameterDefinition extends ParameterDefinition {
import java.io.IOException;
public class RunParameterDefinition extends SimpleParameterDefinition {
private final String projectName;
......@@ -69,7 +72,7 @@ public class RunParameterDefinition extends ParameterDefinition {
public ParameterValue getDefaultParameterValue() {
Run<?,?> lastBuild = getProject().getLastBuild();
if (lastBuild != null) {
return new RunParameterValue(getName(), lastBuild.getExternalizableId(), getDescription());
return createValue(lastBuild.getExternalizableId());
} else {
return null;
}
......@@ -82,17 +85,8 @@ public class RunParameterDefinition extends ParameterDefinition {
return value;
}
@Override
public ParameterValue createValue(StaplerRequest req) {
String[] value = req.getParameterValues(getName());
if (value == null) {
return getDefaultParameterValue();
} else if (value.length != 1) {
throw new IllegalArgumentException("Illegal number of parameter values for " + getName() + ": " + value.length);
} else {
return new RunParameterValue(getName(), value[0], getDescription());
}
}
public RunParameterValue createValue(String value) {
return new RunParameterValue(getName(), value, getDescription());
}
}
package hudson.model;
import org.kohsuke.stapler.StaplerRequest;
import hudson.cli.CLICommand;
import java.io.IOException;
/**
* Convenient base class for {@link ParameterDefinition} whose value can be represented in a context-independent single string token.
*
* @author Kohsuke Kawaguchi
*/
public abstract class SimpleParameterDefinition extends ParameterDefinition {
protected SimpleParameterDefinition(String name) {
super(name);
}
protected SimpleParameterDefinition(String name, String description) {
super(name, description);
}
/**
* Creates a {@link ParameterValue} from the string representation.
*/
public abstract ParameterValue createValue(String value);
@Override
public final ParameterValue createValue(StaplerRequest req) {
String[] value = req.getParameterValues(getName());
if (value == null) {
return getDefaultParameterValue();
} else if (value.length != 1) {
throw new IllegalArgumentException("Illegal number of parameter values for " + getName() + ": " + value.length);
} else {
return createValue(value[0]);
}
}
@Override
public final ParameterValue createValue(CLICommand command, String value) throws IOException, InterruptedException {
return createValue(value);
}
}
......@@ -28,11 +28,14 @@ import net.sf.json.JSONObject;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.StaplerRequest;
import hudson.Extension;
import hudson.cli.CLICommand;
import java.io.IOException;
/**
* Parameter whose value is a string value.
*/
public class StringParameterDefinition extends ParameterDefinition {
public class StringParameterDefinition extends SimpleParameterDefinition {
private String defaultValue;
......@@ -80,15 +83,7 @@ public class StringParameterDefinition extends ParameterDefinition {
return value;
}
@Override
public ParameterValue createValue(StaplerRequest req) {
String[] value = req.getParameterValues(getName());
if (value == null) {
return getDefaultParameterValue();
} else if (value.length != 1) {
throw new IllegalArgumentException("Illegal number of parameter values for " + getName() + ": " + value.length);
} else
return new StringParameterValue(getName(), value[0], getDescription());
}
public ParameterValue createValue(String value) {
return new StringParameterValue(getName(), value, getDescription());
}
}
......@@ -30,6 +30,9 @@ import org.jvnet.hudson.test.TestBuilder
import hudson.model.AbstractBuild
import hudson.Launcher
import hudson.model.BuildListener
import hudson.model.ParametersDefinitionProperty
import hudson.model.StringParameterDefinition
import hudson.model.ParametersAction
/**
* {@link BuildCommand} test.
......@@ -68,4 +71,12 @@ public class BuildCommandTest extends HudsonTestCase {
assertFalse(p.getBuildByNumber(1).isBuilding())
}
void testParameters() {
def p = createFreeStyleProject();
p.addProperty(new ParametersDefinitionProperty([new StringParameterDefinition("key",null)]));
new CLI(getURL()).execute(["build","-s","-p","key=foobar",p.name]);
def b = assertBuildStatusSuccess(p.getBuildByNumber(1));
assertEquals("foobar",b.getAction(ParametersAction.class).getParameter("key").value);
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册