diff --git a/changelog.html b/changelog.html index ba3c08d119a57aaa3bcb924aca0c41c22f1cf000..4ef9ac3733341ebb4a9f6170e7106d79b3ae8ac4 100644 --- a/changelog.html +++ b/changelog.html @@ -61,6 +61,9 @@ Upcoming changes
  • Pass full list of all possible jobs to ViewJobFilter when recurse option is set (issue 20143) +
  • + get-job and update-job CLI commands can now work with folders, or indeed any AbstractItem. + (issue 20236)
  • Added API allowing plugins to hide entries from the context menu even while they appear in the sidepanel. (issue 19173) diff --git a/core/src/main/java/hudson/cli/GetJobCommand.java b/core/src/main/java/hudson/cli/GetJobCommand.java index 0b8223442cb9ec78f2a5b9189254d126552f7c78..efc1e90836927f593b6016c53615634d20d67978 100644 --- a/core/src/main/java/hudson/cli/GetJobCommand.java +++ b/core/src/main/java/hudson/cli/GetJobCommand.java @@ -24,7 +24,7 @@ package hudson.cli; import hudson.Extension; -import hudson.model.AbstractProject; +import hudson.model.AbstractItem; import hudson.model.Item; import hudson.util.IOUtils; import org.kohsuke.args4j.Argument; @@ -35,7 +35,7 @@ import org.kohsuke.args4j.Argument; @Extension public class GetJobCommand extends CLICommand { @Argument(metaVar="JOB",usage="Name of the job",required=true) - public AbstractProject job; + public AbstractItem job; @Override public String getShortDescription() { diff --git a/core/src/main/java/hudson/cli/UpdateJobCommand.java b/core/src/main/java/hudson/cli/UpdateJobCommand.java index eaea4227725943861748e87bae1f133bb5651503..a9a34403ec03699296d900e103a498efdccfa008 100644 --- a/core/src/main/java/hudson/cli/UpdateJobCommand.java +++ b/core/src/main/java/hudson/cli/UpdateJobCommand.java @@ -24,7 +24,7 @@ package hudson.cli; import hudson.Extension; -import hudson.model.AbstractProject; +import hudson.model.AbstractItem; import org.kohsuke.args4j.Argument; import javax.xml.transform.Source; @@ -36,7 +36,7 @@ import javax.xml.transform.stream.StreamSource; @Extension public class UpdateJobCommand extends CLICommand { @Argument(metaVar="JOB",usage="Name of the job",required=true) - public AbstractProject job; + public AbstractItem job; @Override public String getShortDescription() { diff --git a/core/src/main/java/hudson/cli/handlers/AbstractItemOptionHandler.java b/core/src/main/java/hudson/cli/handlers/AbstractItemOptionHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..c99f3090799e291487bff797f9f2b457c26bfb84 --- /dev/null +++ b/core/src/main/java/hudson/cli/handlers/AbstractItemOptionHandler.java @@ -0,0 +1,48 @@ +/* + * The MIT License + * + * Copyright 2013 Jesse Glick. + * + * 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.cli.handlers; + +import hudson.model.AbstractItem; +import org.kohsuke.MetaInfServices; +import org.kohsuke.args4j.CmdLineParser; +import org.kohsuke.args4j.OptionDef; +import org.kohsuke.args4j.spi.OptionHandler; +import org.kohsuke.args4j.spi.Setter; + +/** + * Refers to an {@link AbstractItem} by name. + * @since 1.538 + */ +@MetaInfServices(OptionHandler.class) public class AbstractItemOptionHandler extends GenericItemOptionHandler { + + public AbstractItemOptionHandler(CmdLineParser parser, OptionDef option, Setter setter) { + super(parser, option, setter); + } + + @Override protected Class type() { + return AbstractItem.class; + } + +} diff --git a/core/src/main/java/hudson/cli/handlers/AbstractProjectOptionHandler.java b/core/src/main/java/hudson/cli/handlers/AbstractProjectOptionHandler.java index 0dc515751ca8e16b7c78d92c8327dd7ff0c0d83e..b630e48b56abe656345304b8a7f7acc339c1cfe2 100644 --- a/core/src/main/java/hudson/cli/handlers/AbstractProjectOptionHandler.java +++ b/core/src/main/java/hudson/cli/handlers/AbstractProjectOptionHandler.java @@ -24,42 +24,26 @@ package hudson.cli.handlers; import hudson.model.AbstractProject; -import jenkins.model.Jenkins; -import org.kohsuke.args4j.CmdLineException; import org.kohsuke.args4j.CmdLineParser; import org.kohsuke.args4j.OptionDef; -import org.kohsuke.args4j.spi.OptionHandler; -import org.kohsuke.args4j.spi.Parameters; import org.kohsuke.args4j.spi.Setter; import org.kohsuke.MetaInfServices; +import org.kohsuke.args4j.spi.OptionHandler; /** * Refer to {@link AbstractProject} by its name. * * @author Kohsuke Kawaguchi */ -@MetaInfServices +@MetaInfServices(OptionHandler.class) @SuppressWarnings("rawtypes") -public class AbstractProjectOptionHandler extends OptionHandler { +public class AbstractProjectOptionHandler extends GenericItemOptionHandler { public AbstractProjectOptionHandler(CmdLineParser parser, OptionDef option, Setter setter) { super(parser, option, setter); } - @Override - public int parseArguments(Parameters params) throws CmdLineException { - Jenkins h = Jenkins.getInstance(); - String src = params.getParameter(0); - - AbstractProject s = h.getItemByFullName(src,AbstractProject.class); - if (s==null) { - AbstractProject nearest = AbstractProject.findNearest(src); - if (nearest!=null) - throw new CmdLineException(owner, "No such job '"+src+"' perhaps you meant '"+ nearest.getFullName() +"'?"); - else - throw new CmdLineException(owner, "No such job '"+src+"'"); - } - setter.addValue(s); - return 1; + @Override protected Class type() { + return AbstractProject.class; } @Override diff --git a/core/src/main/java/hudson/cli/handlers/GenericItemOptionHandler.java b/core/src/main/java/hudson/cli/handlers/GenericItemOptionHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..502d65f9254e21b31653fd17334370cefdf27c00 --- /dev/null +++ b/core/src/main/java/hudson/cli/handlers/GenericItemOptionHandler.java @@ -0,0 +1,72 @@ +/* + * The MIT License + * + * Copyright 2013 Jesse Glick. + * + * 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.cli.handlers; + +import hudson.model.Item; +import hudson.model.Items; +import jenkins.model.Jenkins; +import org.kohsuke.args4j.CmdLineException; +import org.kohsuke.args4j.CmdLineParser; +import org.kohsuke.args4j.OptionDef; +import org.kohsuke.args4j.spi.OptionHandler; +import org.kohsuke.args4j.spi.Parameters; +import org.kohsuke.args4j.spi.Setter; + +/** + * Refers to an {@link Item} by its name. + * May be subclassed to handle specific kinds of items. + * (Use {@code @MetaInfServices(OptionHandler.class)} to register the subclass.) + * @param the kind of item being handled + * @since 1.538 + */ +public abstract class GenericItemOptionHandler extends OptionHandler { + + protected GenericItemOptionHandler(CmdLineParser parser, OptionDef option, Setter setter) { + super(parser, option, setter); + } + + protected abstract Class type(); + + @Override public int parseArguments(Parameters params) throws CmdLineException { + Jenkins j = Jenkins.getInstance(); + String src = params.getParameter(0); + T s = j.getItemByFullName(src, type()); + if (s == null) { + T nearest = Items.findNearest(type(), src, j); + if (nearest != null) { + throw new CmdLineException(owner, "No such job '" + src + "'; perhaps you meant '" + nearest.getFullName() + "'?"); + } else { + throw new CmdLineException(owner, "No such job '" + src + "'"); + } + } + setter.addValue(s); + return 1; + } + + @Override public String getDefaultMetaVariable() { + return "ITEM"; + } + +} diff --git a/core/src/main/java/hudson/cli/handlers/TopLevelItemOptionHandler.java b/core/src/main/java/hudson/cli/handlers/TopLevelItemOptionHandler.java index 0c4a4ea97869db3f6976869405256a328c0b128e..97fdf4b8c5b648585af94cb233a515529462fb3d 100644 --- a/core/src/main/java/hudson/cli/handlers/TopLevelItemOptionHandler.java +++ b/core/src/main/java/hudson/cli/handlers/TopLevelItemOptionHandler.java @@ -1,14 +1,10 @@ package hudson.cli.handlers; -import hudson.model.AbstractProject; -import jenkins.model.Jenkins; import hudson.model.TopLevelItem; import org.kohsuke.MetaInfServices; -import org.kohsuke.args4j.CmdLineException; import org.kohsuke.args4j.CmdLineParser; import org.kohsuke.args4j.OptionDef; import org.kohsuke.args4j.spi.OptionHandler; -import org.kohsuke.args4j.spi.Parameters; import org.kohsuke.args4j.spi.Setter; /** @@ -16,33 +12,18 @@ import org.kohsuke.args4j.spi.Setter; * * @author Kohsuke Kawaguchi */ -@MetaInfServices -public class TopLevelItemOptionHandler extends OptionHandler { +@MetaInfServices(OptionHandler.class) +public class TopLevelItemOptionHandler extends GenericItemOptionHandler { public TopLevelItemOptionHandler(CmdLineParser parser, OptionDef option, Setter setter) { super(parser, option, setter); } - @Override - @SuppressWarnings("rawtypes") - public int parseArguments(Parameters params) throws CmdLineException { - Jenkins h = Jenkins.getInstance(); - String src = params.getParameter(0); - - TopLevelItem s = h.getItemByFullName(src, TopLevelItem.class); - if (s==null) { - AbstractProject nearest = AbstractProject.findNearest(src); - if (nearest!=null) - throw new CmdLineException(owner, "No such job '"+src+"' perhaps you meant '"+ nearest.getFullName() +"'?"); - else - throw new CmdLineException(owner, "No such job '"+src+"'"); - } - - setter.addValue(s); - return 1; + @Override protected Class type() { + return TopLevelItem.class; } @Override public String getDefaultMetaVariable() { - return "JOB"; + return "JOB"; // TODO or should we pick up default value, ITEM? } } diff --git a/core/src/main/java/hudson/model/AbstractProject.java b/core/src/main/java/hudson/model/AbstractProject.java index a1f9eb8d0e4ab09270edbfb8a69c4a3fa6f653de..9fbcfda90b407701b73bc65b9497adbf19a0068a 100644 --- a/core/src/main/java/hudson/model/AbstractProject.java +++ b/core/src/main/java/hudson/model/AbstractProject.java @@ -80,7 +80,6 @@ import hudson.triggers.TriggerDescriptor; import hudson.util.AlternativeUiTextProvider; import hudson.util.AlternativeUiTextProvider.Message; import hudson.util.DescribableList; -import hudson.util.EditDistance; import hudson.util.FormValidation; import hudson.widgets.BuildHistoryWidget; import hudson.widgets.HistoryWidget; @@ -2299,6 +2298,7 @@ public abstract class AbstractProject

    ,R extends A /** * Finds a {@link AbstractProject} that has the name closest to the given name. + * @see Items#findNearest */ public static AbstractProject findNearest(String name) { return findNearest(name,Hudson.getInstance()); @@ -2308,15 +2308,10 @@ public abstract class AbstractProject

    ,R extends A * Finds a {@link AbstractProject} whose name (when referenced from the specified context) is closest to the given name. * * @since 1.419 + * @see Items#findNearest */ public static AbstractProject findNearest(String name, ItemGroup context) { - List projects = Hudson.getInstance().getAllItems(AbstractProject.class); - String[] names = new String[projects.size()]; - for( int i=0; i REVERSE_INTEGER_COMPARATOR = new Comparator() { diff --git a/core/src/main/java/hudson/model/Items.java b/core/src/main/java/hudson/model/Items.java index 746a5aa4fbba066714aa45289c943be9cce23c41..5ca561beaa59cd96aee46e8cc0c95b266c918139 100644 --- a/core/src/main/java/hudson/model/Items.java +++ b/core/src/main/java/hudson/model/Items.java @@ -32,6 +32,7 @@ import hudson.XmlFile; import hudson.matrix.Axis; import hudson.triggers.Trigger; import hudson.util.DescriptorList; +import hudson.util.EditDistance; import hudson.util.XStream2; import jenkins.model.Jenkins; import org.apache.commons.lang.StringUtils; @@ -39,6 +40,7 @@ import org.apache.commons.lang.StringUtils; import java.io.File; import java.io.IOException; import java.util.*; +import javax.annotation.CheckForNull; /** * Convenience methods related to {@link Item}. @@ -279,6 +281,25 @@ public class Items { return r; } + /** + * Finds an item whose name (when referenced from the specified context) is closest to the given name. + * @param the type of item being considered + * @param type same as {@code T} + * @param name the supplied name + * @param context a context to start from (used to compute relative names) + * @return the closest available item + * @since 1.538 + */ + public static @CheckForNull T findNearest(Class type, String name, ItemGroup context) { + List projects = Jenkins.getInstance().getAllItems(type); + String[] names = new String[projects.size()]; + for (int i = 0; i < projects.size(); i++) { + names[i] = projects.get(i).getRelativeNameFrom(context); + } + String nearest = EditDistance.findNearest(name, names); + return Jenkins.getInstance().getItem(nearest, context, type); + } + /** * Used to load/save job configuration. * diff --git a/test/src/test/java/hudson/cli/GetJobCommandTest.java b/test/src/test/java/hudson/cli/GetJobCommandTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2ecbf318ba5435d40b663fc1e3a73bedf3abebd0 --- /dev/null +++ b/test/src/test/java/hudson/cli/GetJobCommandTest.java @@ -0,0 +1,66 @@ +/* + * The MIT License + * + * Copyright 2012 Jesse Glick. + * + * 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.cli; + +import edu.umd.cs.findbugs.annotations.SuppressWarnings; +import hudson.model.FreeStyleProject; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.Collections; +import java.util.Locale; +import org.apache.commons.io.input.NullInputStream; +import static org.junit.Assert.*; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.Bug; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.MockFolder; + +@SuppressWarnings("DM_DEFAULT_ENCODING") +public class GetJobCommandTest { + + @Rule public JenkinsRule j = new JenkinsRule(); + + @Bug(20236) + @Test public void withFolders() throws Exception { + MockFolder d = j.createFolder("d"); + FreeStyleProject p = d.createProject(FreeStyleProject.class, "p"); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + PrintStream outS = new PrintStream(out); + int result = new GetJobCommand().main(Collections.singletonList("d/p"), Locale.ENGLISH, new NullInputStream(0), outS, outS); + outS.flush(); + String output = out.toString(); + assertEquals(output, 0, result); + assertEquals(p.getConfigFile().asString(), output); + out = new ByteArrayOutputStream(); + outS = new PrintStream(out); + result = new GetJobCommand().main(Collections.singletonList("d"), Locale.ENGLISH, new NullInputStream(0), outS, outS); + outS.flush(); + output = out.toString(); + assertEquals(output, 0, result); + assertEquals(d.getConfigFile().asString(), output); + } + +}