PageRenderTime 26ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/src/main/java/hudson/plugins/mercurial/HgExe.java

https://github.com/davidmc24/mercurial-plugin
Java | 237 lines | 146 code | 30 blank | 61 comment | 12 complexity | 31e529df38a741e1fb39abe5de0d046a MD5 | raw file
  1. /*
  2. * The MIT License
  3. *
  4. * Copyright (c) 2004-2010, InfraDNA, Inc.
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. * THE SOFTWARE.
  23. */
  24. package hudson.plugins.mercurial;
  25. import edu.umd.cs.findbugs.annotations.CheckForNull;
  26. import hudson.AbortException;
  27. import hudson.EnvVars;
  28. import hudson.FilePath;
  29. import hudson.Launcher;
  30. import hudson.Launcher.ProcStarter;
  31. import hudson.model.AbstractBuild;
  32. import hudson.model.Node;
  33. import hudson.model.TaskListener;
  34. import hudson.util.ArgumentListBuilder;
  35. import java.io.ByteArrayOutputStream;
  36. import java.io.IOException;
  37. import java.util.Arrays;
  38. import java.util.Collection;
  39. import java.util.HashMap;
  40. import java.util.LinkedHashSet;
  41. import java.util.List;
  42. import java.util.Map;
  43. import java.util.Set;
  44. import java.util.WeakHashMap;
  45. import java.util.regex.Pattern;
  46. /**
  47. * Encapsulates the invocation of "hg".
  48. *
  49. * <p>
  50. * This reduces the amount of code in the main logic.
  51. *
  52. * TODO: perhaps a subtype 'Repository' with a repository location field would be good.
  53. *
  54. * @author Kohsuke Kawaguchi
  55. */
  56. public class HgExe {
  57. private final ArgumentListBuilder base;
  58. /**
  59. * Environment variables to invoke hg with.
  60. */
  61. private final EnvVars env;
  62. public final Launcher launcher;
  63. public final Node node;
  64. public final TaskListener listener;
  65. private final Capability capability;
  66. public HgExe(MercurialSCM scm, Launcher launcher, AbstractBuild build, TaskListener listener, EnvVars env) throws IOException, InterruptedException {
  67. this(scm,launcher,build.getBuiltOn(),listener,env);
  68. }
  69. public HgExe(MercurialSCM scm, Launcher launcher, Node node, TaskListener listener, EnvVars env) throws IOException, InterruptedException {
  70. base = scm.findHgExe(node, listener, true);
  71. this.node = node;
  72. this.env = env;
  73. this.launcher = launcher;
  74. this.listener = listener;
  75. this.capability = Capability.get(this);
  76. }
  77. private ProcStarter l(ArgumentListBuilder args) {
  78. // set the default stdout
  79. return MercurialSCM.launch(launcher).cmds(args).stdout(listener).envs(env);
  80. }
  81. private ArgumentListBuilder seed() {
  82. return base.clone();
  83. }
  84. public ProcStarter pull() {
  85. return run("pull");
  86. }
  87. public ProcStarter clone(String... args) {
  88. return l(seed().add("clone").add(args));
  89. }
  90. public ProcStarter bundleAll(String file) {
  91. return run("bundle","--all",file);
  92. }
  93. public ProcStarter bundle(Collection<String> bases, String file) {
  94. ArgumentListBuilder args = seed().add("bundle");
  95. for (String head : bases) {
  96. args.add("--base", head);
  97. }
  98. args.add(file);
  99. return l(args);
  100. }
  101. public ProcStarter init(FilePath path) {
  102. return run("init",path.getRemote());
  103. }
  104. public ProcStarter unbundle(String bundleFile) {
  105. return run("unbundle",bundleFile);
  106. }
  107. public ProcStarter cleanAll() {
  108. return run("--config", "extensions.purge=", "clean", "--all");
  109. }
  110. /**
  111. * Runs arbitrary command.
  112. */
  113. public ProcStarter run(String... args) {
  114. return l(seed().add(args));
  115. }
  116. public ProcStarter run(ArgumentListBuilder args) {
  117. return l(seed().add(args.toCommandArray()));
  118. }
  119. /**
  120. * Obtains the heads of the repository.
  121. */
  122. public Set<String> heads(FilePath repo, boolean useTimeout) throws IOException, InterruptedException {
  123. if (capability.headsIn15 == null) {
  124. try {
  125. Set<String> output = heads(repo, useTimeout, true);
  126. capability.headsIn15 = true;
  127. return output;
  128. } catch (AbortException x) {
  129. Set<String> output = heads(repo, useTimeout, false);
  130. capability.headsIn15 = false;
  131. return output;
  132. }
  133. } else {
  134. return heads(repo, useTimeout, capability.headsIn15);
  135. }
  136. }
  137. private Set<String> heads(FilePath repo, boolean useTimeout, boolean usingHg15Syntax) throws IOException, InterruptedException {
  138. ArgumentListBuilder args = new ArgumentListBuilder("heads", "--template", "{node}\\n");
  139. if(usingHg15Syntax)
  140. args.add("--topo", "--closed");
  141. String output = popen(repo,listener,useTimeout,args);
  142. Set<String> heads = new LinkedHashSet<String>(Arrays.asList(output.split("\n")));
  143. heads.remove("");
  144. return heads;
  145. }
  146. /**
  147. * Gets the revision ID of the tip of the workspace.
  148. */
  149. public @CheckForNull String tip(FilePath repository) throws IOException, InterruptedException {
  150. String id = popen(repository, listener, false, new ArgumentListBuilder("log", "--rev", ".", "--template", "{node}"));
  151. if (!REVISIONID_PATTERN.matcher(id).matches()) {
  152. listener.error("Expected to get an id but got '" + id + "' instead.");
  153. return null; // HUDSON-7723
  154. }
  155. return id;
  156. }
  157. /**
  158. * Gets the current value of a specified config item.
  159. */
  160. public String config(FilePath repository, String name) throws IOException, InterruptedException {
  161. return popen(repository, listener, false, new ArgumentListBuilder("showconfig", name)).trim();
  162. }
  163. public List<String> toArgList() {
  164. return base.toList();
  165. }
  166. /**
  167. * Runs the command and captures the output.
  168. */
  169. public String popen(FilePath repository, TaskListener listener, boolean useTimeout, ArgumentListBuilder args)
  170. throws IOException, InterruptedException {
  171. args = seed().add(args.toCommandArray());
  172. ByteArrayOutputStream rev = new ByteArrayOutputStream();
  173. if (MercurialSCM.joinWithPossibleTimeout(l(args).pwd(repository).stdout(rev), useTimeout, listener) == 0) {
  174. return rev.toString();
  175. } else {
  176. listener.error("Failed to run " + args.toStringWithQuote());
  177. listener.getLogger().write(rev.toByteArray());
  178. throw new AbortException();
  179. }
  180. }
  181. /**
  182. * Capability of a particular hg invocation configuration (and location) on a specific node. Cached.
  183. */
  184. private static final class Capability {
  185. /**
  186. * Whether this supports 1.5-style "heads --topo ..." syntax.
  187. */
  188. volatile Boolean headsIn15;
  189. private static final Map<Node, Map<List<String>, Capability>> MAP = new WeakHashMap<Node,Map<List<String>,Capability>>();
  190. synchronized static Capability get(HgExe hg) {
  191. Map<List<String>,Capability> m = MAP.get(hg.node);
  192. if (m == null) {
  193. m = new HashMap<List<String>,Capability>();
  194. MAP.put(hg.node, m);
  195. }
  196. List<String> hgConfig = hg.toArgList();
  197. Capability cap = m.get(hgConfig);
  198. if (cap==null)
  199. m.put(hgConfig,cap = new Capability());
  200. return cap;
  201. }
  202. }
  203. /**
  204. * Pattern that matches revision ID.
  205. */
  206. private static final Pattern REVISIONID_PATTERN = Pattern.compile("[0-9a-f]{40}");
  207. }