diff options
author | Bogdano Arendartchuk <bogdano@mandriva.org> | 2008-02-08 01:26:06 +0000 |
---|---|---|
committer | Bogdano Arendartchuk <bogdano@mandriva.org> | 2008-02-08 01:26:06 +0000 |
commit | 56a69b1bce8e89c97317eae95bc09f2e7de8c1d2 (patch) | |
tree | 63dd50265afd2b2385badb46476ff607ca5e10f4 | |
parent | 4a27a17de745d90553dfad701fc6c65da24680d1 (diff) | |
parent | 1e929f67375b699cdcac0b1557d115eed7035ae8 (diff) | |
download | mgarepo-56a69b1bce8e89c97317eae95bc09f2e7de8c1d2.tar mgarepo-56a69b1bce8e89c97317eae95bc09f2e7de8c1d2.tar.gz mgarepo-56a69b1bce8e89c97317eae95bc09f2e7de8c1d2.tar.bz2 mgarepo-56a69b1bce8e89c97317eae95bc09f2e7de8c1d2.tar.xz mgarepo-56a69b1bce8e89c97317eae95bc09f2e7de8c1d2.zip |
New branch to work on tarballs repository
-rw-r--r-- | CHANGES | 36 | ||||
-rw-r--r-- | MANIFEST.in | 2 | ||||
-rw-r--r-- | PKG-INFO | 2 | ||||
-rw-r--r-- | RepSys/ConfigParser.py | 5 | ||||
-rw-r--r-- | RepSys/command.py | 8 | ||||
-rw-r--r-- | RepSys/commands/authoremail.py | 3 | ||||
-rw-r--r-- | RepSys/commands/changed.py | 2 | ||||
-rw-r--r-- | RepSys/commands/ci.py | 12 | ||||
-rw-r--r-- | RepSys/commands/co.py | 6 | ||||
-rw-r--r-- | RepSys/commands/create.py | 5 | ||||
-rw-r--r-- | RepSys/commands/editlog.py | 3 | ||||
-rw-r--r-- | RepSys/commands/getspec.py | 5 | ||||
-rw-r--r-- | RepSys/commands/getsrpm.py | 29 | ||||
-rw-r--r-- | RepSys/commands/markrelease.py | 5 | ||||
-rw-r--r-- | RepSys/commands/patchspec.py | 2 | ||||
-rw-r--r-- | RepSys/commands/rpmlog.py | 5 | ||||
-rw-r--r-- | RepSys/commands/submit.py | 35 | ||||
-rw-r--r-- | RepSys/commands/switch.py | 7 | ||||
-rw-r--r-- | RepSys/commands/sync.py | 4 | ||||
-rw-r--r-- | RepSys/log.py | 129 | ||||
-rw-r--r-- | RepSys/mirror.py | 16 | ||||
-rw-r--r-- | RepSys/rpmutil.py | 161 | ||||
-rw-r--r-- | RepSys/svn.py | 551 | ||||
-rw-r--r-- | RepSys/util.py | 3 | ||||
-rw-r--r-- | compatv15.chlog | 13 | ||||
-rwxr-xr-x | create-srpm | 12 | ||||
-rw-r--r-- | default.chlog | 6 | ||||
-rwxr-xr-x | repsys | 46 | ||||
-rw-r--r-- | repsys-example.conf | 65 | ||||
-rw-r--r-- | repsys.conf | 43 | ||||
-rw-r--r-- | repsys.spec | 22 | ||||
-rwxr-xr-x | setup.py | 4 |
32 files changed, 796 insertions, 451 deletions
@@ -1,6 +1,39 @@ +* 1.6.20 +- dropped all authenticated access support: subversion authentication has + been broken for a long time and the workarounds weren't decent. It will + be back in 1.7.x. +- added configuration option svn-command in the global section, allowing + to replace the default svn command +- force svn+ssh:// URLs to be in BatchMode, in order to not have any + interactivity at all with ssh +- fixed incompatibility with Python-2.4 on urlparse +- fixed emptylog message, which was not being shown when needed +- template: hide the first release when it has only invisible lines +- fixed bad url used when using -v in getsrpm +- make 'repsys submit' without package name or revision number work again +- if REPSYS_CONF is set, /etc/repsys.conf and ~/.repsys/config will not be + readed anymore +- changed the built-in template to the current default.chlog +- added option -d to repsys sync, to download the missing source files +- added option -F to repsys ci to set a log message file +- added option --strict to getsrpm to check if the revision provided + matches the package URL; +- added boolean configuration option strict-revision in the submit + section, to allow forcing the use of --strict +- added option --list in create-srpm to list the available targets +- make submit -l work on svn+ssh:// targets +- the fix for the unreleased commits problem in the previous release was + wrong, really fixed it +- moved all configuration options that will hardly be changed to + repsys-example.conf; we now have a shorter repsys.conf +- fixed the use of file:/// URLs when using just the package name +- don't give the wrong message "invalid command 'CMD'" when this is not + the case +- added more help messages in subcommands + * 1.6.19 - added complement for SILENT: CLOG, which hides everything that does not - start with this token + start with this token - fixed generation of unreleased commits, it was using the previous markrelease revision as reference - added option -o to 'co' to disable the use of mirror when checking out @@ -14,6 +47,7 @@ - make "sync" compatible with rpm-4.4.8 behavior - "co" don't use mirror when URL is provided - "ci" don't relocate back to mirrors after commit (should use switch) +- ldapusers: added options ldap-uri and ldap-starttls - fixed use of __import__, incompatible with python2.4 in plugin support - fixed bug of wrong paths when using mirrors diff --git a/MANIFEST.in b/MANIFEST.in index 1264d01..f014036 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,8 @@ recursive-include RepSys *.py include RepSys/plugins/*.txt include repsys repsys.conf MANIFEST.in +include CHANGES include README.LDAP +include repsys-example.conf include *.chlog include rebrand-mdk create-srpm getsrpm-mdk @@ -1,6 +1,6 @@ Metadata-Version: 1.0 Name: repsys -Version: 1.6.0 +Version: 1.6.15 Summary: Tools for Mandriva Linux repository access and management Home-page: http://qa.mandriva.com/twiki/bin/view/Main/RepositorySystem Author: Gustavo Niemeyer diff --git a/RepSys/ConfigParser.py b/RepSys/ConfigParser.py index 4dc3e3c..3b4e213 100644 --- a/RepSys/ConfigParser.py +++ b/RepSys/ConfigParser.py @@ -350,11 +350,12 @@ class Config: self._config = ConfigParser() self._wrapped = {} conffiles = [] - conffiles.append("/etc/repsys.conf") repsys_conf = os.environ.get("REPSYS_CONF") if repsys_conf: conffiles.append(repsys_conf) - conffiles.append(os.path.expanduser("~/.repsys/config")) + else: + conffiles.append("/etc/repsys.conf") + conffiles.append(os.path.expanduser("~/.repsys/config")) for file in conffiles: if os.path.isfile(file): self._config.read(file) diff --git a/RepSys/command.py b/RepSys/command.py index 8029e08..1833bcd 100644 --- a/RepSys/command.py +++ b/RepSys/command.py @@ -1,6 +1,7 @@ #!/usr/bin/python from RepSys import Error, config -import sys, os, urllib +import sys, os +import urlparse import optparse __all__ = ["OptionParser", "do_command", "default_parent"] @@ -46,8 +47,9 @@ def default_parent(url): if not default_parent: raise Error, "received a relative url, " \ "but default_parent was not setup" - type, rest = urllib.splittype(default_parent) - url = type+':'+os.path.normpath(rest+'/'+url) + parsed = list(urlparse.urlparse(default_parent)) + parsed[2] = os.path.normpath(parsed[2] + "/" + url) + url = urlparse.urlunparse(parsed) return url # vim:et:ts=4:sw=4 diff --git a/RepSys/commands/authoremail.py b/RepSys/commands/authoremail.py index aee7b58..f5b8b70 100644 --- a/RepSys/commands/authoremail.py +++ b/RepSys/commands/authoremail.py @@ -7,6 +7,9 @@ import getopt HELP = """\ Usage: repsys authoremail [OPTIONS] AUTHOR +Shows the e-mail of an SVN author. It is just a simple interface to access +the [authors] section of repsys.conf. + Options: -h Show this message diff --git a/RepSys/commands/changed.py b/RepSys/commands/changed.py index d3094a8..62b20b6 100644 --- a/RepSys/commands/changed.py +++ b/RepSys/commands/changed.py @@ -8,6 +8,8 @@ import sys HELP = """\ Usage: repsys changed [OPTIONS] URL +Shows if there are pending changes since the last package release. + Options: -a Check all packages in given URL -s Show differences diff --git a/RepSys/commands/ci.py b/RepSys/commands/ci.py index 9ffa3bd..8d373b5 100644 --- a/RepSys/commands/ci.py +++ b/RepSys/commands/ci.py @@ -5,12 +5,16 @@ from RepSys.rpmutil import commit HELP = """\ Usage: repsys ci [TARGET] -Will commit a change. The difference between an ordinary "svn ci" and -"repsys ci" is that it relocates the working copy to the default repository -in case the option "mirror" is set in repsys.conf. +Will commit recent modifications in the package. + +The difference between an ordinary "svn ci" and "repsys ci" is that it +relocates the working copy to the default repository in case the option +"mirror" is set in repsys.conf. Options: -h Show this message + -m MSG Use the MSG as the log message + -F FILE Read log message from FILE Examples: repsys ci @@ -20,6 +24,8 @@ Examples: def parse_options(): parser = OptionParser(help=HELP) parser.add_option("-m", dest="message", default=None) + parser.add_option("-F", dest="logfile", type="string", + default=None) opts, args = parser.parse_args() if len(args): opts.target = args[0] diff --git a/RepSys/commands/co.py b/RepSys/commands/co.py index cadcf56..830b7e7 100644 --- a/RepSys/commands/co.py +++ b/RepSys/commands/co.py @@ -8,12 +8,18 @@ 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. + Options: -r REV Revision to checkout -o Do not use the mirror (use official server) -h Show this message Examples: + repsys co pkgname repsys co http://repos/svn/cnc/snapshot/foo repsys co http://repos/svn/cnc/snapshot/foo foo-pkg """ diff --git a/RepSys/commands/create.py b/RepSys/commands/create.py index 56af1ef..a8709f0 100644 --- a/RepSys/commands/create.py +++ b/RepSys/commands/create.py @@ -8,11 +8,14 @@ import sys HELP = """\ Usage: repsys create [OPTIONS] URL +Creates the minimal structure of a package in the repository. + Options: -h Show this message Examples: - repsys create http://repos/svn/cnc/snapshot/newpkg + repsys create newpkg + repsys create svn+ssh://svn.mandriva.com/svn/packages/cooker/newpkg """ def parse_options(): diff --git a/RepSys/commands/editlog.py b/RepSys/commands/editlog.py index 367238f..1962ed8 100644 --- a/RepSys/commands/editlog.py +++ b/RepSys/commands/editlog.py @@ -30,8 +30,7 @@ def parse_options(): def editlog(pkgdirurl, revision): svn = SVN() - svn.propedit("svn:log", pkgdirurl, revision=SVN.makerev(revision), - revprop=True) + svn.propedit("svn:log", pkgdirurl, rev=revision) def main(): do_command(parse_options, editlog) diff --git a/RepSys/commands/getspec.py b/RepSys/commands/getspec.py index 1079a81..5e44074 100644 --- a/RepSys/commands/getspec.py +++ b/RepSys/commands/getspec.py @@ -8,12 +8,15 @@ import sys HELP = """\ Usage: repsys getspec [OPTIONS] REPPKGURL +Prints the .spec file of a given package. + Options: -t DIR Use DIR as target for spec file (default is ".") -h Show this message Examples: - repsys getspec http://repos/svn/cnc/snapshot/foo + repsys getspec pkgname + repsys getspec svn+ssh://svn.mandriva.com/svn/packages/cooker/pkgname """ def parse_options(): diff --git a/RepSys/commands/getsrpm.py b/RepSys/commands/getsrpm.py index d76aca7..f9a63d2 100644 --- a/RepSys/commands/getsrpm.py +++ b/RepSys/commands/getsrpm.py @@ -16,20 +16,25 @@ import os HELP = """\ Usage: repsys getsrpm [OPTIONS] REPPKGURL +Generates the source RPM (.srpm) file of a given package. + Options: - -c Use files in current/ directory (default) - -p Use files in pristine/ directory - -v VER Use files from the version specified by VER (e.g. 2.2.1-2cl) - -r REV Use files from current directory, in revision REV (e.g. 1001) - -t DIR Put SRPM file in directory DIR when done (default is ".") - -P USER Define the RPM packager inforamtion to USER - -s FILE Run script with "FILE TOPDIR SPECFILE" command - -n Rename the package to include the revision number - -l Use subversion log to build rpm %changelog - -T FILE Template to be used to generate the %changelog - -h Show this message + -c Use files in current/ directory (default) + -p Use files in pristine/ directory + -v VER Use files from the version specified by VER (e.g. 2.2.1-2cl) + -r REV Use files from current directory, in revision REV (e.g. 1001) + -t DIR Put SRPM file in directory DIR when done (default is ".") + -P USER Define the RPM packager inforamtion to USER + -s FILE Run script with "FILE TOPDIR SPECFILE" command + -n Rename the package to include the revision number + -l Use subversion log to build rpm %changelog + -T FILE Template to be used to generate the %changelog + -h Show this message + --strict Check if the given revision contains changes in REPPKGURL Examples: + repsys getsrpm python + repsys getsrpm -l python repsys getsrpm http://foo.bar/svn/cnc/snapshot/python repsys getsrpm -p http://foo.bar/svn/cnc/releases/8cl/python repsys getsrpm -r 1001 file:///svn/cnc/snapshot/python @@ -69,6 +74,8 @@ def parse_options(): parser.add_option("-n", dest="revname", action="store_true") parser.add_option("-l", dest="svnlog", action="store_true") parser.add_option("-T", dest="template", type="string", default=None) + parser.add_option("--strict", dest="strict", default=False, + action="store_true") opts, args = parser.parse_args() del opts.__ignore if len(args) != 1: diff --git a/RepSys/commands/markrelease.py b/RepSys/commands/markrelease.py index 440775b..1707f39 100644 --- a/RepSys/commands/markrelease.py +++ b/RepSys/commands/markrelease.py @@ -21,6 +21,11 @@ HELP = """\ Usage: repsys markrelease [OPTIONS] REPPKGURL +This subcommand creates a 'tag' for a given revision of a given package. + +The tag will be stored in the directory releases/ inside the package +structure. + Options: -f FILE Try to extract information from given file -r REV Revision which will be used to make the release copy tag diff --git a/RepSys/commands/patchspec.py b/RepSys/commands/patchspec.py index 155ff4f..8330cc3 100644 --- a/RepSys/commands/patchspec.py +++ b/RepSys/commands/patchspec.py @@ -11,6 +11,8 @@ import sys HELP = """\ Usage: repsys patchspec [OPTIONS] REPPKGURL PATCHFILE +It will try to patch a spec file from a given package url. + Options: -l LOG Use LOG as log message -h Show this message diff --git a/RepSys/commands/rpmlog.py b/RepSys/commands/rpmlog.py index 7ea1ac0..5ba5fdd 100644 --- a/RepSys/commands/rpmlog.py +++ b/RepSys/commands/rpmlog.py @@ -12,6 +12,8 @@ import sys HELP = """\ Usage: repsys rpmlog [OPTIONS] REPPKGDIRURL +Prints the RPM changelog of a given package. + Options: -r REV Collect logs from given revision to revision 0 -n NUM Output only last NUM entries @@ -19,7 +21,8 @@ Options: -h Show this message Examples: - repsys rpmlog https://repos/snapshot/python + repsys rpmlog python + repsys rpmlog http://svn.mandriva.com/svn/packages/cooker/python """ def parse_options(): diff --git a/RepSys/commands/submit.py b/RepSys/commands/submit.py index 5c95526..f1726dc 100644 --- a/RepSys/commands/submit.py +++ b/RepSys/commands/submit.py @@ -20,6 +20,17 @@ Usage: repsys submit [OPTIONS] [URL [REVISION]] Submits the package from URL to the submit host. +The submit host will try to build the package, and upon successful +completion will 'tag' the package and upload it to the official +repositories. + +The status of the submit can visualized at: + +http://kenobi.mandriva.com/bs/output.php + +If no URL and revision are specified, the latest changed revision in +the package working copy of the current directory will be used. + Options: -t TARGET Submit given package URL to given target -l Just list available targets @@ -37,13 +48,14 @@ Examples: repsys submit https://repos/svn/mdv/cooker/foo 14800 repsys submit -r 14800 https://repos/svn/mdv/cooker/foo repsys submit -l https://repos + repsys submit --define section=main/testing -t 2008.0 """ def parse_options(): parser = OptionParser(help=HELP) parser.defaults["revision"] = "" parser.add_option("-t", dest="target", default="Cooker") - parser.add_option("-l", dest="list", action="store_true") + parser.add_option("-l", action="callback", callback=list_targets) parser.add_option("-r", dest="revision", type="string", nargs=1) parser.add_option("-s", dest="submithost", type="string", nargs=1, default=None) @@ -51,15 +63,8 @@ def parse_options(): opts, args = parser.parse_args() if not args: name, rev = get_submit_info(".") - try: - yn = raw_input("Submit '%s', revision %d (y/N)? " % (name, rev)) - except KeyboardInterrupt: - yn = "n" - if yn.lower() in ("y", "yes"): - args = name, str(rev) - else: - print "Cancelled." - sys.exit(1) + args = name, str(rev) + print "submitting %s at revision %s..." % args elif len(args) > 2: raise Error, "invalid arguments" opts.pkgdirurl = default_parent(args[0]) @@ -72,6 +77,16 @@ def parse_options(): raise Error, "provide -l or a revision number" return opts +def list_targets(option, opt, val, parser): + host = config.get("submit", "host") + if host is None: + raise Error, "no submit host defined in repsys.conf" + createsrpm = get_helper("create-srpm") + #TODO make it configurable + command = "ssh %s %s --list" % (host, createsrpm) + execcmd(command, show=True) + sys.exit(0) + def submit(pkgdirurl, revision, target, list=0, define=[], submithost=None): #if not NINZ: # raise Error, "you must have NINZ installed to use this command" diff --git a/RepSys/commands/switch.py b/RepSys/commands/switch.py index dcbdd17..5cbe2d7 100644 --- a/RepSys/commands/switch.py +++ b/RepSys/commands/switch.py @@ -5,9 +5,10 @@ from RepSys.rpmutil import switch HELP = """\ Usage: repsys switch [URL] -Relocates the working copy to the base location URL. If URL is not -provided, it will use the option default_parent from repsys.conf as -default, or, if the current working copy is already based in +Relocates the working copy to the base location URL. + +If URL is not provided, it will use the option default_parent from +repsys.conf as default, or, if the current working copy is already based in default_parent, it will use the location from the mirror option from repsys.conf. diff --git a/RepSys/commands/sync.py b/RepSys/commands/sync.py index 42ede8d..a51db22 100644 --- a/RepSys/commands/sync.py +++ b/RepSys/commands/sync.py @@ -12,6 +12,8 @@ from the spec file. Options: --dry-run Print results without changing the working copy + --download -d + Try to download the source files not found -h Show this message Examples: @@ -22,6 +24,8 @@ def parse_options(): parser = OptionParser(help=HELP) parser.add_option("--dry-run", dest="dryrun", default=False, action="store_true") + parser.add_option("-d", "--download", dest="download", default=False, + action="store_true") opts, args = parser.parse_args() if len(args): opts.target = args[0] diff --git a/RepSys/log.py b/RepSys/log.py index 2c69693..60cf0d5 100644 --- a/RepSys/log.py +++ b/RepSys/log.py @@ -13,25 +13,43 @@ import os import re import time import locale -import codecs import glob import tempfile import shutil + +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 - ## - #if not $rel.released - (not released yet) ++ 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 +$line #end for #end for #for $author in $rel.authors + #if $author.revisions and not $author.revisions[0].lines + #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 @@ -50,14 +68,13 @@ def getrelease(pkgdirurl, rev=None, macros=[], exported=None): Is here where things should be changed if "automatic release increasing" will be used. """ - svn = SVN() from RepSys.rpmutil import rpm_macros_defs - tmpdir = tempfile.mktemp() + 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, revision=SVN.makerev(rev)) + svn.export(specurl, tmpdir, rev=rev) else: tmpdir = os.path.join(exported, "SPECS") try: @@ -89,8 +106,7 @@ def getrelease(pkgdirurl, rev=None, macros=[], exported=None): if exported is None and os.path.isdir(tmpdir): shutil.rmtree(tmpdir) - -class ChangelogRevision: +class _Revision: lines = [] date = None raw_date = None @@ -103,11 +119,12 @@ class ChangelogRevision: def __repr__(self): lines = repr(self.lines)[:30] + "...]" - line = "<ChangelogRevision %d author=%r date=%r lines=%s>" % \ + line = "<_Revision %d author=%r date=%r lines=%s>" % \ (self.revision, self.author, self.date, lines) return line -class ChangelogRelease(ChangelogRevision): + +class _Release(_Revision): version = None release = None revisions = [] @@ -116,11 +133,11 @@ class ChangelogRelease(ChangelogRevision): visible = False def __init__(self, **kwargs): - ChangelogRevision.__init__(self, **kwargs) self.revisions = [] + _Revision.__init__(self, **kwargs) def __repr__(self): - line = "<ChangelogRelease v=%s r=%s revs=%r>" % \ + line = "<_Release v=%s r=%s revs=%r>" % \ (self.version, self.release, self.revisions) return line @@ -153,7 +170,7 @@ def format_lines(lines): return entrylines -class ChangelogByAuthor: +class _Author: name = None email = None revisions = None @@ -163,49 +180,50 @@ 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) - # all the mess below is to sort by author and by revision number + # create _Authors and sort them by their latest revisions decorated = [] for authorname, revs in authors.iteritems(): - author = ChangelogByAuthor() + author = _Author() author.name = revs[0].author_name author.email = revs[0].author_email - revdeco = [(r.revision, r) for r in revs] - revdeco.sort(reverse=1) - author.revisions = [t[1] for t in revdeco] + author.revisions = revs revlatest = author.revisions[0] - # keep the latest revision even for silented authors (below) + # keep the latest revision even for completely invisible + # authors (below) if latest is None or revlatest.revision > latest.revision: latest = revlatest count = sum(len(rev.lines) for rev in author.revisions) if count == 0: - # skipping author with only silented lines + # only sort those visible authors, invisible ones are used + # only in "latest" continue - decorated.append((revdeco[0][0], author)) + decorated.append((revlatest.revision, author)) + decorated.sort(reverse=1) - if not decorated: - # skipping release with only authors with silented lines - continue + 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 - decorated.sort(reverse=1) - release.authors = [t[1] for t in decorated] - # the difference between a released and a not released _Release is - # the way the release numbers is obtained. So, when this is a - # released, we already have it, but if we don't, we should get de - # version/release string using getrelease and then get the first - first, release.authors = release.authors[0], release.authors[1:] - release.author_name = first.name - release.author_email = first.email - release.release_revisions = first.revisions - - #release.date = first.revisions[0].date release.date = latest.date release.raw_date = latest.raw_date - #release.revision = first.revisions[0].revision release.revision = latest.revision grouped.append(release) @@ -217,7 +235,7 @@ def group_revisions_by_author(currentlog): revisions = [] last_author = None for entry in currentlog: - revision = ChangelogRevision() + revision = _Revision() revision.lines = format_lines(entry.lines) revision.raw_date = entry.date revision.date = parse_raw_date(entry.date) @@ -225,7 +243,7 @@ def group_revisions_by_author(currentlog): if entry.author == last_author: revisions[-1].revisions.append(revision) else: - author = ChangelogByAuthor() + author = _Author() author.name, author.email = get_author_name(entry.author) author.revisions = [revision] revisions.append(author) @@ -269,7 +287,7 @@ def filter_log_lines(lines): def make_release(author=None, revision=None, date=None, lines=None, entries=[], released=True, version=None, release=None): - rel = ChangelogRelease() + rel = _Release() rel.author = author if author: rel.author_name, rel.author_email = get_author_name(author) @@ -284,7 +302,7 @@ def make_release(author=None, revision=None, date=None, lines=None, lines = filter_log_lines(entry.lines) if lines: rel.visible = True - revision = ChangelogRevision() + revision = _Revision() revision.revision = entry.revision revision.lines = format_lines(lines) revision.date = parse_raw_date(entry.date) @@ -297,7 +315,8 @@ def make_release(author=None, revision=None, date=None, lines=None, def dump_file(releases, currentlog=None, template=None): - templpath = template or config.get("template", "path", 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 @@ -311,12 +330,7 @@ def dump_file(releases, currentlog=None, template=None): "releases" : releases, "revisions_by_author": revisions_author}] t = Template(**params) - chlog = t.respond() - try: - chlog = chlog.decode("utf8") - except UnicodeError: - pass - return chlog + return t.respond() class InvalidEntryError(Exception): @@ -379,18 +393,14 @@ def parse_markrelease_log(relentry): def svn2rpm(pkgdirurl, rev=None, size=None, submit=False, template=None, macros=[], exported=None): - size = size or 0 concat = config.get("log", "concat", "").split() revoffset = get_revision_offset() svn = SVN() pkgreleasesurl = os.path.join(pkgdirurl, "releases") pkgcurrenturl = os.path.join(pkgdirurl, "current") - releaseslog = list(svn.log(pkgreleasesurl, - strict_node_history=False, noerror=1)) or [] - currentlog = list(svn.log(pkgcurrenturl, - strict_node_history=False, - revision_start=SVN.makerev(rev), - revision_end=SVN.makerev(revoffset), limit=size)) + 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 @@ -460,11 +470,6 @@ def specfile_svn2rpm(pkgdirurl, specfile, rev=None, size=None, submit=False, template=None, macros=[], exported=None): newlines = [] found = 0 - - encoding = locale.getpreferredencoding() - - def open(name, mode="r"): - return codecs.open(name, mode, encoding, errors="replace") # Strip old changelogs for line in open(specfile): diff --git a/RepSys/mirror.py b/RepSys/mirror.py index 1e9e9c2..5b114df 100644 --- a/RepSys/mirror.py +++ b/RepSys/mirror.py @@ -7,16 +7,16 @@ from RepSys.svn import SVN def _normdirurl(url): """normalize url for relocate_path needs""" parsed = urlparse.urlparse(url) - path = os.path.normpath(parsed.path) - newurl = urlparse.urlunparse((parsed.scheme, parsed.netloc, path, - parsed.params, parsed.query, parsed.fragment)) + path = os.path.normpath(parsed[2]) + newurl = urlparse.urlunparse((parsed[0], parsed[1], path, + parsed[3], parsed[4], parsed[5])) return newurl def _joinurl(url, relpath): parsed = urlparse.urlparse(url) - newpath = os.path.join(parsed.path, relpath) - newurl = urlparse.urlunparse((parsed.scheme, parsed.netloc, newpath, - parsed.params, parsed.query, parsed.fragment)) + newpath = os.path.join(parsed[2], relpath) + newurl = urlparse.urlunparse((parsed[0], parsed[1], newpath, + parsed[3], parsed[4], parsed[5])) return newurl def same_base(parent, url): @@ -47,9 +47,7 @@ def enabled(wcurl=None): def mirror_relocate(oldparent, newparent, url, wcpath): svn = SVN() newurl = relocate_path(oldparent, newparent, url) - # note that svn.relocate requires paths without trailling slashes, - # http://foo/svn/bar/baz/ will fail - svn.relocate(url, newurl, wcpath) + svn.switch(newurl, url, path=wcpath, relocate="True") return newurl def switchto_parent(svn, url, path): diff --git a/RepSys/rpmutil.py b/RepSys/rpmutil.py index 4cbba62..3bb1b50 100644 --- a/RepSys/rpmutil.py +++ b/RepSys/rpmutil.py @@ -5,11 +5,11 @@ from RepSys.svn import SVN from RepSys.simplerpm import SRPM from RepSys.log import specfile_svn2rpm from RepSys.util import execcmd -import pysvn from RepSys.command import default_parent import rpm import tempfile import shutil +import string import glob import sys import os @@ -19,7 +19,7 @@ def get_spec(pkgdirurl, targetdir=".", submit=False): tmpdir = tempfile.mktemp() try: geturl = "/".join([pkgdirurl, "current", "SPECS"]) - svn.export("%s" % geturl, tmpdir) + svn.export("'%s'" % geturl, tmpdir) speclist = glob.glob(os.path.join(tmpdir, "*.spec")) if not speclist: raise Error, "no spec files found" @@ -34,6 +34,22 @@ def rpm_macros_defs(macros): args = " ".join(defs) return args +def rev_touched_url(url, rev): + svn = SVN() + info = svn.info2(url) + if info is None: + raise Error, "can't fetch svn info about the URL: %s" % url + root = info["Repository Root"] + urlpath = url[len(root):] + touched = False + entries = svn.log(root, start=rev, limit=1) + entry = entries[0] + for change in entry.changed: + path = change.get("path") + if path and path.startswith(urlpath): + touched = True + return touched + def get_srpm(pkgdirurl, mode = "current", targetdirs = None, @@ -47,7 +63,8 @@ def get_srpm(pkgdirurl, submit = False, template = None, macros = [], - verbose = 0): + verbose = 0, + strict = False): svn = SVN() tmpdir = tempfile.mktemp() topdir = "--define '_topdir %s'" % tmpdir @@ -67,7 +84,13 @@ def get_srpm(pkgdirurl, geturl = os.path.join(pkgdirurl, "current") else: raise Error, "unsupported get_srpm mode: %s" % mode - exportedrev = svn.export(geturl, tmpdir, revision=SVN.makerev(revision)) + strict = strict or config.getbool("submit", "strict-revision", False) + if strict and not rev_touched_url(geturl, revision): + #FIXME would be nice to have the revision number even when + # revision is None + raise Error, "the revision %s does not change anything "\ + "inside %s" % (revision or "HEAD", geturl) + svn.export(geturl, tmpdir, rev=revision) srpmsdir = os.path.join(tmpdir, "SRPMS") os.mkdir(srpmsdir) specsdir = os.path.join(tmpdir, "SPECS") @@ -79,6 +102,9 @@ def get_srpm(pkgdirurl, submit = not not revision specfile_svn2rpm(pkgdirurl, spec, revision, submit=submit, template=template, macros=macros, exported=tmpdir) + #FIXME revisioreal not needed if revision is None + #FIXME use geturl instead of pkgdirurl + revisionreal = svn.revision(pkgdirurl) for script in scripts: #FIXME revision can be "None" status, output = execcmd(script, tmpdir, spec, str(revision), @@ -93,13 +119,13 @@ def get_srpm(pkgdirurl, (topdir, builddir, rpmdir, sourcedir, specdir, srcrpmdir, patchdir, packager, spec, defs)) - if revision: + if revision and revisionreal: #FIXME duplicate glob line srpm = glob.glob(os.path.join(srpmsdir, "*.src.rpm"))[0] srpminfo = SRPM(srpm) release = srpminfo.release srpmbase = os.path.basename(srpm) - os.rename(srpm, "%s/@%s:%s" % (srpmsdir, exportedrev.number, srpmbase)) + os.rename(srpm, "%s/@%s:%s" % (srpmsdir, revisionreal, srpmbase)) srpm = glob.glob(os.path.join(srpmsdir, "*.src.rpm"))[0] if not targetdirs: targetdirs = (".",) @@ -131,7 +157,7 @@ def patch_spec(pkgdirurl, patchfile, log=""): if status != 0: raise Error, "can't apply patch:\n%s\n" % output else: - svn.checkin(tmpdir, log=log) + svn.commit(tmpdir, log="") finally: if os.path.isdir(tmpdir): shutil.rmtree(tmpdir) @@ -149,7 +175,9 @@ def put_srpm(pkgdirurl, srpmfile, appendname=0, log=""): version = srpm.version versionurl = "/".join([pkgdirurl, "releases", version]) releaseurl = "/".join([versionurl, srpm.release]) - ret = svn.mkdir(pkgdirurl, "Created package directory", noerror=1) + #FIXME when pre-commit hook fails, there's no clear way to know + # what happened + ret = svn.mkdir(pkgdirurl, noerror=1, log="Created package directory") if ret: svn.checkout(pkgdirurl, tmpdir) svn.mkdir(os.path.join(tmpdir, "releases")) @@ -161,7 +189,7 @@ def put_srpm(pkgdirurl, srpmfile, appendname=0, log=""): version_exists = 1 currentdir = os.path.join(tmpdir, "current") else: - if svn.exists(releaseurl): + if svn.ls(releaseurl, noerror=1): raise Error, "release already exists" svn.checkout("/".join([pkgdirurl, "current"]), tmpdir) svn.mkdir(versionurl, noerror=1, @@ -217,7 +245,7 @@ def put_srpm(pkgdirurl, srpmfile, appendname=0, log=""): if os.path.isdir(unpackdir): shutil.rmtree(unpackdir) - svn.checkin(tmpdir, log=log) + svn.commit(tmpdir, log=log) finally: if os.path.isdir(tmpdir): shutil.rmtree(tmpdir) @@ -257,7 +285,7 @@ def create_package(pkgdirurl, log="", verbose=0): if verbose: print "done" print "Committing...", - svn.checkin(tmpdir, + svn.commit(tmpdir, log="Created package structure for '%s'." % basename) print "done" finally: @@ -280,7 +308,7 @@ def mark_release(pkgdirurl, version, release, revision): releasesurl = "/".join([pkgdirurl, "releases"]) versionurl = "/".join([releasesurl, version]) releaseurl = "/".join([versionurl, release]) - if svn.exists(releaseurl, noerror=1): + if svn.ls(releaseurl, noerror=1): raise Error, "release already exists" svn.mkdir(releasesurl, noerror=1, log="Created releases directory.") @@ -294,7 +322,7 @@ def mark_release(pkgdirurl, version, release, revision): log="Copying release %s-%s to pristine/ directory." % (version, release)) markreleaselog = create_markrelease_log(version, release, revision) - svn.copy(currenturl, releaseurl, revision=revision, + svn.copy(currenturl, releaseurl, rev=revision, log=markreleaselog) def check_changed(pkgdirurl, all=0, show=0, verbose=0): @@ -305,29 +333,30 @@ def check_changed(pkgdirurl, all=0, show=0, verbose=0): if verbose: print "Getting list of packages...", sys.stdout.flush() - packages = [x['name'] for x in svn.ls(baseurl)] + packages = [x[:-1] for x in svn.ls(baseurl)] if verbose: print "done" if not packages: raise Error, "couldn't get list of packages" else: - packages = [pkgdirurl] + baseurl, basename = os.path.split(pkgdirurl) + packages = [basename] clean = [] changed = [] nopristine = [] nocurrent = [] - for pkgdirurl in packages: - package = os.path.basename(pkgdirurl) + for package in packages: + pkgdirurl = os.path.join(baseurl, package) current = os.path.join(pkgdirurl, "current") pristine = os.path.join(pkgdirurl, "pristine") if verbose: print "Checking package %s..." % package, sys.stdout.flush() - if not svn.exists(current): + if not svn.ls(current, noerror=1): if verbose: print "NO CURRENT" nocurrent.append(package) - elif not svn.exists(pristine): + elif not svn.ls(pristine, noerror=1): if verbose: print "NO PRISTINE" nopristine.append(package) @@ -368,7 +397,7 @@ def checkout(pkgdirurl, path=None, revision=None, use_mirror=True): current = mirror.checkout_url(current) print "checking out from mirror", current svn = SVN() - svn.checkout(current, path, revision=SVN.makerev(revision), show=1) + svn.checkout(current, path, rev=revision, show=1) def _getpkgtopdir(basedir=None): if basedir is None: @@ -378,15 +407,15 @@ def _getpkgtopdir(basedir=None): if dirname == "SPECS" or dirname == "SOURCES": topdir = os.pardir else: - topdir = "." + topdir = "" return topdir -def sync(dryrun=False): +def sync(dryrun=False, download=False): svn = SVN() topdir = _getpkgtopdir() # run svn info because svn st does not complain when topdir is not an # working copy - svn.info(topdir) + svn.info(topdir or ".") specsdir = os.path.join(topdir, "SPECS/") sourcesdir = os.path.join(topdir, "SOURCES/") for path in (specsdir, sourcesdir): @@ -401,18 +430,34 @@ def sync(dryrun=False): spec = rpm.TransactionSet().parseSpec(specpath) except rpm.error, e: raise Error, "could not load spec file: %s" % e - sources = [os.path.basename(name) - for name, no, flags in spec.sources()] - sourcesst = dict((os.path.basename(st.path), st) - for st in svn.status(sourcesdir, get_all=False, ignore=True)) + sources = dict((os.path.basename(name), name) + for name, no, flags in spec.sources()) + sourcesst = dict((os.path.basename(path), (path, st)) + for st, path in svn.status(sourcesdir, noignore=True)) toadd = [] - for source in sources: + for source, url in sources.iteritems(): sourcepath = os.path.join(sourcesdir, source) - if sourcesst.get(source): + pst = sourcesst.get(source) + if pst: + if os.path.isfile(sourcepath): + toadd.append(sourcepath) + else: + sys.stderr.write("warning: %s not found, skipping\n" % sourcepath) + elif download and not os.path.isfile(sourcepath): + print "%s not found, downloading from %s" % (sourcepath, url) + fmt = config.get("global", "download-command", + "wget -c -O '$dest' $url") + context = {"dest": sourcepath, "url": url} + try: + cmd = string.Template(fmt).substitute(context) + except KeyError, e: + raise Error, "invalid variable %r in download-command "\ + "configuration option" % e + execcmd(cmd, show=True) if os.path.isfile(sourcepath): toadd.append(sourcepath) else: - sys.stderr.write("warning: %s not found\n" % sourcepath) + raise Error, "file not found: %s" % sourcepath # rm entries not found in sources and still in svn found = os.listdir(sourcesdir) toremove = [] @@ -426,31 +471,35 @@ def sync(dryrun=False): for path in toremove: print "D\t%s" % path if not dryrun: - svn.remove(path) + svn.remove(path, local=True) for path in toadd: print "A\t%s" % path if not dryrun: - svn.add(path) + svn.add(path, local=True) -def commit(target=".", message=None): +def commit(target=".", message=None, logfile=None): svn = SVN() - status = svn.status(target, silent=True) + status = svn.status(target, quiet=True) if not status: print "nothing to commit" return - info = svn.info(target) - url = info.url + info = svn.info2(target) + url = info.get("URL") if url is None: raise Error, "working copy URL not provided by svn info" mirrored = mirror.enabled(url) if mirrored: newurl = mirror.switchto_parent(svn, url, target) print "relocated to", newurl - # we can't use the svn object here because pexpect hides VISUAL - mopt = "" + # we can't use the svn object here because svn --non-interactive option + # hides VISUAL + opts = [] if message is not None: - mopt = "-m \"%s\"" % message - os.system("svn ci %s %s" % (mopt, target)) + opts.append("-m \"%s\"" % message) + if logfile is not None: + opts.append("-F \"%s\"" % logfile) + mopts = " ".join(opts) + os.system("svn ci %s %s" % (mopts, target)) if mirrored: print "use \"repsys switch\" in order to switch back to mirror "\ "later" @@ -458,8 +507,10 @@ def commit(target=".", message=None): def switch(mirrorurl=None): svn = SVN() topdir = _getpkgtopdir() - info = svn.info(topdir) - wcurl = info.url + info = svn.info2(topdir) + wcurl = info.get("URL") + if wcurl is None: + raise Error, "working copy URL not provided by svn info" newurl = mirror.autoswitch(svn, topdir, wcurl, mirrorurl) print "switched to", newurl @@ -483,14 +534,16 @@ def get_submit_info(path): if not os.path.isdir(os.path.join(path, ".svn")): raise Error, "subversion directory not found" - # Now, extract the package name. svn = SVN() - info = svn.info(path) - url = info.url + # Now, extract the package name. + info = svn.info2(path) + url = info.get("URL") + if url is None: + raise Error, "missing URL from svn info %s" % path toks = url.split("/") if len(toks) < 2 or toks[-1] != "current": - raise Error, "invalid package URL %s, needs current/" % url + raise Error, "unexpected URL received from 'svn info'" name = toks[-2] # Finally, guess revision. @@ -498,15 +551,17 @@ def get_submit_info(path): files = [] files.extend(glob.glob("%s/*" % specsdir)) files.extend(glob.glob("%s/*" % sourcesdir)) - for file in files: - info = svn.info(file) - if info is None: # not in working copy + for file in files: + info = svn.info2(file) + if info is None: continue - rev = info.commit_revision.number - if rev > max: - max = rev + rawrev = info.get("Last Changed Rev") + if rawrev: + rev = int(rawrev) + if rev > max: + max = rev if max == -1: - raise Error, "unable to find the latest revision" + raise Error, "revision tag not found in 'svn info' output" return name, max diff --git a/RepSys/svn.py b/RepSys/svn.py index 527b1e5..985329d 100644 --- a/RepSys/svn.py +++ b/RepSys/svn.py @@ -1,18 +1,10 @@ from RepSys import Error, config from RepSys.util import execcmd, get_auth - import sys -import os import re import time -import threading -import tempfile -import pysvn - -__all__ = ["SVN", "Revision", "SVNLogEntry", "SVNError"] -class SVNError(Error): - pass +__all__ = ["SVN", "SVNLook", "SVNLogEntry"] class SVNLogEntry: def __init__(self, revision, author, date): @@ -26,208 +18,347 @@ class SVNLogEntry: return cmp(self.date, other.date) class SVN: - _client = None - _client_lock = None - _current_message = None - - def __init__(self): - self._client = pysvn.Client() - self._client_lock = threading.Lock() - self._client.callback_get_log_message = self._log_handler - self._client.callback_get_login = self._unsupported_auth - self._client.callback_ssl_client_cert_password_prompt = \ - self._unsupported_auth - self._client.callback_ssl_client_cert_prompt = \ - self._unsupported_auth - self._client.callback_ssl_server_prompt = \ - self._unsupported_auth - - def _log_handler(self): - if self._current_message is None: - #TODO make it use EDITOR - raise ValueError, "No log message defined" - return True, self._current_message - - def _unsupported_auth(self, *args, **kwargs): - raise SVNError, "svn is trying to get login information, " \ - "seems that you're not using ssh-agent" - - def _get_log_message(self, received_kwargs): - message = received_kwargs.pop("log", None) - messagefile = received_kwargs.pop("logfile", None) - if messagefile and not message: - message = open(messagefile).read() - return message - - def _set_notify_callback(self, callback): - self._client.callback_notify = callback - - def _make_wrapper(self, meth, notify=None): - def wrapper(*args, **kwargs): - self._client_lock.acquire() - try: - self._current_message = self._get_log_message(kwargs) - ignore_errors = kwargs.pop("noerror", None) - if notify: - self._client.callback_notify = notify - try: - return meth(*args, **kwargs) - except pysvn.ClientError, (msg,): - if not ignore_errors: - raise SVNError, msg - return None - finally: - self._client_lock.release() - self._current_message = None - return wrapper - - def _client_wrap(self, attrname): - meth = getattr(self._client, attrname) - wrapper = self._make_wrapper(meth) - return wrapper - - def __getattr__(self, attrname): - return self._client_wrap(attrname) - - def makerev(number=None, head=None): - if number is not None: - args = (pysvn.opt_revision_kind.number, number) - else: - args = (pysvn.opt_revision_kind.head,) - return pysvn.Revision(*args) - makerev = staticmethod(makerev) - - def revision(self, url, last_changed=False): - infos = self._client.info2(url, recurse=False) - if last_changed: - revnum = infos[0][1].last_changed_rev.number - else: - revnum = infos[0][1].rev.number - return revnum - - # this override method fixed the problem in pysvn's mkdir which - # requires a log_message parameter - def mkdir(self, path, log=None, **kwargs): - meth = self._client_wrap("mkdir") - # we can't raise an error because pysvn's mkdir will use - # log_message only if path is remote, but it *always* requires this - # parameter. Also, 'log' is never used. - log = log or "There's a silent bug in your code" - return meth(path, log, log=None, **kwargs) - - def checkout(self, url, targetpath, show=False, **kwargs): - if show: - def callback(event): - types = pysvn.wc_notify_action - action = event["action"] - if action == types.update_add: - print "A %s" % event["path"] - elif action == types.update_completed: - print "Checked out revision %d" % \ - event["revision"].number - self._set_notify_callback(callback) - meth = self._client_wrap("checkout") - meth(url, targetpath, **kwargs) - - def checkin(self, path, log, **kwargs): - # XXX use EDITOR when log empty - meth = self._client_wrap("checkin") - return meth(path, log, log=None, **kwargs) - - def log(self, *args, **kwargs): - meth = self._client_wrap("log") - entries = meth(discover_changed_paths=True, *args, **kwargs) - if entries is None: - return - for entrydic in entries: - entry = SVNLogEntry(entrydic["revision"].number, - entrydic["author"], - time.localtime(entrydic["date"])) - entry.lines[:] = entrydic["message"].split("\n") - for cp in entrydic["changed_paths"]: - from_rev = cp["copyfrom_revision"] - if from_rev: - from_rev = from_rev.number - changed = { - "action": cp["action"], - "path": cp["path"], - "from_rev": from_rev, - "from_path": cp["copyfrom_path"], - } - entry.changed.append(changed) - yield entry - - def exists(self, path): - return self.ls(path, noerror=1) is not None - - def status(self, *args, **kwargs): - # add one keywork "silent" that workaround the strange behavior of - # pysvn's get_all, which seems to be broken, this way we also have - # the same interface of svn.py from repsys 1.6.x - meth = self._client_wrap("status") - silent = kwargs.pop("silent", None) - st = meth(*args, **kwargs) - if silent: - unversioned = pysvn.wc_status_kind.unversioned - st = [entry for entry in st - if entry.text_status is not unversioned] - return st - - def diff(self, path1, *args, **kwargs): - head = pysvn.Revision(pysvn.opt_revision_kind.head) - revision1 = kwargs.pop("revision1", head) - revision2 = kwargs.pop("revision2", head) - if args: - kwargs["url_or_path2"] = args[0] - tmpdir = tempfile.gettempdir() - meth = self._client_wrap("diff") - diff_text = meth(tmpdir, path1, revision1=revision1, - revision2=revision2, **kwargs) - return diff_text - - def _edit_message(self, message): - # argh! - editor = os.getenv("EDITOR", "vim") - fd, fpath = tempfile.mkstemp(prefix="repsys") - result = (False, None) + def _execsvn(self, *args, **kwargs): + localcmds = ("add", "revert", "cleanup") + if not kwargs.get("show") and args[0] not in localcmds: + args = list(args) + args.append("--non-interactive") + svn_command = config.get("global", "svn-command", + "SVN_SSH='ssh -o \"BatchMode yes\"' svn") + cmdstr = svn_command + " " + " ".join(args) try: - f = os.fdopen(fd, "w") - f.write(message) - f.close() - lastchange = os.stat(fpath).st_mtime - for i in xrange(10): - status = os.system("%s %s" % (editor, fpath)) - if status != 0: - raise SVNError, "the editor failed with %d" % status - newchange = os.stat(fpath).st_mtime - if newchange == lastchange: - print "Log message unchanged or not specified" - print "(a)bort, (c)ontinue, (e)dit" - choice = raw_input() - if not choice or choice[0] == 'e': - continue - elif choice[0] == 'a': - break - elif choice[0] == 'c': - pass # ignore and go ahead - newmessage = open(fpath).read() - result = (True, newmessage) - break - finally: - os.unlink(fpath) - return result - - def propedit(self, propname, pkgdirurl, revision, revprop=False): - revision = (revision) - if revprop: - propget = self.revpropget - propset = self.revpropset + return execcmd(cmdstr, **kwargs) + except Error, e: + if "Permission denied" in e.message: + raise Error, ("%s\n" + "Seems ssh-agent or ForwardAgent are not setup, see " + "http://wiki.mandriva.com/en/Development/Docs/Contributor_Tricks#SSH_configuration" + " for more information." % e) + elif "authorization failed" in e.message: + raise Error, ("%s\n" + "Note that repsys does not support any HTTP " + "authenticated access." % e) + raise + + 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 + received_kwargs.has_key("log") or + received_kwargs.has_key("logfile")): + 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 received_kwargs.has_key("rev"): + ret = received_kwargs.get("rev") + if isinstance(ret, basestring): + try: + ret = int(ret) + except ValueError: + raise Error, "invalid revision provided" + if ret: + cmd_args.append("-r %d" % ret) + + def add(self, path, **kwargs): + cmd = ["add", path] + return self._execsvn_success(noauth=1, *cmd, **kwargs) + + def copy(self, pathfrom, pathto, **kwargs): + cmd = ["copy", pathfrom, 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] + self._add_log(cmd, kwargs) + if force: + cmd.append("--force") + return self._execsvn_success(*cmd, **kwargs) + + def mkdir(self, path, **kwargs): + cmd = ["mkdir", path] + self._add_log(cmd, kwargs) + return self._execsvn_success(*cmd, **kwargs) + + def commit(self, path, **kwargs): + cmd = ["commit", path] + self._add_log(cmd, kwargs) + return self._execsvn_success(*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 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] + 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] + status, output = self._execsvn(local=True, *cmd, **kwargs) + if status == 0 and "Not a versioned resource" 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 = dict(pairs) + return info + + def ls(self, path, **kwargs): + cmd = ["ls", path] + status, output = self._execsvn(*cmd, **kwargs) + if status == 0: + return output.split() + return None + + def status(self, path, **kwargs): + cmd = ["status", 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.split() for x in output.splitlines()] + return None + + def cleanup(self, path, **kwargs): + cmd = ["cleanup", path] + return self._execsvn_success(*cmd, **kwargs) + + def revert(self, path, **kwargs): + cmd = ["revert", 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] + 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: - propget = self.propget - propset = self.propset - revision, message = propget(propname, pkgdirurl, revision=revision) - changed, newmessage = self._edit_message(message) - if changed: - propset(propname, newmessage, pkgdirurl, revision=revision) + 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 + +class SVNLook: + 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 kwargs.has_key(key): + execcmd_kwargs[key] = kwargs[key] + return execcmd(*execcmd_args, **execcmd_kwargs) + + def _add_txnrev(self, cmd_args, received_kwargs): + if received_kwargs.has_key("txn"): + 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 received_kwargs.has_key("rev"): + 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() # vim:et:ts=4:sw=4 diff --git a/RepSys/util.py b/RepSys/util.py index 8c66199..83f2ebe 100644 --- a/RepSys/util.py +++ b/RepSys/util.py @@ -29,7 +29,8 @@ def execcmd(*cmd, **kwargs): status = os.system(cmdstr) output = "" else: - status, output = commands_getstatusoutput("LANG=C LANGUAGE=C LC_ALL=C "+cmdstr) + status, output = commands_getstatusoutput( + "LANG=C LANGUAGE=C LC_ALL=C "+cmdstr) if status != 0 and not kwargs.get("noerror"): raise Error, "command failed: %s\n%s\n" % (cmdstr, output) if config.getbool("global", "verbose", 0): diff --git a/compatv15.chlog b/compatv15.chlog new file mode 100644 index 0000000..3373a19 --- /dev/null +++ b/compatv15.chlog @@ -0,0 +1,13 @@ +## Sample Changelog template +## +#import time +#for $author in $revisions_by_author +* $author.revisions[0].date $author.name <$author.email> ++ ${time.strftime("%Y-%m-%d %H:%M:%S", author.revisions[0].raw_date)} ($author.revisions[0].revision) + #for $rev in $author.revisions + #for $line in $rev.lines +$line + #end for + #end for + +#end for diff --git a/create-srpm b/create-srpm index 3dab068..0c51e97 100755 --- a/create-srpm +++ b/create-srpm @@ -89,8 +89,11 @@ def parse_options(): dest="urlmap", default=False, help="disable url mapping") parser.add_option("--define", action="append") + parser.add_option("--list", dest="list_targets", default=False, + action="store_true", + help="list submit targets available") opts, args = parser.parse_args() - if not args: + if not opts.list_targets and not args: parser.error("you must supply a package url") return opts, args @@ -100,7 +103,12 @@ def main(): iface = CmdIface() opts, args = parse_options() try: - iface.submit_package(args[0], opts.revision, opts.target, opts.urlmap, opts.define) + if opts.list_targets: + for target in iface.submit_targets(): + print target + else: + iface.submit_package(args[0], opts.revision, opts.target, + opts.urlmap, opts.define) except Error, e: sys.stderr.write("error: %s\n" % str(e)) sys.exit(1) diff --git a/default.chlog b/default.chlog index aff3958..93dcd1b 100644 --- a/default.chlog +++ b/default.chlog @@ -1,5 +1,11 @@ ## Sample Changelog 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 @@ -3,33 +3,31 @@ from RepSys import Error, plugins from RepSys.command import * import getopt import sys -import codecs -import locale - -VERSION="1.7.r%s" % ("$Rev$".split()[-2].strip()) +VERSION="1.6.20" HELP = """\ Usage: repsys COMMAND [COMMAND ARGUMENTS] +Tool to access and manage a package repository structure. + +http://wiki.mandriva.com/en/Development/Packaging/RepositorySystem/Quickstart + Useful commands: - co - sync - ci - submit - create - getspec - getsrpm - rpmlog - changed - authoremail - putsrpm - switch + co checkout a package + ci commit changes + sync add-remove all file changes from the .spec + submit submit a package for build + getspec prints the spec + rpmlog prints the RPM changelog + getsrpm creates the source RPM + create create the structure of a new package + changed shows changes not submitted + authoremail prints the e-mail of a given author + switch relocate to mirror or upstream repository Run "repsys COMMAND --help" for more information. -Run "repsys --help-plugins" for help on loaded plugins. - Written by Gustavo Niemeyer <gustavo@niemeyer.net> """ @@ -68,11 +66,10 @@ def dispatch_command(command, argv, debug=0): commands_module = getattr(repsys_module, "commands") command_module = getattr(commands_module, command) except (ImportError, AttributeError): - if debug: - import traceback - traceback.print_exc() - sys.exit(1) - raise Error, "invalid command '%s'" % command + etype, exc, tb = sys.exc_info() + if tb.tb_next is None and not debug: + raise Error, "invalid command '%s'" % command + raise command_module.main() if __name__ == "__main__": @@ -81,9 +78,6 @@ if __name__ == "__main__": except Error, e: sys.stderr.write("plugin initialization error: %s\n" % e) sys.exit(1) - encoding = locale.getpreferredencoding() - sys.stdout = codecs.getwriter(encoding)(sys.stdout, errors="replace") - sys.stderr = codecs.getwriter(encoding)(sys.stderr, errors="replace") do_command(parse_options, dispatch_command) # vim:et:ts=4:sw=4 diff --git a/repsys-example.conf b/repsys-example.conf new file mode 100644 index 0000000..99c44f5 --- /dev/null +++ b/repsys-example.conf @@ -0,0 +1,65 @@ +[global] +verbose = no +default_parent = svn+ssh://svn.mandriva.com/svn/packages/cooker +url-map = svn\+ssh://svn\.mandriva\.com/(.*) file:///\1 +#tempdir = /tmp +## the command used to download files when using repsys sync -d +#download-command = wget -c -O '$dest' $url + + +[log] +oldurl = svn+ssh://svn.mandriva.com/svn/packages/misc +# controls up to which revision the rpm changelog +# will be constructed (default zero, i.e., oldest +# commit) +revision-offset = 0 +# commit lines containing this string won't be shown in the changelog: +ignore-string = SILENT +# and in case of only allowing a few lines to be visible, use this: +#unignore-string = CLOG + +[template] +# set the cheetah template used to generate the spec changelog from svn +path = /usr/share/repsys/default.chlog + +[helper] +# create-srpm is called by repsys on the server-side when submitting a +# package +create-srpm = /usr/share/repsys/create-srpm +# upload-srpm is called by create-srpm to copy the generated .src.rpm to +# the proper build queue +upload-srpm = /usr/local/bin/youri.devel + +# this section maps usernames found in svn commits to the ones that must be +# shown in the changelog +# users can be retrieved from LDAP through the use of the repsys-ldap +# plugin +[users] +# jsmith = John Smith <jsmith26@example.com> + +[submit] +host = kenobi.mandriva.com +default = Cooker + +[submit Cooker] +target = /export/home/repsys +allowed = svn+ssh://svn.mandriva.com/svn/packages/cooker +scripts = /usr/share/repsys/rebrand-mdk +## +## rpm-macros refers to the sections containing the macros used for this +## target. The values will be used to build the rpmbuild command line. For +## example: +## +## [macros cooker] +## a = b +## c = %a +## +## will render in the command line: --define "a b" --define "c %a". +## +#rpm-macros = global cooker + +#[macros global] +#distsuffix = mdv + +#[macros cooker] +#mandriva_release = 2007.1 diff --git a/repsys.conf b/repsys.conf index 2201ac1..0d1ddc5 100644 --- a/repsys.conf +++ b/repsys.conf @@ -1,56 +1,15 @@ [global] -verbose = no default_parent = svn+ssh://svn.mandriva.com/svn/packages/cooker -url-map = svn\+ssh://svn\.mandriva\.com/(.*) file:///\1 +## uncomment it in case you don't have a account in the Mandriva cluster: #mirror = http://svn.mandriva.com/svn/packages/cooker/ -#tempdir = /tmp [log] oldurl = svn+ssh://svn.mandriva.com/svn/packages/misc -# controls up to which revision the rpm changelog -# will be constructed (default zero, i.e., oldest -# commit) -revision-offset = 0 -# commit lines containing this string won't be shown in the changelog: -ignore-string = SILENT -# and in case of only allowing a few lines to be visible, use this: -#unignore-string = CLOG -# see repsys.macros: -#macros-file = /etc/repsys.macros - -[template] -path = /usr/share/repsys/default.chlog [helper] create-srpm = /usr/share/repsys/create-srpm upload-srpm = /usr/local/bin/youri.devel -[users] -# jsmith = John Smith <jsmith26@example.com> - [submit] host = kenobi.mandriva.com default = Cooker - -[submit Cooker] -target = /export/home/repsys -allowed = svn+ssh://svn.mandriva.com/svn/packages/cooker -scripts = /usr/share/repsys/rebrand-mdk -## -## rpm-macros refers to the sections containing the macros used for this -## target. The values will be used to build the rpmbuild command line. For -## example: -## -## [macros cooker] -## a = b -## c = %a -## -## will render in the command line: --define "a b" --define "c %a". -## -#rpm-macros = global cooker - -#[macros global] -#distsuffix = mdv - -#[macros cooker] -#mandriva_release = 2007.1 diff --git a/repsys.spec b/repsys.spec index bcfda51..edb0cf8 100644 --- a/repsys.spec +++ b/repsys.spec @@ -1,9 +1,9 @@ Name: repsys -Version: 1.6.2a -Release: 1mdk +Version: 1.6.15 +Release: %mkrel 1 Summary: Tools for Mandriva Linux repository access and management Group: Development/Other -Source: %{name}-%{repsys_version}.tar.bz2 +Source: %{name}-%{version}.tar.bz2 License: GPL URL: http://qa.mandriva.com/twiki/bin/view/Main/RepositorySystem Prefix: %{_prefix} @@ -12,14 +12,13 @@ Buildrequires: python-devel BuildRoot: %{_tmppath}/%{name}-%{version}-root BuildRequires: python BuildRequires: python-devel -Requires: python-cheetah -Requires: pysvn +Requires: python-cheetah python-rpm %description Tools for Mandriva Linux repository access and management. %prep -%setup -q -n %{name}-%{repsys_version} +%setup -q %build python setup.py build @@ -45,10 +44,21 @@ rm -rf %{buildroot} %{_bindir}/getsrpm-mdk %{_datadir}/repsys/rebrand-mdk %{_datadir}/repsys/create-srpm +%{_datadir}/repsys/compatv15.chlog +%{_datadir}/repsys/default.chlog +%{_datadir}/repsys/revno.chlog +%{_datadir}/repsys/oldfashion.chlog +%{py_sitedir}/RepSys # MAKE THE CHANGES IN CVS: NO PATCH OR SOURCE ALLOWED %changelog +* Wed Oct 18 2006 Olivier Blin <blino@mandriva.com> 1.6.6-1mdv2007.1 +- use a different "submit <target>" section per target in configuration file + +* Tue Oct 17 2006 Andreas Hasenack <andreas@mandriva.com> 1.6.5-1mdk +- 1.6.5 + * Thu Feb 02 2006 Andreas Hasenack <andreas@mandriva.com> 1.6.0-1mdk - version 1.6.0, see CVS changelog @@ -18,13 +18,15 @@ setup(name="repsys", url = "http://qa.mandriva.com/twiki/bin/view/Main/RepositorySystem", license = "GPL", long_description = """Tools for Mandriva Linux repository access and management.""", - packages = ["RepSys", "RepSys.cgi", "RepSys.commands"], + packages = ["RepSys", "RepSys.cgi", "RepSys.commands", + "RepSys.plugins"], scripts = ["repsys", "getsrpm-mdk"], data_files = [ ("/usr/share/repsys/", ["default.chlog", "revno.chlog", "oldfashion.chlog", + "compatv15.chlog", "create-srpm", "rebrand-mdk"]), ("/etc/", ["repsys.conf"])] |