remove svn keyword 'Revision' that is dead anyway
[build.git] / module-tools.py
index 3def09f..4265a94 100755 (executable)
@@ -122,6 +122,113 @@ class Command:
             print 'Done',
         return result
 
+
+
+class SvnRepository:
+    type = "svn"
+
+    def __init__(self, path, options):
+        self.path = path
+        self.options = options
+
+    @classmethod
+    def checkout(cls, remote, path, options):
+        Command("rm -rf %s" % path, options).run_silent()
+        Command("svn co %s %s" % (remote, path), options).run_fatal()
+        return SvnRepository(path, options)
+
+    @classmethod
+    def remote_exists(cls, remote):
+        return os.system("svn list %s &> /dev/null" % remote) == 0
+
+    def update(self):
+        Command("svn up %s" % self.path, self.options).run_fatal()
+
+    def commit(self, logfile):
+        # add all new files to the repository
+        Command("svn status %s | grep '^\?' | sed -e 's/? *//' | sed -e 's/ /\\ /g' | xargs svn add" %
+                self.path, self.options).run_silent()
+        Command("svn commit -F %s %s" % (logfile, self.path), self.options).run_fatal()
+
+    def revert(self):
+        Command("svn revert %s -R" % self.path, self.options).run_fatal()
+
+    def is_clean(self):
+        command="svn status %s" % self.path
+        return len(Command(command,self.options).output_of(True)) == 0
+
+    def is_valid(self):
+        return os.path.exists(os.path.join(self.path, ".svn"))
+    
+
+
+class GitRepository:
+    type = "git"
+
+    def __init__(self, path, options):
+        self.path = path
+        self.options = options
+
+    @classmethod
+    def checkout(cls, remote, path, options, depth=1):
+        Command("rm -rf %s" % path, options).run_silent()
+        Command("git clone --depth %d %s %s" % (depth, remote, path), options).run_fatal()
+        return GitRepository(path, options)
+
+    @classmethod
+    def remote_exists(cls, remote):
+        return os.system("git --no-pager ls-remote %s &> /dev/null" % remote) == 0
+
+    def __run_in_repo(self, fun, *args):
+        cwd = os.getcwd()
+        os.chdir(self.path)
+        ret = fun(*args)
+        os.chdir(cwd)
+        return ret
+
+    def __run_command_in_repo(self, command):
+        c = Command(command, self.options)
+        return self.__run_in_repo(c.run_fatal)
+
+    def update(self):
+        return self.__run_command_in_repo("git pull")
+
+    def commit(self, logfile):
+        self.__run_command_in_repo("git add -A")
+        self.__run_command_in_repo("git commit -F  %s" % logfile)
+        self.__run_command_in_repo("git push")
+
+    def revert(self):
+        self.__run_command_in_repo("git --no-pager reset --hard")
+        self.__run_command_in_repo("git --no-pager clean -f")
+
+    def is_clean(self):
+        def check_commit():
+            command="git status"
+            s="nothing to commit (working directory clean)"
+            return Command(command, self.options).output_of(True).find(s) >= 0
+        return self.__run_in_repo(check_commit)
+
+    def is_valid(self):
+        return os.path.exists(os.path.join(self.path, ".git"))
+    
+
+class Repository:
+    """ Generic repository """
+    supported_repo_types = [SvnRepository, GitRepository]
+
+    def __init__(self, path, options):
+        self.path = path
+        self.options = options
+        for repo in self.supported_repo_types:
+            self.repo = repo(self.path, self.options)
+            if self.repo.is_valid():
+                break
+
+    def __getattr__(self, attr):
+        return getattr(self.repo, attr)
+
+
 class Svnpath:
     def __init__(self,path,options):
         self.path=path
@@ -269,7 +376,7 @@ class Module:
     @staticmethod
     def html_dump_header(title):
         nowdate=time.strftime("%Y-%m-%d")
-        nowtime=time.strftime("%H:%M")
+        nowtime=time.strftime("%H:%M (%Z)")
         print """
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
@@ -986,7 +1093,7 @@ will be based on latest tag %s and *not* on the current trunk"""%(self.name,bran
         # create commit log file
         tmp="/tmp/branching-%d"%os.getpid()
         f=open(tmp,"w")
-        f.write("Branch %s for module %s created (as new trunk) from tag %s\n"%(new_trunk_name,self.name,latest_tag_name))
+        f.write("Branch %s for module %s created (as new trunk) from tag %s\n"%(branch_name,self.name,latest_tag_name))
         f.close()
 
         # review the renumbering changes in trunk
@@ -1003,15 +1110,15 @@ will be based on latest tag %s and *not* on the current trunk"""%(self.name,bran
         command="svn copy --file %s %s %s"%(tmp,tag_url,new_tag_url)
         self.run_prompt("Create initial tag in trunk",command)
         os.unlink(tmp)
-        # print message about SVNBRANCH
+        # print message about BRANCH
         print """You might now wish to review your tags files
 Please make sure you mention as appropriate 
-%s-SVNBRANCH := %s""" %(self.name,branch_name)
+%s-BRANCH := %s""" %(self.name,branch_name)
 
 ##############################
 class Package:
 
-    def __init__(self, package, module, svnpath, spec):
+    def __init__(self, package, module, svnpath, spec,options):
         self.package=package
         self.module=module
         self.svnrev = None
@@ -1023,6 +1130,7 @@ class Package:
         if self.svnrev:
             self.specpath += "@%s" % self.svnrev
         self.basename=os.path.basename(svnpath)
+        self.options=options
 
     # returns a http URL to the trac path where full diff can be viewed (between self and pkg)
     # typically http://svn.planet-lab.org/changeset?old_path=Monitor%2Ftags%2FMonitor-1.0-7&new_path=Monitor%2Ftags%2FMonitor-1.0-13
@@ -1038,6 +1146,12 @@ class Package:
             return "%s://%s/changeset?old_path=%s&new_path=%s"%(method,hostname,self_path,pkg_path)
         else:
             return None
+    
+    def inline_full_diff (self, pkg):
+        print '{{{'
+        command='svn diff %s %s'%(self.svnpath,pkg.svnpath)
+        Command(command,self.options).run()
+        print '}}}'
 
     def details (self):
         return "[%s %s] [%s (spec)]"%(self.svnpath,self.basename,self.specpath)
@@ -1080,6 +1194,9 @@ class Build (Module):
         make_options="--no-print-directory -C %s stage1=true PLDISTRO=%s PLDISTROTAGS=%s 2> /dev/null"%(self.edge_dir(),distro,distrotag)
         command="make %s packages"%make_options
         make_packages=Command(command,self.options).output_of()
+        if self.options.verbose:
+            print 'obtaining packages information with command:'
+            print command
         pkg_line=re.compile("\Apackage=(?P<package>[^\s]+)\s+ref_module=(?P<module>[^\s]+)\s.*\Z")
         for line in make_packages.split("\n"):
             if not line:
@@ -1098,7 +1215,7 @@ class Build (Module):
                 svnpath=Command(command,self.options).output_of().strip()
                 command="make %s +%s-SPEC"%(make_options,package)
                 spec=Command(command,self.options).output_of().strip()
-                result[package]=Package(package,module,svnpath,spec)
+                result[module]=Package(package,module,svnpath,spec,self.options)
         return result
 
     def get_distrotags (self):
@@ -1164,23 +1281,32 @@ class Release:
             # parse make packages
             packages_new=build_new.get_packages(distrotag)
             pnames_new=set(packages_new.keys())
-            if options.verbose: print 'got packages for ',build_new.display
             packages_old=build_old.get_packages(distrotag)
             pnames_old=set(packages_old.keys())
-            if options.verbose: print 'got packages for ',build_old.display
 
-            # get created, deprecated, and preserved package names
+            # get names of created, deprecated, and preserved modules
             pnames_created = list(pnames_new-pnames_old)
-            pnames_created.sort()
             pnames_deprecated = list(pnames_old-pnames_new)
-            pnames_deprecated.sort()
             pnames = list(pnames_new.intersection(pnames_old))
+
+            pnames_created.sort()
+            pnames_deprecated.sort()
             pnames.sort()
 
             if options.verbose: 
-                print "Found new pnames",pnames_new
-                print "Found deprecated pnames",pnames_deprecated
-                print "Found preserved pnames",pnames
+                print '--------------------'
+                print 'got packages for ',build_new.display
+                print pnames_new
+                print '--------------------'
+                print 'got packages for ',build_old.display
+                print pnames_old
+                print '--------------------'
+                print "Found new modules",pnames_created
+                print '--------------------'
+                print "Found deprecated modules",pnames_deprecated
+                print '--------------------'
+                print "Found preserved modules",pnames
+                print '--------------------'
 
             # display created and deprecated 
             for name in pnames_created:
@@ -1215,26 +1341,30 @@ class Release:
                 print '=== %s - %s to %s : package %s === #package-%s-%s-%s'%(
                     distrotag,build_old.display,build_new.display,name,name,distro,build_new.display)
                 print ' * from %s to %s'%(pobj_old.details(),pobj_new.details())
-                trac_diff_url=pobj_old.trac_full_diff(pobj_new)
-                if trac_diff_url:
-                    print ' * [%s View full diff]'%trac_diff_url
-                print '{{{'
-                for line in specdiff.split('\n'):
-                    if not line:
-                        continue
-                    if Release.discard_matcher.match(line):
-                        continue
-                    if line[0] in ['@']:
-                        print '----------'
-                    elif line[0] in ['+','-']:
-                        print_fold(line)
-                print '}}}'
+                if options.inline_diff:
+                    pobj_old.inline_full_diff(pobj_new)
+                else:
+                    trac_diff_url=pobj_old.trac_full_diff(pobj_new)
+                    if trac_diff_url:
+                        print ' * [%s View full diff]'%trac_diff_url
+                    else:
+                        print ' * No full diff available'
+                    print '{{{'
+                    for line in specdiff.split('\n'):
+                        if not line:
+                            continue
+                        if Release.discard_matcher.match(line):
+                            continue
+                        if line[0] in ['@']:
+                            print '----------'
+                        elif line[0] in ['+','-']:
+                            print_fold(line)
+                    print '}}}'
 
 ##############################
 class Main:
 
     module_usage="""Usage: %prog [options] module_desc [ .. module_desc ]
-Revision: $Revision$
 
 module-tools : a set of tools to manage subversion tags and specfile
   requires the specfile to either
@@ -1294,7 +1424,7 @@ Branches:
                 break
         if not mode:
             print "Unsupported command",sys.argv[0]
-            print "Supported commands:" + Modes.modes.keys.join(" ")
+            print "Supported commands:" + " ".join(Main.modes.keys())
             sys.exit(1)
 
         if mode not in Main.release_modes:
@@ -1350,6 +1480,8 @@ Branches:
         else:
             parser.add_option("-n","--dry-run",action="store_true",dest="dry_run",default=False,
                               help="dry run - shell commands are only displayed")
+            parser.add_option("-i","--inline-diff",action="store_true",dest="inline_diff",default=False,
+                              help="calls svn diff on whole module, not just only the spec file")
             parser.add_option("-t","--distrotags",action="callback",callback=Main.optparse_list, dest="distrotags",
                               default=[], nargs=1,type="string",
                               help="""specify distro-tags files, e.g. onelab-tags-4.2.mk