aboutsummaryrefslogtreecommitdiffstats
path: root/MgaRepo
diff options
context:
space:
mode:
Diffstat (limited to 'MgaRepo')
-rw-r--r--MgaRepo/VCS.py468
-rw-r--r--MgaRepo/binrepo.py4
-rw-r--r--MgaRepo/commands/clone.py51
-rw-r--r--MgaRepo/commands/log.py12
-rw-r--r--MgaRepo/commands/maintdb.py3
-rw-r--r--MgaRepo/commands/submit.py10
-rw-r--r--MgaRepo/git.py57
-rw-r--r--MgaRepo/rpmutil.py66
-rw-r--r--MgaRepo/simplerpm.py12
-rw-r--r--MgaRepo/svn.py451
-rw-r--r--MgaRepo/util.py127
11 files changed, 722 insertions, 539 deletions
diff --git a/MgaRepo/VCS.py b/MgaRepo/VCS.py
new file mode 100644
index 0000000..30d9279
--- /dev/null
+++ b/MgaRepo/VCS.py
@@ -0,0 +1,468 @@
+from MgaRepo import Error, SilentError, config
+from MgaRepo.util import execcmd, get_auth
+import sys
+import os
+import re
+import time
+
+class VCSLogEntry(object):
+ def __init__(self, revision, author, date):
+ self.revision = revision
+ self.author = author
+ self.date = date
+ self.changed = []
+ self.lines = []
+
+ def __lt__(self, other):
+ return (self.date < other.date)
+
+ def __eq__(self,other):
+ return (self.date == other.date)
+
+class VCS(object):
+ def __init__(self):
+ self.vcs_name = None
+ self.vcs_command = None
+ self.vcs_wrapper = "mga-ssh"
+ self.vcs_supports = {'clone' : False}
+ self.env_defaults = None
+
+ def _execVcs(self, *args, **kwargs):
+ localcmds = ("add", "revert", "cleanup", "mv")
+ kwargs["collecterr"] = False
+ if kwargs.get("show"):
+ if not kwargs.get("local"):
+ kwargs["collecterr"] = True
+ else:
+ if args[0] not in localcmds:
+ args = list(args)
+ args.append("--non-interactive")
+ else:
+ if args[0] == "mv":
+ kwargs["collecterr"] = False
+ else:
+ kwargs["collecterr"] = True
+ kwargs["cleanerr"] = True
+ if kwargs.get("xml"):
+ args.append("--xml")
+ cmdargs = [self.vcs_command]
+ cmdargs.extend(args)
+ try:
+ if args[0] in ('info', 'checkout','log'):
+ kwargs['info'] = True
+ else:
+ kwargs['info'] = False
+ return execcmd(cmdargs, **kwargs)
+ except Error as e:
+ msg = None
+ if e.args:
+ if "Permission denied" in e.args:
+ msg = ("It seems ssh-agent or ForwardAgent are not setup "
+ "or your username is wrong. See "
+ "https://wiki.mageia.org/en/Packagers_ssh"
+ " for more information.")
+ elif "authorization failed" in e.args:
+ msg = ("Note that mgarepo does not support any HTTP "
+ "authenticated access.")
+ if kwargs.get("show") and \
+ not config.getbool("global", "verbose", 0):
+ # svn has already dumped error messages, we don't need to
+ # do it too
+ if msg:
+ sys.stderr.write("\n")
+ sys.stderr.write(msg)
+ sys.stderr.write("\n")
+ raise SilentError
+ elif msg:
+ raise Error("%s\n%s" % (e, msg))
+ raise
+
+ def _set_env(self):
+ wrapper = "mgarepo-ssh"
+ repsys = config.get("global", "mgarepo-cmd")
+ if repsys:
+ dir = os.path.dirname(repsys)
+ path = os.path.join(dir, wrapper)
+ if os.path.exists(path):
+ wrapper = path
+ defaults = {"SVN_SSH": wrapper}
+ os.environ.update(defaults)
+ raw = config.get("global", "svn-env")
+ if raw:
+ for line in raw.split("\n"):
+ env = line.strip()
+ if not env:
+ continue
+ try:
+ name, value = env.split("=", 1)
+ except ValueError:
+ sys.stderr.write("invalid svn environment line: %r\n" % env)
+ continue
+ os.environ[name] = value
+
+ def _execVcs_success(self, *args, **kwargs):
+ status, output = self._execVcs(*args, **kwargs)
+ return status == 0
+
+ def _add_log(self, cmd_args, received_kwargs, optional=0):
+ if (not optional or
+ "log" in received_kwargs or
+ "logfile" in received_kwargs):
+ ret = received_kwargs.get("log")
+ if ret is not None:
+ cmd_args.extend(("-m", ret))
+ ret = received_kwargs.get("logfile")
+ if ret is not None:
+ cmd_args.extend(("-F", ret))
+
+ def _add_revision(self, cmd_args, received_kwargs, optional=0):
+ if not optional or "rev" in received_kwargs:
+ ret = received_kwargs.get("rev")
+ if isinstance(ret, str):
+ if not ret.startswith("{"): # if not a datespec
+ try:
+ ret = int(ret)
+ except ValueError:
+ raise Error("invalid revision provided")
+ if ret:
+ cmd_args.extend(("-r", str(ret)))
+
+ def add(self, path, **kwargs):
+ cmd = ["add", path + '@' if '@' in path else path]
+ return self._execVcs_success(noauth=1, *cmd, **kwargs)
+
+ def copy(self, pathfrom, pathto, **kwargs):
+ cmd = ["copy", pathfrom + '@' if '@' in pathfrom else pathfrom, pathto + '@' if '@' in pathto else pathto]
+ self._add_revision(cmd, kwargs, optional=1)
+ self._add_log(cmd, kwargs)
+ return self._execVcs_success(*cmd, **kwargs)
+
+ def remove(self, path, force=0, **kwargs):
+ cmd = ["remove", path + '@' if '@' in path else path]
+ self._add_log(cmd, kwargs)
+ if force:
+ cmd.append("--force")
+ return self._execVcs_success(*cmd, **kwargs)
+
+ def mkdir(self, path, **kwargs):
+ cmd = ["mkdir", path + '@' if '@' in path else path]
+ if kwargs.get("parents"):
+ cmd.append("--parents")
+ self._add_log(cmd, kwargs)
+ return self._execVcs_success(*cmd, **kwargs)
+
+ def _execVcs_commit(self, *cmd, **kwargs):
+ status, output = self._execVcs(*cmd, **kwargs)
+ match = re.search("Committed revision (?P<rev>\\d+)\\.$", output)
+ if match:
+ rawrev = match.group("rev")
+ return int(rawrev)
+
+ def commit(self, path, **kwargs):
+ cmd = ["commit", path + '@' if '@' in path else path]
+ if kwargs.get("nonrecursive"):
+ cmd.append("-N")
+ self._add_log(cmd, kwargs)
+ return self._execVcs_commit(*cmd, **kwargs)
+
+ def import_(self, path, url, **kwargs):
+ cmd = ["import", path, url]
+ self._add_log(cmd, kwargs)
+ return self._execVcs_commit(*cmd, **kwargs)
+
+ def export(self, url, targetpath, **kwargs):
+ cmd = ["export", url, targetpath]
+ self._add_revision(cmd, kwargs, optional=1)
+ return self._execVcs_success(*cmd, **kwargs)
+
+ def checkout(self, url, targetpath, **kwargs):
+ cmd = ["checkout", url, targetpath]
+ self._add_revision(cmd, kwargs, optional=1)
+ return self._execVcs_success(*cmd, **kwargs)
+
+ def clone(self, url, targetpath, **kwargs):
+ if self.vcs_supports['clone']:
+ cmd = ["clone", url, targetpath]
+ return self._execVcs_success(*cmd, **kwargs)
+ else:
+ raise Error("%s doesn't support 'clone'" % self.vcs_name)
+
+ def propget(self, propname, targets, **kwargs):
+ cmd = ["propget", propname, targets]
+ if kwargs.get("revprop"):
+ cmd.append("--revprop")
+ self._add_revision(cmd, kwargs)
+ status, output = self._execVcs(local=True, *cmd, **kwargs)
+ return output
+
+ def propset(self, propname, value, targets, **kwargs):
+ cmd = ["propset", propname, value, targets]
+ return self._execVcs_success(*cmd, **kwargs)
+
+ def propedit(self, propname, target, **kwargs):
+ cmd = ["propedit", propname, target]
+ if kwargs.get("rev"):
+ cmd.append("--revprop")
+ self._add_revision(cmd, kwargs)
+ return self._execVcs_success(local=True, show=True, *cmd, **kwargs)
+
+ def revision(self, path, **kwargs):
+ cmd = ["info", path + '@' if '@' in path else path]
+ status, output = self._execVcs(local=True, *cmd, **kwargs)
+ if status == 0:
+ for line in output.splitlines():
+ if line.startswith("Last Changed Rev: "):
+ return int(line.split()[3])
+ return None
+
+ def info(self, path, **kwargs):
+ cmd = ["info", path + '@' if '@' in path else path]
+ status, output = self._execVcs(local=True, noerror=True, *cmd, **kwargs)
+ if (("Not a versioned resource" not in output) and ("svn: warning: W155010" not in output)):
+ return output.splitlines()
+ return None
+
+ def info2(self, *args, **kwargs):
+ lines = self.info(*args, **kwargs)
+ if lines is None:
+ return None
+ pairs = [[w.strip() for w in line.split(":", 1)] for line in lines]
+ info = {}
+ for pair in pairs:
+ if pair != ['']:
+ info[pair[0]]=pair[1]
+ return info
+
+ def ls(self, path, **kwargs):
+ cmd = ["ls", path + '@' if '@' in path else path]
+ status, output = self._execVcs(*cmd, **kwargs)
+ if status == 0:
+ return output.split()
+ return None
+
+ def status(self, path, **kwargs):
+ cmd = ["status", path + '@' if '@' in path else path]
+ if kwargs.get("verbose"):
+ cmd.append("-v")
+ if kwargs.get("noignore"):
+ cmd.append("--no-ignore")
+ if kwargs.get("quiet"):
+ cmd.append("--quiet")
+ status, output = self._execVcs(*cmd, **kwargs)
+ if status == 0:
+ return [(x[0], x[8:]) for x in output.splitlines()]
+ return None
+
+ def cleanup(self, path, **kwargs):
+ cmd = ["cleanup", path + '@' if '@' in path else path]
+ return self._execVcs_success(*cmd, **kwargs)
+
+ def revert(self, path, **kwargs):
+ cmd = ["revert", path + '@' if '@' in path else path]
+ status, output = self._execVcs(*cmd, **kwargs)
+ if status == 0:
+ return [x.split() for x in output.split()]
+ return None
+
+ def switch(self, url, oldurl=None, path=None, relocate=False, **kwargs):
+ cmd = ["switch"]
+ if relocate:
+ if oldurl is None:
+ raise Error("You must supply the old URL when "\
+ "relocating working copies")
+ cmd.append("--relocate")
+ cmd.append(oldurl)
+ cmd.append(url)
+ if path is not None:
+ cmd.append(path)
+ return self._execVcs_success(*cmd, **kwargs)
+
+ def update(self, path, **kwargs):
+ cmd = ["update", path + '@' if '@' in path else path]
+ self._add_revision(cmd, kwargs, optional=1)
+ status, output = self._execVcs(*cmd, **kwargs)
+ if status == 0:
+ return [x.split() for x in output.split()]
+ return None
+
+ def merge(self, url1, url2=None, rev1=None, rev2=None, path=None,
+ **kwargs):
+ cmd = ["merge"]
+ if rev1 and rev2 and not url2:
+ cmd.append("-r")
+ cmd.append("%s:%s" % (rev1, rev2))
+ cmd.append(url1)
+ else:
+ if not url2:
+ raise ValueError("url2 needed if two revisions are not provided")
+ if rev1:
+ cmd.append("%s@%s" % (url1, rev1))
+ else:
+ cmd.append(url1)
+ if rev2:
+ cmd.append("%s@%s" % (url2, rev2))
+ else:
+ cmd.append(url2)
+ if path:
+ cmd.append(path)
+ status, output = self._execVcs(*cmd, **kwargs)
+ if status == 0:
+ return [x.split() for x in output.split()]
+ return None
+
+ def diff(self, pathurl1, pathurl2=None, **kwargs):
+ cmd = ["diff", pathurl1]
+ self._add_revision(cmd, kwargs, optional=1)
+ if pathurl2:
+ cmd.append(pathurl2)
+ status, output = self._execVcs(*cmd, **kwargs)
+ if status == 0:
+ return output
+ return None
+
+ def cat(self, url, **kwargs):
+ cmd = ["cat", url]
+ self._add_revision(cmd, kwargs, optional=1)
+ status, output = self._execVcs(*cmd, **kwargs)
+ if status == 0:
+ return output
+ return None
+
+ def log(self, url, start=None, end=0, limit=None, **kwargs):
+ cmd = ["log", "-v", url]
+ if start is not None or end != 0:
+ if start is not None and type(start) is not type(0):
+ try:
+ start = int(start)
+ except (ValueError, TypeError):
+ raise Error("invalid log start revision provided")
+ if type(end) is not type(0):
+ try:
+ end = int(end)
+ except (ValueError, TypeError):
+ raise Error("invalid log end revision provided")
+ start = start or "HEAD"
+ cmd.extend(("-r", "%s:%s" % (start, end)))
+ if limit is not None:
+ try:
+ limit = int(limit)
+ except (ValueError, TypeError):
+ raise Error("invalid limit number provided")
+ cmd.extend(("--limit", str(limit)))
+ status, output = self._execVcs(*cmd, **kwargs)
+ if status != 0:
+ return None
+
+ revheader = re.compile("^r(?P<revision>[0-9]+) \| (?P<author>[^\|]+) \| (?P<date>[^\|]+) \| (?P<lines>[0-9]+) (?:line|lines)$")
+ changedpat = re.compile(r"^\s+(?P<action>[^\s]+) (?P<path>[^\s]+)(?: \([^\s]+ (?P<from_path>[^:]+)(?:\:(?P<from_rev>[0-9]+))?\))?$")
+ logseparator = "-"*72
+ linesleft = 0
+ entry = None
+ log = []
+ appendchanged = 0
+ changedheader = 0
+ for line in output.splitlines():
+ line = line.rstrip()
+ if changedheader:
+ appendchanged = 1
+ changedheader = 0
+ elif appendchanged:
+ if not line:
+ appendchanged = 0
+ continue
+ m = changedpat.match(line)
+ if m:
+ changed = m.groupdict().copy()
+ from_rev = changed.get("from_rev")
+ if from_rev is not None:
+ try:
+ changed["from_rev"] = int(from_rev)
+ except (ValueError, TypeError):
+ raise Error("invalid revision number in % log" % self.vcs_name)
+ entry.changed.append(changed)
+ elif linesleft == 0:
+ if line != logseparator:
+ m = revheader.match(line)
+ if m:
+ linesleft = int(m.group("lines"))
+ timestr = " ".join(m.group("date").split()[:2])
+ timetuple = time.strptime(timestr,
+ "%Y-%m-%d %H:%M:%S")
+ entry = VCSLogEntry(int(m.group("revision")),
+ m.group("author"), timetuple)
+ log.append(entry)
+ changedheader = 1
+ else:
+ entry.lines.append(line)
+ linesleft -= 1
+ log.sort()
+ log.reverse()
+ return log
+
+ def mv(self, path, dest, message=None, **kwargs):
+ cmd = ["mv", path, dest, ]
+ if message:
+ cmd.extend(("-m", str(message)))
+ else:
+ kwargs['show'] = True
+ self._add_log(cmd, kwargs)
+ return self._execVcs_success(*cmd, **kwargs)
+
+class VCSLook(object):
+ def __init__(self, repospath, txn=None, rev=None):
+ self.repospath = repospath
+ self.txn = txn
+ self.rev = rev
+ self.execcmd = None
+
+ def _execVcslook(self, cmd, *args, **kwargs):
+ execcmd_args = ["svnlook", cmd, self.repospath]
+ self._add_txnrev(execcmd_args, kwargs)
+ execcmd_args += args
+ execcmd_kwargs = {}
+ keywords = ["show", "noerror"]
+ for key in keywords:
+ if key in kwargs:
+ execcmd_kwargs[key] = kwargs[key]
+ return execcmd(*execcmd_args, **execcmd_kwargs)
+
+ def _add_txnrev(self, cmd_args, received_kwargs):
+ if "txn" in received_kwargs:
+ txn = received_kwargs.get("txn")
+ if txn is not None:
+ cmd_args.extend(("-t", txn))
+ elif self.txn is not None:
+ cmd_args.extend(("-t", self.txn))
+ if "rev" in received_kwargs:
+ rev = received_kwargs.get("rev")
+ if rev is not None:
+ cmd_args.exten(("-r", str(rev)))
+ elif self.rev is not None:
+ cmd_args.extend(("-r", str(self.rev)))
+
+ def changed(self, **kwargs):
+ status, output = self._execVcslook("changed", **kwargs)
+ if status != 0:
+ return None
+ changes = []
+ for line in output.splitlines():
+ line = line.rstrip()
+ if not line:
+ continue
+ entry = [None, None, None]
+ changedata, changeprop, path = None, None, None
+ if line[0] != "_":
+ changedata = line[0]
+ if line[1] != " ":
+ changeprop = line[1]
+ path = line[4:]
+ changes.append((changedata, changeprop, path))
+ return changes
+
+ def author(self, **kwargs):
+ status, output = self._execVcslook("author", **kwargs)
+ if status != 0:
+ return None
+ return output.strip()
+
+# vim:et:ts=4:sw=4
diff --git a/MgaRepo/binrepo.py b/MgaRepo/binrepo.py
index 89679b0..2d08300 100644
--- a/MgaRepo/binrepo.py
+++ b/MgaRepo/binrepo.py
@@ -102,12 +102,12 @@ def upload_binary(topdir, filename):
return
host = config.get("binrepo", "upload_host")
upload_bin_helper = get_helper("upload-bin")
- command = "ssh %s %s %s" % (host, upload_bin_helper, filename)
+ command = ["ssh", host, upload_bin_helper, filename]
try:
filein = open(filepath, 'r')
except Error as e:
raise Error("Could not open file %s\n" % filepath)
- status, output = execcmd(command, show=True, geterr=True, stdin=filein)
+ status, output = execcmd(command, show=True, collecterr=True, stdin=filein)
def import_binaries(topdir, pkgname):
"""Import all binaries from a given package checkout
diff --git a/MgaRepo/commands/clone.py b/MgaRepo/commands/clone.py
new file mode 100644
index 0000000..589ec75
--- /dev/null
+++ b/MgaRepo/commands/clone.py
@@ -0,0 +1,51 @@
+#!/usr/bin/python
+from MgaRepo import Error
+from MgaRepo.command import *
+from MgaRepo.rpmutil import clone
+import getopt
+import sys
+
+HELP = """\
+Usage: repsys co [OPTIONS] URL [LOCALPATH]
+
+Checkout the package source from the Mandriva repository.
+
+If the 'mirror' option is enabled, the package is obtained from the mirror
+repository.
+
+You can specify the distro branch to checkout from by using distro/pkgname.
+
+Options:
+ -d The distribution branch to checkout from
+ -b The package branch
+ -M Do not use the mirror (use the main repository)
+ -h Show this message
+
+Examples:
+ repsys clone pkgname
+ repsys clone -d 2009.0 pkgname
+ repsys clone 2009.0/pkgame
+ repsys clone http://repos/svn/cnc/snapshot/foo
+ repsys clone http://repos/svn/cnc/snapshot/foo foo-pkg
+"""
+
+def parse_options():
+ parser = OptionParser(help=HELP)
+ parser.add_option("--distribution", "-d", dest="distro", default=None)
+ parser.add_option("--branch", "-b", dest="branch", default=None)
+ opts, args = parser.parse_args()
+ if len(args) not in (1, 2):
+ raise Error("invalid arguments")
+ # here we don't use package_url in order to notify the user we are
+ # using the mirror
+ opts.pkgdirurl = args[0]
+ if len(args) == 2:
+ opts.path = args[1]
+ else:
+ opts.path = None
+ return opts
+
+def main():
+ do_command(parse_options, clone)
+
+# vim:et:ts=4:sw=4
diff --git a/MgaRepo/commands/log.py b/MgaRepo/commands/log.py
index 2181125..330a96a 100644
--- a/MgaRepo/commands/log.py
+++ b/MgaRepo/commands/log.py
@@ -6,6 +6,8 @@ from MgaRepo.rpmutil import sync
from MgaRepo.util import execcmd
import sys
import os
+import subprocess
+import shlex
HELP = """\
Usage: mgarepo log [OPTIONS] [PACKAGE]
@@ -57,9 +59,13 @@ def svn_log(pkgdirurl, verbose=False, limit=None, revision=None, releases=None):
args.append("-r")
args.append(revision)
if os.isatty(sys.stdin.fileno()):
- args.append("| less")
- rawcmd = " ".join(args)
- execcmd(rawcmd, show=True)
+ pager = shlex.split(os.environ.get("PAGER", "less"))
+ p = subprocess.Popen(args, stdout=subprocess.PIPE)
+ p2 = subprocess.Popen(pager, stdin=p.stdout)
+ p2.wait()
+ p.wait()
+ else:
+ execcmd(args, show=True)
def main():
do_command(parse_options, svn_log)
diff --git a/MgaRepo/commands/maintdb.py b/MgaRepo/commands/maintdb.py
index 9a97be8..2aa2a4c 100644
--- a/MgaRepo/commands/maintdb.py
+++ b/MgaRepo/commands/maintdb.py
@@ -33,8 +33,7 @@ def parse_options():
def maintdb(maintdb_args):
host = config.get("maintdb", "host", "maintdb.mageia.org")
maintdb_helper = get_helper("maintdb")
- cmd_args = ' '.join(maintdb_args)
- command = "ssh %s %s %s" % (host, maintdb_helper, cmd_args)
+ command = ["ssh", host, maintdb_helper] + maintdb_args
execcmd(command, show=True)
sys.exit(0)
diff --git a/MgaRepo/commands/submit.py b/MgaRepo/commands/submit.py
index 9f05dca..d36290f 100644
--- a/MgaRepo/commands/submit.py
+++ b/MgaRepo/commands/submit.py
@@ -160,9 +160,10 @@ def list_targets(option, opt, val, parser):
raise Error("no submit host defined in mgarepo.conf")
createsrpm = get_helper("create-srpm")
#TODO make it configurable
- command = "ssh %s %s --list" % (host, createsrpm)
- execcmd(command, show=True)
- sys.exit(0)
+ args = ["ssh", host, createsrpm, "--list"]
+ execcmd(args, show=true)
+ sys.exit(0) # it is invoked via optparse callback, thus we need to
+ # force ending the script
def submit(urls, target, define=[], submithost=None, atonce=False, sid=None):
if submithost is None:
@@ -197,8 +198,7 @@ def submit(urls, target, define=[], submithost=None, atonce=False, sid=None):
else:
cmdsargs.extend((baseargs + [url]) for url in urls)
for cmdargs in cmdsargs:
- command = subprocess.list2cmdline(cmdargs)
- status, output = execcmd(command)
+ status, output = execcmd(cmdargs)
if status == 0:
print("Package submitted!")
else:
diff --git a/MgaRepo/git.py b/MgaRepo/git.py
new file mode 100644
index 0000000..a424a08
--- /dev/null
+++ b/MgaRepo/git.py
@@ -0,0 +1,57 @@
+from MgaRepo import Error, config
+from MgaRepo.util import execcmd
+from MgaRepo.VCS import *
+from os.path import basename, dirname
+from os import chdir, getcwd
+import sys
+import re
+import time
+from xml.etree import ElementTree
+import subprocess
+
+class GITLogEntry(VCSLogEntry):
+ def __init__(self, revision, author, date):
+ VCSLogEntry.__init__(self, revision, author, data)
+
+class GIT(VCS):
+ def __init__(self):
+ VCS.__init__(self)
+ self.vcs_name = "git"
+ self.vcs_command = config.get("global", "git-command", "git")
+ self.vcs_supports['clone'] = True
+ self.env_defaults = {"GIT_SSH": self.vcs_wrapper}
+
+ def clone(self, url, targetpath, **kwargs):
+ if url.split(':')[0].find("svn") < 0:
+ return VCS.clone(self, url, targetpath, **kwargs)
+ else:
+ # To speed things up on huge repositories, we'll just grab all the
+ # revision numbers for this specific directory and grab these only
+ # in stead of having to go through each and every revision...
+ retval, result = execcmd("svn log --stop-on-copy --xml %s" % url)
+ if retval:
+ return retval
+ parser = ElementTree.XMLParser()
+ result = "".join(result.split("\n"))
+ parser.feed(result)
+ log = parser.close()
+ logentries = log.getiterator("logentry")
+ revisions = []
+ topurl = dirname(url)
+ trunk = basename(url)
+ tags = "releases"
+ execcmd("git svn init %s --trunk=%s --tags=%s %s" % (topurl, trunk, tags, targetpath), show=True)
+ chdir(targetpath)
+ for entry in logentries:
+ revisions.append(entry.attrib["revision"])
+ while revisions:
+ execcmd("git svn fetch -r%d" % int(revisions.pop()), show=True)
+
+ cmd = ["svn", "rebase"]
+ return self._execVcs_success(*cmd, **kwargs)
+
+class SVNLook(VCSLook):
+ def __init__(self, repospath, txn=None, rev=None):
+ VCSLook.__init__(self, repospath, txn, rev)
+
+# vim:et:ts=4:sw=4
diff --git a/MgaRepo/rpmutil.py b/MgaRepo/rpmutil.py
index 8c00da9..850bd4d 100644
--- a/MgaRepo/rpmutil.py
+++ b/MgaRepo/rpmutil.py
@@ -1,9 +1,10 @@
#!/usr/bin/python3
from MgaRepo import Error, config
from MgaRepo import mirror, layout, log, binrepo
+from MgaRepo.git import GIT
from MgaRepo.svn import SVN
from MgaRepo.simplerpm import SRPM
-from MgaRepo.util import execcmd
+from MgaRepo.util import execcmd, CommandError
from MgaRepo.command import default_parent
import rpm
import urllib.parse
@@ -20,7 +21,7 @@ def get_spec(pkgdirurl, targetdir=".", submit=False):
try:
geturl = layout.checkout_url(pkgdirurl, append_path="SPECS")
mirror.info(geturl)
- svn.export("'%s'" % geturl, tmpdir)
+ svn.export(geturl, tmpdir)
speclist = glob.glob(os.path.join(tmpdir, "*.spec"))
if not speclist:
raise Error("no spec files found")
@@ -33,11 +34,6 @@ def get_spec(pkgdirurl, targetdir=".", submit=False):
if os.path.isdir(tmpdir):
shutil.rmtree(tmpdir)
-def rpm_macros_defs(macros):
- defs = ("--define \"%s %s\"" % macro for macro in macros)
- args = " ".join(defs)
- return args
-
#FIXME move it to another module
def rev_touched_url(url, rev):
svn = SVN()
@@ -72,13 +68,17 @@ def get_srpm(pkgdirurl,
strict = False):
svn = SVN()
tmpdir = tempfile.mktemp()
- topdir = "--define '_topdir %s'" % tmpdir
- builddir = "--define '_builddir %s/%s'" % (tmpdir, "BUILD")
- rpmdir = "--define '_rpmdir %s/%s'" % (tmpdir, "RPMS")
- sourcedir = "--define '_sourcedir %s/%s'" % (tmpdir, "SOURCES")
- specdir = "--define '_specdir %s/%s'" % (tmpdir, "SPECS")
- srcrpmdir = "--define '_srcrpmdir %s/%s'" % (tmpdir, "SRPMS")
- patchdir = "--define '_patchdir %s/%s'" % (tmpdir, "SOURCES")
+ topdir = "_topdir %s" % tmpdir
+ builddir = "_builddir %s/%s" % (tmpdir, "BUILD")
+ rpmdir = "_rpmdir %s/%s" % (tmpdir, "RPMS")
+ sourcedir = "_sourcedir %s/%s" % (tmpdir, "SOURCES")
+ specdir = "_specdir %s/%s" % (tmpdir, "SPECS")
+ srcrpmdir = "_srcrpmdir %s/%s" % (tmpdir, "SRPMS")
+ patchdir = "_patchdir %s/%s" % (tmpdir, "SOURCES")
+ temppath = "_tmppath %s" % (tmpdir)
+
+ rpmdefs = [("--define", expr) for expr in (topdir, builddir, rpmdir,
+ sourcedir, specdir, srcrpmdir, patchdir, temppath)]
try:
if mode == "version":
@@ -107,7 +107,7 @@ def get_srpm(pkgdirurl,
if config.getbool("srpm", "run-prep", False):
makefile = os.path.join(tmpdir, "Makefile")
if os.path.exists(makefile):
- execcmd("make", "-C", tmpdir, "srpm-prep")
+ execcmd(("make", "-C", tmpdir, "srpm-prep"))
if not speclist:
raise Error("no spec files found")
spec = speclist[0]
@@ -124,11 +124,22 @@ def get_srpm(pkgdirurl,
if packager:
packager = " --define 'packager %s'" % packager
- defs = rpm_macros_defs(macros)
sourcecmd = config.get("helper", "rpmbuild", "rpmbuild")
- execcmd("%s -bs --nodeps %s %s %s %s %s %s %s %s %s %s" %
- (sourcecmd, topdir, builddir, rpmdir, sourcedir, specdir,
- srcrpmdir, patchdir, packager, spec, defs))
+ args = [sourcecmd, "-bs", "--nodeps"]
+ for pair in rpmdefs:
+ args.extend(pair)
+ for pair in macros:
+ args.extend(("--define", "%s %s" % pair))
+ args.append(spec)
+ try:
+ execcmd(args)
+ except CommandError as e:
+ if config.getbool("global", "verbose"):
+ cmdline = e.cmdline + "\n"
+ else:
+ cmdline = ""
+ raise Error("error while creating the source RPM "
+ "(with %s):\n%s%s" % (sourcecmd, cmdline, e.output))
# copy the generated SRPMs to their target locations
targetsrpms = []
@@ -170,7 +181,7 @@ def patch_spec(pkgdirurl, patchfile, log=""):
if not speclist:
raise Error("no spec files found")
spec = speclist[0]
- status, output = execcmd("patch", spec, patchfile)
+ status, output = execcmd(["patch", spec, patchfile])
if status != 0:
raise Error("can't apply patch:\n%s\n" % output)
else:
@@ -250,9 +261,9 @@ def put_srpm(srpmfile, markrelease=False, striplog=True, branch=None,
svn.remove(entrypath)
# Copy all files
- execcmd("cp -rlf", uspecsdir, currentdir)
+ execcmd(["cp", "-rf", uspecsdir, currentdir])
if os.path.isdir(usourcesdir):
- execcmd("cp -rlf", usourcesdir, currentdir)
+ execcmd(["cp", "-rlf", usourcesdir, currentdir])
# Add new entries
for entry in [x for x in uspecsentries
@@ -481,6 +492,17 @@ def checkout(pkgdirurl, path=None, revision=None, branch=None, distro=None, back
if not spec:
binrepo.download_binaries(path)
+def clone(pkgdirurl, path=None, branch=None,
+ distro=None):
+ o_pkgdirurl = pkgdirurl
+ pkgdirurl = layout.package_url(o_pkgdirurl, distro=distro)
+ current = layout.checkout_url(pkgdirurl, branch=branch)
+ if path is None:
+ path = layout.package_name(pkgdirurl)
+ mirror.info(current)
+ git = GIT()
+ git.clone(current, path, show=1)
+
def getpkgtopdir(basedir=None):
#FIXME this implementation doesn't work well with relative path names,
# which is something we need in order to have a friendlier output
diff --git a/MgaRepo/simplerpm.py b/MgaRepo/simplerpm.py
index a06e694..3d50176 100644
--- a/MgaRepo/simplerpm.py
+++ b/MgaRepo/simplerpm.py
@@ -7,15 +7,17 @@ class SRPM:
self._getinfo()
def _getinfo(self):
- cmdstr = "rpm -qp --nosignature --qf '%%{name} %%{epoch} %%{release} %%{version}' %s"
- status, output = execcmd(cmdstr % self.filename)
+ args = ["rpm", "-qp", "--qf", "%{name} %{epoch} %{release} %{version}",
+ self.filename]
+ status, output = execcmd(args)
self.name, self.epoch, self.release, self.version = output.split()
if self.epoch == "(none)":
self.epoch = None
def unpack(self, topdir):
- execcmd(("rpm -i --nodeps --define '_sourcedir %s/SOURCES' " +
- "--define '_specdir %s/SPECS' --define '_patchdir %s/SOURCES' %s")
- % (topdir, topdir, topdir, self.filename))
+ args ["rpm", "-i", "--nodeps", "--define", "_sourcedir", topdir+"/SOURCES",
+ "--define", "_specdir", topdir + "/SPECS", "--define", "_patchdir",
+ topdir+"/SOURCES", self.filename]
+ execcmd(args)
# vim:et:ts=4:sw=4
diff --git a/MgaRepo/svn.py b/MgaRepo/svn.py
index 74f47a3..3a74954 100644
--- a/MgaRepo/svn.py
+++ b/MgaRepo/svn.py
@@ -1,5 +1,5 @@
-from MgaRepo import Error, SilentError, config
-from MgaRepo.util import execcmd, get_auth
+from MgaRepo.util import execcmd
+from MgaRepo.VCS import *
import sys
import os
import re
@@ -7,445 +7,20 @@ import time
__all__ = ["SVN", "SVNLook", "SVNLogEntry"]
-class SVNLogEntry:
+class SVNLogEntry(VCSLogEntry):
def __init__(self, revision, author, date):
- self.revision = revision
- self.author = author
- self.date = date
- self.changed = []
- self.lines = []
+ VCSLogEntry.__init__(self, revision, author, data)
- def __lt__(self, other):
- return (self.date < other.date)
-
- def __eq__(self,other):
- return (self.date == other.date)
+class SVN(VCS):
+ def __init__(self):
+ VCS.__init__(self)
+ self.vcs_name = "svn"
+ self.vcs_command = config.get("global", "svn-command", "svn")
+ self.env_defaults = {"SVN_SSH": self.vcs_wrapper}
-class SVN:
- def _execsvn(self, *args, **kwargs):
- localcmds = ("add", "revert", "cleanup", "mv")
- if not kwargs.get("show") and args[0] not in localcmds:
- args = list(args)
- args.append("--non-interactive")
- else:
- if args[0] == "mv":
- kwargs["geterr"] = False
- else:
- kwargs["geterr"] = True
- kwargs["cleanerr"] = True
- if kwargs.get("xml"):
- args.append("--xml")
- self._set_env()
- svn_command = config.get("global", "svn-command", "svn")
- cmdstr = svn_command + " " + " ".join(args)
- try:
- if args[0] in ('info', 'checkout','log'):
- kwargs['info'] = True
- else:
- kwargs['info'] = False
- return execcmd(cmdstr, **kwargs)
- except Error as e:
- msg = None
- if e.args:
- if "Permission denied" in e.args:
- msg = ("It seems ssh-agent or ForwardAgent are not setup "
- "or your username is wrong. See "
- "https://wiki.mageia.org/en/Packagers_ssh"
- " for more information.")
- elif "authorization failed" in e.args:
- msg = ("Note that mgarepo does not support any HTTP "
- "authenticated access.")
- if kwargs.get("show") and \
- not config.getbool("global", "verbose", 0):
- # svn has already dumped error messages, we don't need to
- # do it too
- if msg:
- sys.stderr.write("\n")
- sys.stderr.write(msg)
- sys.stderr.write("\n")
- raise SilentError
- elif msg:
- raise Error("%s\n%s" % (e, msg))
- raise
-
- def _set_env(self):
- wrapper = "mgarepo-ssh"
- repsys = config.get("global", "mgarepo-cmd")
- if repsys:
- dir = os.path.dirname(repsys)
- path = os.path.join(dir, wrapper)
- if os.path.exists(path):
- wrapper = path
- defaults = {"SVN_SSH": wrapper}
- os.environ.update(defaults)
- raw = config.get("global", "svn-env")
- if raw:
- for line in raw.split("\n"):
- env = line.strip()
- if not env:
- continue
- try:
- name, value = env.split("=", 1)
- except ValueError:
- sys.stderr.write("invalid svn environment line: %r\n" % env)
- continue
- os.environ[name] = value
-
- def _execsvn_success(self, *args, **kwargs):
- status, output = self._execsvn(*args, **kwargs)
- return status == 0
-
- def _add_log(self, cmd_args, received_kwargs, optional=0):
- if (not optional or
- "log" in received_kwargs or
- "logfile" in received_kwargs):
- ret = received_kwargs.get("log")
- if ret is not None:
- cmd_args.append("-m '%s'" % ret)
- ret = received_kwargs.get("logfile")
- if ret is not None:
- cmd_args.append("-F '%s'" % ret)
-
- def _add_revision(self, cmd_args, received_kwargs, optional=0):
- if not optional or "rev" in received_kwargs:
- ret = received_kwargs.get("rev")
- if isinstance(ret, str):
- if not ret.startswith("{"): # if not a datespec
- try:
- ret = int(ret)
- except ValueError:
- raise Error("invalid revision provided")
- if ret:
- cmd_args.append("-r '%s'" % ret)
-
- def add(self, path, **kwargs):
- cmd = ["add", path + '@' if '@' in path else path]
- return self._execsvn_success(noauth=1, *cmd, **kwargs)
-
- def copy(self, pathfrom, pathto, **kwargs):
- cmd = ["copy", pathfrom + '@' if '@' in pathfrom else pathfrom, pathto + '@' if '@' in pathto else pathto]
- self._add_revision(cmd, kwargs, optional=1)
- self._add_log(cmd, kwargs)
- return self._execsvn_success(*cmd, **kwargs)
-
- def remove(self, path, force=0, **kwargs):
- cmd = ["remove", path + '@' if '@' in path else path]
- self._add_log(cmd, kwargs)
- if force:
- cmd.append("--force")
- return self._execsvn_success(*cmd, **kwargs)
-
- def mkdir(self, path, **kwargs):
- cmd = ["mkdir", path + '@' if '@' in path else path]
- if kwargs.get("parents"):
- cmd.append("--parents")
- self._add_log(cmd, kwargs)
- return self._execsvn_success(*cmd, **kwargs)
-
- def _execsvn_commit(self, *cmd, **kwargs):
- status, output = self._execsvn(*cmd, **kwargs)
- match = re.search("Committed revision (?P<rev>\\d+)\\.$", output)
- if match:
- rawrev = match.group("rev")
- return int(rawrev)
-
- def commit(self, path, **kwargs):
- cmd = ["commit", path + '@' if '@' in path else path]
- if kwargs.get("nonrecursive"):
- cmd.append("-N")
- self._add_log(cmd, kwargs)
- return self._execsvn_commit(*cmd, **kwargs)
-
- def import_(self, path, url, **kwargs):
- cmd = ["import", "'%s'" % path, "'%s'" % url]
- self._add_log(cmd, kwargs)
- return self._execsvn_commit(*cmd, **kwargs)
-
- def export(self, url, targetpath, **kwargs):
- cmd = ["export", "'%s'" % url, targetpath]
- self._add_revision(cmd, kwargs, optional=1)
- return self._execsvn_success(*cmd, **kwargs)
-
- def checkout(self, url, targetpath, **kwargs):
- cmd = ["checkout", "'%s'" % url, targetpath]
- self._add_revision(cmd, kwargs, optional=1)
- return self._execsvn_success(*cmd, **kwargs)
-
- def propget(self, propname, targets, **kwargs):
- cmd = ["propget", propname, targets]
- if kwargs.get("revprop"):
- cmd.append("--revprop")
- self._add_revision(cmd, kwargs)
- status, output = self._execsvn(local=True, *cmd, **kwargs)
- return output
-
- def propset(self, propname, value, targets, **kwargs):
- cmd = ["propset", propname, "'%s'" % value, targets]
- return self._execsvn_success(*cmd, **kwargs)
-
- def propedit(self, propname, target, **kwargs):
- cmd = ["propedit", propname, target]
- if kwargs.get("rev"):
- cmd.append("--revprop")
- self._add_revision(cmd, kwargs)
- return self._execsvn_success(local=True, show=True, *cmd, **kwargs)
-
- def revision(self, path, **kwargs):
- cmd = ["info", path + '@' if '@' in path else path]
- status, output = self._execsvn(local=True, *cmd, **kwargs)
- if status == 0:
- for line in output.splitlines():
- if line.startswith("Last Changed Rev: "):
- return int(line.split()[3])
- return None
-
- def info(self, path, **kwargs):
- cmd = ["info", path + '@' if '@' in path else path]
- status, output = self._execsvn(local=True, noerror=True, *cmd, **kwargs)
- if (("Not a versioned resource" not in output) and ("svn: warning: W155010" not in output)):
- return output.splitlines()
- return None
-
- def info2(self, *args, **kwargs):
- lines = self.info(*args, **kwargs)
- if lines is None:
- return None
- pairs = [[w.strip() for w in line.split(":", 1)] for line in lines]
- info = {}
- for pair in pairs:
- if pair != ['']:
- info[pair[0]]=pair[1]
- return info
-
- def ls(self, path, **kwargs):
- cmd = ["ls", path + '@' if '@' in path else path]
- status, output = self._execsvn(*cmd, **kwargs)
- if status == 0:
- return output.split()
- return None
-
- def status(self, path, **kwargs):
- cmd = ["status", path + '@' if '@' in path else path]
- if kwargs.get("verbose"):
- cmd.append("-v")
- if kwargs.get("noignore"):
- cmd.append("--no-ignore")
- if kwargs.get("quiet"):
- cmd.append("--quiet")
- status, output = self._execsvn(*cmd, **kwargs)
- if status == 0:
- return [(x[0], x[8:]) for x in output.splitlines()]
- return None
-
- def cleanup(self, path, **kwargs):
- cmd = ["cleanup", path + '@' if '@' in path else path]
- return self._execsvn_success(*cmd, **kwargs)
-
- def revert(self, path, **kwargs):
- cmd = ["revert", path + '@' if '@' in path else path]
- status, output = self._execsvn(*cmd, **kwargs)
- if status == 0:
- return [x.split() for x in output.split()]
- return None
-
- def switch(self, url, oldurl=None, path=None, relocate=False, **kwargs):
- cmd = ["switch"]
- if relocate:
- if oldurl is None:
- raise Error("You must supply the old URL when "\
- "relocating working copies")
- cmd.append("--relocate")
- cmd.append(oldurl)
- cmd.append(url)
- if path is not None:
- cmd.append(path)
- return self._execsvn_success(*cmd, **kwargs)
-
- def update(self, path, **kwargs):
- cmd = ["update", path + '@' if '@' in path else path]
- self._add_revision(cmd, kwargs, optional=1)
- status, output = self._execsvn(*cmd, **kwargs)
- if status == 0:
- return [x.split() for x in output.split()]
- return None
-
- def merge(self, url1, url2=None, rev1=None, rev2=None, path=None,
- **kwargs):
- cmd = ["merge"]
- if rev1 and rev2 and not url2:
- cmd.append("-r")
- cmd.append("%s:%s" % (rev1, rev2))
- cmd.append(url1)
- else:
- if not url2:
- raise ValueError("url2 needed if two revisions are not provided")
- if rev1:
- cmd.append("%s@%s" % (url1, rev1))
- else:
- cmd.append(url1)
- if rev2:
- cmd.append("%s@%s" % (url2, rev2))
- else:
- cmd.append(url2)
- if path:
- cmd.append(path)
- status, output = self._execsvn(*cmd, **kwargs)
- if status == 0:
- return [x.split() for x in output.split()]
- return None
-
- def diff(self, pathurl1, pathurl2=None, **kwargs):
- cmd = ["diff", pathurl1]
- self._add_revision(cmd, kwargs, optional=1)
- if pathurl2:
- cmd.append(pathurl2)
- status, output = self._execsvn(*cmd, **kwargs)
- if status == 0:
- return output
- return None
-
- def cat(self, url, **kwargs):
- cmd = ["cat", url]
- self._add_revision(cmd, kwargs, optional=1)
- status, output = self._execsvn(*cmd, **kwargs)
- if status == 0:
- return output
- return None
-
- def log(self, url, start=None, end=0, limit=None, **kwargs):
- cmd = ["log", "-v", url]
- if start is not None or end != 0:
- if start is not None and type(start) is not type(0):
- try:
- start = int(start)
- except (ValueError, TypeError):
- raise Error("invalid log start revision provided")
- if type(end) is not type(0):
- try:
- end = int(end)
- except (ValueError, TypeError):
- raise Error("invalid log end revision provided")
- start = start or "HEAD"
- cmd.append("-r %s:%s" % (start, end))
- if limit is not None:
- try:
- limit = int(limit)
- except (ValueError, TypeError):
- raise Error("invalid limit number provided")
- cmd.append("--limit %d" % limit)
- status, output = self._execsvn(*cmd, **kwargs)
- if status != 0:
- return None
-
- revheader = re.compile("^r(?P<revision>[0-9]+) \| (?P<author>[^\|]+) \| (?P<date>[^\|]+) \| (?P<lines>[0-9]+) (?:line|lines)$")
- changedpat = re.compile(r"^\s+(?P<action>[^\s]+) (?P<path>[^\s]+)(?: \([^\s]+ (?P<from_path>[^:]+)(?:\:(?P<from_rev>[0-9]+))?\))?$")
- logseparator = "-"*72
- linesleft = 0
- entry = None
- log = []
- appendchanged = 0
- changedheader = 0
- for line in output.splitlines():
- line = line.rstrip()
- if changedheader:
- appendchanged = 1
- changedheader = 0
- elif appendchanged:
- if not line:
- appendchanged = 0
- continue
- m = changedpat.match(line)
- if m:
- changed = m.groupdict().copy()
- from_rev = changed.get("from_rev")
- if from_rev is not None:
- try:
- changed["from_rev"] = int(from_rev)
- except (ValueError, TypeError):
- raise Error("invalid revision number in svn log")
- entry.changed.append(changed)
- elif linesleft == 0:
- if line != logseparator:
- m = revheader.match(line)
- if m:
- linesleft = int(m.group("lines"))
- timestr = " ".join(m.group("date").split()[:2])
- timetuple = time.strptime(timestr,
- "%Y-%m-%d %H:%M:%S")
- entry = SVNLogEntry(int(m.group("revision")),
- m.group("author"), timetuple)
- log.append(entry)
- changedheader = 1
- else:
- entry.lines.append(line)
- linesleft -= 1
- log.sort()
- log.reverse()
- return log
-
- def mv(self, path, dest, message=None, **kwargs):
- cmd = ["mv", path, dest, ]
- if message:
- cmd.append("-m '%s'"%message)
- else:
- kwargs['show'] = True
- self._add_log(cmd, kwargs)
- return self._execsvn_success(*cmd, **kwargs)
-
-class SVNLook:
+class SVNLook(VCSLook):
def __init__(self, repospath, txn=None, rev=None):
- self.repospath = repospath
- self.txn = txn
- self.rev = rev
-
- def _execsvnlook(self, cmd, *args, **kwargs):
- execcmd_args = ["svnlook", cmd, self.repospath]
- self._add_txnrev(execcmd_args, kwargs)
- execcmd_args += args
- execcmd_kwargs = {}
- keywords = ["show", "noerror"]
- for key in keywords:
- if key in kwargs:
- execcmd_kwargs[key] = kwargs[key]
- return execcmd(*execcmd_args, **execcmd_kwargs)
-
- def _add_txnrev(self, cmd_args, received_kwargs):
- if "txn" in received_kwargs:
- txn = received_kwargs.get("txn")
- if txn is not None:
- cmd_args += ["-t", txn]
- elif self.txn is not None:
- cmd_args += ["-t", self.txn]
- if "rev" in received_kwargs:
- rev = received_kwargs.get("rev")
- if rev is not None:
- cmd_args += ["-r", rev]
- elif self.rev is not None:
- cmd_args += ["-r", self.rev]
-
- def changed(self, **kwargs):
- status, output = self._execsvnlook("changed", **kwargs)
- if status != 0:
- return None
- changes = []
- for line in output.splitlines():
- line = line.rstrip()
- if not line:
- continue
- entry = [None, None, None]
- changedata, changeprop, path = None, None, None
- if line[0] != "_":
- changedata = line[0]
- if line[1] != " ":
- changeprop = line[1]
- path = line[4:]
- changes.append((changedata, changeprop, path))
- return changes
-
- def author(self, **kwargs):
- status, output = self._execsvnlook("author", **kwargs)
- if status != 0:
- return None
- return output.strip()
+ VCSLook.__init__(self, repospath, txn, rev)
+ self.execcmd = "svnlook"
# vim:et:ts=4:sw=4
diff --git a/MgaRepo/util.py b/MgaRepo/util.py
index 52ae50f..057923f 100644
--- a/MgaRepo/util.py
+++ b/MgaRepo/util.py
@@ -2,6 +2,7 @@
from MgaRepo import Error, config
+import shlex
import subprocess
import getpass
import sys
@@ -11,73 +12,75 @@ import select
from io import StringIO
import httplib2
-# Our own version of commands' commands_exec(). We have a commands
-# module directory, so we can't import Python's standard module
-
-# Our own version of commands' getstatusoutput(). We have a commands
-# module directory, so we can't import Python's standard module
-def commands_getstatusoutput(cmd):
- """Return (status, output) of executing cmd in a shell."""
- pipe = subprocess.Popen('{ ' + cmd + '; } 2>&1', stdin = subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines = True, shell = True)
- of = pipe.stdout.fileno()
- text = ''
- pipe.stdin.close()
- while True:
- text += os.read(of,8192).decode('utf8')
- status = pipe.poll()
- if status is not None or text == '':
- break
- if text[-1:] == '\n': text = text[:-1]
- return status, text
+class CommandError(Error):
+ def __init__(self, cmdline, status, output):
+ self.cmdline = cmdline
+ self.status = status
+ self.output = output
+
+ def __str__(self):
+ return "command failed: %s\n%s\n" % (self.cmdline, self.output)
def execcmd(*cmd, **kwargs):
- cmdstr = " ".join(cmd)
- verbose = config.getbool("global", "verbose", 0)
- if kwargs.get('info') :
- prefix='LANGUAGE=C LC_TIME=C '
+ assert (kwargs.get("collecterr") and kwargs.get("show")) or not kwargs.get("collecterr"), \
+ ("execcmd is implemented to handle collecterr=True only if show=True")
+ # split command args
+ if isinstance(cmd[0], str):
+ cmdargs = shlex.split(cmd[0])
else:
- prefix='LANG=C LANGUAGE=C LC_ALL=C '
- if verbose:
- print(prefix + cmdstr)
- if kwargs.get("show"):
- if kwargs.get("geterr"):
- err = StringIO()
- pstdin = kwargs.get("stdin") if kwargs.get("stdin") else None
- p = subprocess.Popen(prefix + cmdstr, shell=True,
- stdout=subprocess.PIPE, stderr=subprocess.PIPE,
- stdin=pstdin)
- of = p.stdout.fileno()
- ef = p.stderr.fileno()
- while True:
- r,w,x = select.select((of,ef), (), ())
- odata = None
- if of in r:
- odata = (os.read(of, 8192)).decode('utf8')
- sys.stdout.write(odata)
- edata = None
- if ef in r:
- edata = (os.read(ef, 8192)).decode('utf8')
- err.write(edata)
- sys.stderr.write(edata)
-
- status = p.poll()
- if status is not None and odata == '' and edata == '':
- break
- output = err.getvalue()
+ cmdargs = cmd[0][:]
+
+ stdout = None
+ stderr = None
+ env = {}
+ env.update(os.environ)
+ if kwargs.get("info") or not kwargs.get("show") or (kwargs.get("show") and kwargs.get("collecterr")):
+ if kwargs.get("info"):
+ env.update({"LANGUAGE": "C", "LC_TIME": "C"})
else:
- status = os.system(prefix + cmdstr)
- output = ""
- else:
- status, output = commands_getstatusoutput(prefix + cmdstr)
- if status != 0 and not kwargs.get("noerror"):
- if kwargs.get("cleanerr") and not verbose:
- raise Error(output)
+ env.update({"LANG": "C", "LANGUAGE": "C", "LC_ALL": "C"})
+ stdout = subprocess.PIPE
+ if kwargs.get("collecterr"):
+ stderr = subprocess.PIPE
else:
- raise Error("command failed: %s\n%s\n" % (cmdstr, output))
- if verbose:
- print(output)
- sys.stdout.write(output)
- return status, output
+ stderr = subprocess.STDOUT
+
+ proc = subprocess.Popen(cmdargs, shell=False, stdout=stdout,
+ stderr=stderr, env=env)
+
+ output = ""
+
+ if kwargs.get("show") and kwargs.get("collecterr"):
+ error = StringIO()
+ wl = []
+ outfd = proc.stdout.fileno()
+ errfd = proc.stderr.fileno()
+ rl = [outfd, errfd]
+ xl = wl
+ while proc.poll() is None:
+ mrl, _, _ = select.select(rl, wl, xl, 0.5)
+ for fd in mrl:
+ data = os.read(fd, 8192).decode('utf8')
+ if fd == errfd:
+ error.write(data)
+ sys.stderr.write(data)
+ else:
+ sys.stdout.write(data)
+ output = error.getvalue()
+ else:
+ proc.wait()
+ if proc.stdout is not None:
+ output = proc.stdout.read().decode('utf8')
+ if kwargs.get("strip", True):
+ output = output.rstrip()
+
+ if (not kwargs.get("noerror")) and proc.returncode != 0:
+ if kwargs.get("cleanerr"):
+ msg = output
+ cmdline = subprocess.list2cmdline(cmdargs)
+ raise CommandError(cmdline, proc.returncode, output)
+
+ return proc.returncode, output
def get_output_exec(cmdstr):
output = StringIO()