diff options
author | Nicolas Vigier <boklm@mageia.org> | 2011-01-11 00:35:59 +0000 |
---|---|---|
committer | Nicolas Vigier <boklm@mageia.org> | 2011-01-11 00:35:59 +0000 |
commit | ad7fb7807ceaee96521d779993a5e1b28650723f (patch) | |
tree | 2ece42aa7e83b7fdb51702b298aa3eec95da3573 /RepSys/log.py | |
parent | 715e125cc8d0b3fc4a79752e28a8b76a4ce97d5a (diff) | |
download | mgarepo-ad7fb7807ceaee96521d779993a5e1b28650723f.tar mgarepo-ad7fb7807ceaee96521d779993a5e1b28650723f.tar.gz mgarepo-ad7fb7807ceaee96521d779993a5e1b28650723f.tar.bz2 mgarepo-ad7fb7807ceaee96521d779993a5e1b28650723f.tar.xz mgarepo-ad7fb7807ceaee96521d779993a5e1b28650723f.zip |
rename repsys to mgarepo, RepSys to MgaRepo, and update docs and examples for Mageia
Diffstat (limited to 'RepSys/log.py')
-rw-r--r-- | RepSys/log.py | 633 |
1 files changed, 0 insertions, 633 deletions
diff --git a/RepSys/log.py b/RepSys/log.py deleted file mode 100644 index 6cb9da1..0000000 --- a/RepSys/log.py +++ /dev/null @@ -1,633 +0,0 @@ -#!/usr/bin/python -from RepSys import Error, config, layout -from RepSys.svn import SVN -from RepSys.util import execcmd - -try: - from Cheetah.Template import Template -except ImportError: - raise Error, "repsys requires the package python-cheetah" - -from cStringIO import StringIO - -import sys -import os -import re -import time -import locale -import glob -import tempfile -import shutil -import subprocess - - -locale.setlocale(locale.LC_ALL, "C") - -default_template = """ -#if not $releases_by_author[-1].visible - ## Hide the first release that contains no changes. It must be a - ## reimported package and the log gathered from misc/ already should - ## contain a correct entry for the version-release: - #set $releases_by_author = $releases_by_author[:-1] -#end if -#for $rel in $releases_by_author -* $rel.date $rel.author_name <$rel.author_email> $rel.version-$rel.release -+ Revision: $rel.revision -## #if not $rel.released -##+ Status: not released -## #end if - #if not $rel.visible -+ rebuild (emptylog) - #end if - #for $rev in $rel.release_revisions - #for $line in $rev.lines -$line - #end for - #end for - - #for $author in $rel.authors - #if not $author.visible - #continue - #end if - ##alternatively, one could use: - ###if $author.email == "root" - ## #continue - ###end if - + $author.name <$author.email> - #for $rev in $author.revisions - #for $line in $rev.lines - $line - #end for - #end for - - #end for -#end for -""" - -def getrelease(pkgdirurl, rev=None, macros=[], exported=None): - """Tries to obtain the version-release of the package for a - yet-not-markrelease revision of the package. - - Is here where things should be changed if "automatic release increasing" - will be used. - """ - from RepSys.rpmutil import rpm_macros_defs - svn = SVN() - pkgcurrenturl = os.path.join(pkgdirurl, "current") - specurl = os.path.join(pkgcurrenturl, "SPECS") - if exported is None: - tmpdir = tempfile.mktemp() - svn.export(specurl, tmpdir, rev=rev) - else: - tmpdir = os.path.join(exported, "SPECS") - try: - found = glob.glob(os.path.join(tmpdir, "*.spec")) - if not found: - raise Error, "no .spec file found inside %s" % specurl - specpath = found[0] - options = rpm_macros_defs(macros) - command = (("rpm -q --qf '%%{EPOCH}:%%{VERSION}-%%{RELEASE}\n' " - "--specfile %s %s") % - (specpath, options)) - pipe = subprocess.Popen(command, stdout=subprocess.PIPE, - stderr=subprocess.PIPE, shell=True) - pipe.wait() - output = pipe.stdout.read() - error = pipe.stderr.read() - if pipe.returncode != 0: - raise Error, "Error in command %s: %s" % (command, error) - releases = output.split() - try: - epoch, vr = releases[0].split(":", 1) - version, release = vr.split("-", 1) - except ValueError: - raise Error, "Invalid command output: %s: %s" % \ - (command, output) - #XXX check if this is the right way: - if epoch == "(none)": - ev = version - else: - ev = epoch + ":" + version - return ev, release - finally: - if exported is None and os.path.isdir(tmpdir): - shutil.rmtree(tmpdir) - -class _Revision: - lines = [] - date = None - raw_date = None - revision = None - author_name = None - author_email = None - - def __init__(self, **kwargs): - self.__dict__.update(kwargs) - - def __repr__(self): - lines = repr(self.lines)[:30] + "...]" - line = "<_Revision %d author=%r date=%r lines=%s>" % \ - (self.revision, self.author, self.date, lines) - return line - - -class _Release(_Revision): - version = None - release = None - revisions = [] - release_revisions = [] - authors = [] - visible = False - - def __init__(self, **kwargs): - self.revisions = [] - _Revision.__init__(self, **kwargs) - - def __repr__(self): - line = "<_Release v=%s r=%s revs=%r>" % \ - (self.version, self.release, self.revisions) - return line - -unescaped_macro_pat = re.compile(r"([^%])%([^%])") - -def escape_macros(text): - escaped = unescaped_macro_pat.sub("\\1%%\\2", text) - return escaped - -def format_lines(lines): - first = 1 - entrylines = [] - perexpr = re.compile(r"([^%])%([^%])") - for line in lines: - if line: - line = escape_macros(line) - if first: - first = 0 - line = line.lstrip() - if line[0] != "-": - nextline = "- " + line - else: - nextline = line - elif line[0] != " " and line[0] != "-": - nextline = " " + line - else: - nextline = line - if nextline not in entrylines: - entrylines.append(nextline) - return entrylines - - -class _Author: - name = None - email = None - revisions = None - visible = False - - -def group_releases_by_author(releases): - allauthors = [] - grouped = [] - for release in releases: - - # group revisions of the release by author - authors = {} - latest = None - for revision in release.revisions: - authors.setdefault(revision.author, []).append(revision) - - # create _Authors and sort them by their latest revisions - decorated = [] - for authorname, revs in authors.iteritems(): - author = _Author() - author.name = revs[0].author_name - author.email = revs[0].author_email - author.revisions = revs - # #41117: mark those authors without visible messages - author.visible = bool(sum(len(rev.lines) for rev in revs)) - revlatest = author.revisions[0] - # keep the latest revision even for completely invisible - # authors (below) - if latest is None or revlatest.revision > latest.revision: - latest = revlatest - if not author.visible: - # only sort those visible authors, invisible ones are used - # only in "latest" - continue - decorated.append((revlatest.revision, author)) - decorated.sort(reverse=1) - - if release.visible: - release.authors = [t[1] for t in decorated] - firstrel, release.authors = release.authors[0], release.authors[1:] - release.author_name = firstrel.name - release.author_email = firstrel.email - release.release_revisions = firstrel.revisions - else: - # we don't care about other possible authors in completely - # invisible releases - firstrev = release.revisions[0] - release.author_name = firstrev.author_name - release.author_email = firstrev.author_email - release.raw_date = firstrev.raw_date - release.date = firstrev.date - - release.date = latest.date - release.raw_date = latest.raw_date - release.revision = latest.revision - - grouped.append(release) - - return grouped - - -def group_revisions_by_author(currentlog): - revisions = [] - last_author = None - for entry in currentlog: - revision = _Revision() - revision.lines = format_lines(entry.lines) - revision.raw_date = entry.date - revision.date = parse_raw_date(entry.date) - revision.revision = entry.revision - if entry.author == last_author: - revisions[-1].revisions.append(revision) - else: - author = _Author() - author.name, author.email = get_author_name(entry.author) - author.revisions = [revision] - revisions.append(author) - last_author = entry.author - return revisions - - -emailpat = re.compile("(?P<name>.*?)\s*<(?P<email>.*?)>") - -def get_author_name(author): - found = emailpat.match(config.get("users", author, author)) - name = ((found and found.group("name")) or author) - email = ((found and found.group("email")) or author) - return name, email - -def parse_raw_date(rawdate): - return time.strftime("%a %b %d %Y", rawdate) - -def filter_log_lines(lines): - # Lines in commit messages beginning with CLOG will be the only shown - # in the changelog. These lines will have the CLOG token and blanks - # stripped from the beginning. - onlylines = None - clogstr = config.get("log", "unignore-string") - if clogstr: - clogre = re.compile(r"(^%s[^ \t]?[ \t])" % clogstr) - onlylines = [clogre.sub("", line) - for line in lines if line.startswith(clogstr)] - if onlylines: - filtered = onlylines - else: - # Lines in commit messages containing SILENT at any position will be - # skipped; commits with their log messages beggining with SILENT in the - # first positionj of the first line will have all lines ignored. - ignstr = config.get("log", "ignore-string", "SILENT") - if len(lines) and lines[0].startswith(ignstr): - return [] - filtered = [line for line in lines if ignstr not in line] - return filtered - - -def make_release(author=None, revision=None, date=None, lines=None, - entries=[], released=True, version=None, release=None): - rel = _Release() - rel.author = author - if author: - rel.author_name, rel.author_email = get_author_name(author) - rel.revision = revision - rel.version = version - rel.release = release - rel.date = (date and parse_raw_date(date)) or None - rel.lines = lines - rel.released = released - rel.visible = False - for entry in entries: - lines = filter_log_lines(entry.lines) - revision = _Revision() - revision.revision = entry.revision - revision.lines = format_lines(lines) - if revision.lines: - rel.visible = True - revision.date = parse_raw_date(entry.date) - revision.raw_date = entry.date - revision.author = entry.author - (revision.author_name, revision.author_email) = \ - get_author_name(entry.author) - rel.revisions.append(revision) - return rel - - -def dump_file(releases, currentlog=None, template=None): - templpath = template or config.get("template", "path", - "/usr/share/repsys/default.chlog") - params = {} - if templpath is None or not os.path.exists(templpath): - params["source"] = default_template - sys.stderr.write("warning: %s not found. using built-in template.\n"% - templpath) - else: - params["file"] = templpath - releases_author = group_releases_by_author(releases) - revisions_author = group_revisions_by_author(currentlog) - params["searchList"] = [{"releases_by_author" : releases_author, - "releases" : releases, - "revisions_by_author": revisions_author}] - t = Template(**params) - return t.respond() - - -class InvalidEntryError(Exception): - pass - -def parse_repsys_entry(revlog): - # parse entries in the format: - # %repsys <operation> - # key: value - # .. - # <newline> - # <comments> - # - if len(revlog.lines) == 0 or not revlog.lines[0].startswith("%repsys"): - raise InvalidEntryError - try: - data = {"operation" : revlog.lines[0].split()[1]} - except IndexError: - raise InvalidEntryError - for line in revlog.lines[1:]: - if not line: - break - try: - key, value = line.split(":", 1) - except ValueError: - raise InvalidEntryError - data[key.strip().lower()] = value.strip() # ??? - return data - - -def get_revision_offset(): - try: - revoffset = config.getint("log", "revision-offset", 0) - except (ValueError, TypeError): - raise Error, ("Invalid revision-offset number in configuration " - "file(s).") - return revoffset or 0 - -oldmsgpat = re.compile( - r"Copying release (?P<rel>[^\s]+) to (?P<dir>[^\s]+) directory\.") - -def parse_markrelease_log(relentry): - if not ((relentry.lines and oldmsgpat.match(relentry.lines[0]) \ - or parse_repsys_entry(relentry))): - raise InvalidEntryError - from_rev = None - path = None - for changed in relentry.changed: - if changed["action"] == "A" and changed["from_rev"]: - from_rev = changed["from_rev"] - path = changed["path"] - break - else: - raise InvalidEntryError - # get the version and release from the names in the path, do not relay - # on log messages - version, release = path.rsplit(os.path.sep, 3)[-2:] - return version, release, from_rev - - -def svn2rpm(pkgdirurl, rev=None, size=None, submit=False, - template=None, macros=[], exported=None): - concat = config.get("log", "concat", "").split() - revoffset = get_revision_offset() - svn = SVN() - pkgreleasesurl = layout.checkout_url(pkgdirurl, releases=True) - pkgcurrenturl = layout.checkout_url(pkgdirurl) - releaseslog = svn.log(pkgreleasesurl, noerror=1) - currentlog = svn.log(pkgcurrenturl, limit=size, start=rev, - end=revoffset) - - # sort releases by copyfrom-revision, so that markreleases for same - # revisions won't look empty - releasesdata = [] - if releaseslog: - for relentry in releaseslog[::-1]: - try: - (version, release, relrevision) = \ - parse_markrelease_log(relentry) - except InvalidEntryError: - continue - releasesdata.append((relrevision, -relentry.revision, relentry, - version, release)) - releasesdata.sort() - - # collect valid releases using the versions provided by the changes and - # the packages - prevrevision = 0 - releases = [] - for (relrevision, dummy, relentry, version, release) in releasesdata: - if prevrevision == relrevision: - # ignore older markrelease of the same revision, since they - # will have no history - continue - entries = [entry for entry in currentlog - if relrevision >= entry.revision and - (prevrevision < entry.revision)] - if not entries: - #XXX probably a forced release, without commits in current/, - # check if this is the right behavior - sys.stderr.write("warning: skipping (possible) release " - "%s-%s@%s, no commits since previous markrelease (r%r)\n" % - (version, release, relrevision, prevrevision)) - continue - - release = make_release(author=relentry.author, - revision=relentry.revision, date=relentry.date, - lines=relentry.lines, entries=entries, - version=version, release=release) - releases.append(release) - prevrevision = relrevision - - # look for commits that have been not submitted (released) yet - # this is done by getting all log entries newer (greater revision no.) - # than releasesdata[-1] (in the case it exists) - if releasesdata: - latest_revision = releasesdata[-1][0] # the latest copied rev - else: - latest_revision = 0 - notsubmitted = [entry for entry in currentlog - if entry.revision > latest_revision] - if notsubmitted: - # if they are not submitted yet, what we have to do is to add - # a release/version number from getrelease() - version, release = getrelease(pkgdirurl, macros=macros, - exported=exported) - toprelease = make_release(entries=notsubmitted, released=False, - version=version, release=release) - releases.append(toprelease) - - data = dump_file(releases[::-1], currentlog=currentlog, template=template) - return data - -def _split_changelog(stream): - current = None - count = 0 - def finish(entry): - lines = entry[2] - # strip newlines at the end - for i in xrange(len(lines)-1, -1, -1): - if lines[i] != "\n": - break - del lines[i] - return entry - for line in stream: - if line.startswith("*"): - if current: - yield finish(current) - fields = line.split() - rawdate = " ".join(fields[:5]) - try: - date = time.strptime(rawdate, "* %a %b %d %Y") - except ValueError, e: - raise Error, "failed to parse spec changelog: %s" % e - curlines = [line] - current = (date, count, curlines) - # count used to ensure stable sorting when changelog entries - # have the same date, otherwise it would also compare the - # changelog lines - count -= 1 - elif current: - curlines.append(line) - else: - pass # not good, but ignore - if current: - yield finish(current) - -def sort_changelog(stream): - entries = _split_changelog(stream) - log = StringIO() - for time, count, elines in sorted(entries, reverse=True): - log.writelines(elines) - log.write("\n") - return log - -def split_spec_changelog(stream): - chlog = StringIO() - spec = StringIO() - found = 0 - visible = 0 - for line in stream: - if line.startswith("%changelog"): - found = 1 - elif not found: - spec.write(line) - elif found: - if line.strip(): - visible = 1 - chlog.write(line) - elif line.startswith("%"): - found = 0 - spec.write(line) - spec.seek(0) - if not visible: - # when there are only blanks in the changelog, make it empty - chlog = StringIO() - return spec, chlog - -def get_old_log(pkgdirurl): - chlog = StringIO() - oldurl = config.get("log", "oldurl") - if oldurl: - svn = SVN() - tmpdir = tempfile.mktemp() - try: - pkgname = layout.package_name(pkgdirurl) - pkgoldurl = os.path.join(oldurl, pkgname) - try: - # we're using HEAD here because fixes in misc/ (oldurl) may - # be newer than packages' last changed revision. - svn.export(pkgoldurl, tmpdir) - except Error: - pass - else: - logfile = os.path.join(tmpdir, "log") - if os.path.isfile(logfile): - file = open(logfile) - chlog.write("\n") # TODO needed? - log = file.read() - log = escape_macros(log) - chlog.write(log) - file.close() - finally: - if os.path.isdir(tmpdir): - shutil.rmtree(tmpdir) - chlog.seek(0) - return chlog - -def get_changelog(pkgdirurl, another=None, svn=True, rev=None, size=None, - submit=False, sort=False, template=None, macros=[], exported=None, - oldlog=False): - """Generates the changelog for a given package URL - - @another: a stream with the contents of a changelog to be merged with - the one generated - @svn: enable changelog from svn - @rev: generate the changelog with the changes up to the given - revision - @size: the number of revisions to be used (as in svn log --limit) - @submit: defines whether the latest unreleased log entries should have - the version parsed from the spec file - @sort: should changelog entries be reparsed and sorted after appending - the oldlog? - @template: the path to the cheetah template used to generate the - changelog from svn - @macros: a list of tuples containing macros to be defined when - parsing the version in the changelog - @exported: the path of a directory containing an already existing - checkout of the package, so that the spec file can be - parsed from there - @oldlog: if set it will try to append the old changelog file defined - in oldurl in repsys.conf - """ - newlog = StringIO() - if svn: - rawsvnlog = svn2rpm(pkgdirurl, rev=rev, size=size, submit=submit, - template=template, macros=macros, exported=exported) - newlog.write(rawsvnlog) - if another: - newlog.writelines(another) - if oldlog: - newlog.writelines(get_old_log(pkgdirurl)) - if sort: - newlog.seek(0) - newlog = sort_changelog(newlog) - newlog.seek(0) - return newlog - -def specfile_svn2rpm(pkgdirurl, specfile, rev=None, size=None, - submit=False, sort=False, template=None, macros=[], exported=None): - fi = open(specfile) - spec, oldchlog = split_spec_changelog(fi) - fi.close() - another = None - if config.getbool("log", "merge-spec", False): - another = oldchlog - sort = sort or config.getbool("log", "sort", False) - chlog = get_changelog(pkgdirurl, another=another, rev=rev, size=size, - submit=submit, sort=sort, template=template, macros=macros, - exported=exported, oldlog=True) - fo = open(specfile, "w") - fo.writelines(spec) - fo.write("\n\n%changelog\n") - fo.writelines(chlog) - fo.close() - -if __name__ == "__main__": - l = svn2rpm(sys.argv[1]) - print l - -# vim:et:ts=4:sw=4 |