1""" 2Support for the Mercurial SCM 3""" 4 5 6import logging 7 8import salt.utils.data 9import salt.utils.path 10from salt.exceptions import CommandExecutionError 11 12log = logging.getLogger(__name__) 13 14 15def __virtual__(): 16 """ 17 Only load if hg is installed 18 """ 19 if salt.utils.path.which("hg") is None: 20 return (False, "The hg execution module cannot be loaded: hg unavailable.") 21 else: 22 return True 23 24 25def _ssh_flag(identity_path): 26 return ["--ssh", "ssh -i {}".format(identity_path)] 27 28 29def revision(cwd, rev="tip", short=False, user=None): 30 """ 31 Returns the long hash of a given identifier (hash, branch, tag, HEAD, etc) 32 33 cwd 34 The path to the Mercurial repository 35 36 rev: tip 37 The revision 38 39 short: False 40 Return an abbreviated commit hash 41 42 user : None 43 Run hg as a user other than what the minion runs as 44 45 CLI Example: 46 47 .. code-block:: bash 48 49 salt '*' hg.revision /path/to/repo mybranch 50 """ 51 cmd = ["hg", "id", "-i", "--debug" if not short else "", "-r", "{}".format(rev)] 52 53 result = __salt__["cmd.run_all"](cmd, cwd=cwd, runas=user, python_shell=False) 54 55 if result["retcode"] == 0: 56 return result["stdout"] 57 else: 58 return "" 59 60 61def describe(cwd, rev="tip", user=None): 62 """ 63 Mimic git describe and return an identifier for the given revision 64 65 cwd 66 The path to the Mercurial repository 67 68 rev: tip 69 The path to the archive tarball 70 71 user : None 72 Run hg as a user other than what the minion runs as 73 74 CLI Example: 75 76 .. code-block:: bash 77 78 salt '*' hg.describe /path/to/repo 79 """ 80 cmd = [ 81 "hg", 82 "log", 83 "-r", 84 "{}".format(rev), 85 "--template", 86 "'{{latesttag}}-{{latesttagdistance}}-{{node|short}}'", 87 ] 88 desc = __salt__["cmd.run_stdout"](cmd, cwd=cwd, runas=user, python_shell=False) 89 90 return desc or revision(cwd, rev, short=True) 91 92 93def archive(cwd, output, rev="tip", fmt=None, prefix=None, user=None): 94 """ 95 Export a tarball from the repository 96 97 cwd 98 The path to the Mercurial repository 99 100 output 101 The path to the archive tarball 102 103 rev: tip 104 The revision to create an archive from 105 106 fmt: None 107 Format of the resulting archive. Mercurial supports: tar, 108 tbz2, tgz, zip, uzip, and files formats. 109 110 prefix : None 111 Prepend <prefix>/ to every filename in the archive 112 113 user : None 114 Run hg as a user other than what the minion runs as 115 116 If ``prefix`` is not specified it defaults to the basename of the repo 117 directory. 118 119 CLI Example: 120 121 .. code-block:: bash 122 123 salt '*' hg.archive /path/to/repo output=/tmp/archive.tgz fmt=tgz 124 """ 125 cmd = [ 126 "hg", 127 "archive", 128 "{}".format(output), 129 "--rev", 130 "{}".format(rev), 131 ] 132 if fmt: 133 cmd.append("--type") 134 cmd.append("{}".format(fmt)) 135 if prefix: 136 cmd.append("--prefix") 137 cmd.append('"{}"'.format(prefix)) 138 return __salt__["cmd.run"](cmd, cwd=cwd, runas=user, python_shell=False) 139 140 141def pull(cwd, opts=None, user=None, identity=None, repository=None): 142 """ 143 Perform a pull on the given repository 144 145 cwd 146 The path to the Mercurial repository 147 148 repository : None 149 Perform pull from the repository different from .hg/hgrc:[paths]:default 150 151 opts : None 152 Any additional options to add to the command line 153 154 user : None 155 Run hg as a user other than what the minion runs as 156 157 identity : None 158 Private SSH key on the minion server for authentication (ssh://) 159 160 .. versionadded:: 2015.5.0 161 162 CLI Example: 163 164 .. code-block:: bash 165 166 salt '*' hg.pull /path/to/repo opts=-u 167 """ 168 cmd = ["hg", "pull"] 169 if identity: 170 cmd.extend(_ssh_flag(identity)) 171 if opts: 172 for opt in opts.split(): 173 cmd.append(opt) 174 if repository is not None: 175 cmd.append(repository) 176 177 ret = __salt__["cmd.run_all"](cmd, cwd=cwd, runas=user, python_shell=False) 178 if ret["retcode"] != 0: 179 raise CommandExecutionError( 180 "Hg command failed: {}".format(ret.get("stderr", ret["stdout"])) 181 ) 182 183 return ret["stdout"] 184 185 186def update(cwd, rev, force=False, user=None): 187 """ 188 Update to a given revision 189 190 cwd 191 The path to the Mercurial repository 192 193 rev 194 The revision to update to 195 196 force : False 197 Force an update 198 199 user : None 200 Run hg as a user other than what the minion runs as 201 202 CLI Example: 203 204 .. code-block:: bash 205 206 salt devserver1 hg.update /path/to/repo somebranch 207 """ 208 cmd = ["hg", "update", "{}".format(rev)] 209 if force: 210 cmd.append("-C") 211 212 ret = __salt__["cmd.run_all"](cmd, cwd=cwd, runas=user, python_shell=False) 213 if ret["retcode"] != 0: 214 raise CommandExecutionError( 215 "Hg command failed: {}".format(ret.get("stderr", ret["stdout"])) 216 ) 217 218 return ret["stdout"] 219 220 221def clone(cwd, repository, opts=None, user=None, identity=None): 222 """ 223 Clone a new repository 224 225 cwd 226 The path to the Mercurial repository 227 228 repository 229 The hg URI of the repository 230 231 opts : None 232 Any additional options to add to the command line 233 234 user : None 235 Run hg as a user other than what the minion runs as 236 237 identity : None 238 Private SSH key on the minion server for authentication (ssh://) 239 240 .. versionadded:: 2015.5.0 241 242 CLI Example: 243 244 .. code-block:: bash 245 246 salt '*' hg.clone /path/to/repo https://bitbucket.org/birkenfeld/sphinx 247 """ 248 cmd = ["hg", "clone", "{}".format(repository), "{}".format(cwd)] 249 if opts: 250 for opt in opts.split(): 251 cmd.append("{}".format(opt)) 252 if identity: 253 cmd.extend(_ssh_flag(identity)) 254 255 ret = __salt__["cmd.run_all"](cmd, runas=user, python_shell=False) 256 if ret["retcode"] != 0: 257 raise CommandExecutionError( 258 "Hg command failed: {}".format(ret.get("stderr", ret["stdout"])) 259 ) 260 261 return ret["stdout"] 262 263 264def status(cwd, opts=None, user=None): 265 """ 266 Show changed files of the given repository 267 268 cwd 269 The path to the Mercurial repository 270 271 opts : None 272 Any additional options to add to the command line 273 274 user : None 275 Run hg as a user other than what the minion runs as 276 277 CLI Example: 278 279 .. code-block:: bash 280 281 salt '*' hg.status /path/to/repo 282 """ 283 284 def _status(cwd): 285 cmd = ["hg", "status"] 286 if opts: 287 for opt in opts.split(): 288 cmd.append("{}".format(opt)) 289 out = __salt__["cmd.run_stdout"](cmd, cwd=cwd, runas=user, python_shell=False) 290 types = { 291 "M": "modified", 292 "A": "added", 293 "R": "removed", 294 "C": "clean", 295 "!": "missing", 296 "?": "not tracked", 297 "I": "ignored", 298 " ": "origin of the previous file", 299 } 300 ret = {} 301 for line in out.splitlines(): 302 t, f = types[line[0]], line[2:] 303 if t not in ret: 304 ret[t] = [] 305 ret[t].append(f) 306 return ret 307 308 if salt.utils.data.is_iter(cwd): 309 return {cwd: _status(cwd) for cwd in cwd} 310 else: 311 return _status(cwd) 312