Implement repa diff
authorEd Bartosh <eduard.bartosh@intel.com>
Sun, 11 May 2014 20:33:16 +0000 (23:33 +0300)
committerEd Bartosh <eduard.bartosh@intel.com>
Mon, 19 May 2014 08:14:46 +0000 (11:14 +0300)
repa diff shows the difference between manifest of target project and
another (usually base) project. It also shows latest accepted submission
in another project and its status in target project.

repa diff should help RE to mantain the difference between base
and target projects.

Change-Id: I6ef93a5eb2c5a60b3778a721caa8be47da9f5882
Signed-off-by: Ed Bartosh <eduard.bartosh@intel.com>
repa.1
repa/diff.py [new file with mode: 0644]
setup.py

diff --git a/repa.1 b/repa.1
index d438e12317ca4a36cbf01e97e3d1f66faf6d4377..f75a8b17c81c936895bc4ae73429c824ae519d28 100644 (file)
--- a/repa.1
+++ b/repa.1
@@ -43,6 +43,9 @@ Submission group is a temporary group of submissions, created for testing purpos
 .RS 2
 6. \fBrmgroup\fR - remove submission group
 .RE
+.RS 2
+7. \fBdiff\fR - show the difference between projects
+.RE
 
 .\" ===========================================================================
 .\" Global options
@@ -295,6 +298,54 @@ Remove submit group.
 Print short help text about the "rmgroup" command and exit.
 .RE
 
+.PP
+.\"
+.\" The "diff" command description
+.\"
+.SS \fBdiff\fR [\-\-help] <project1> <manifest of target project> <manifest1>
+
+.RS 2
+Show the difference between manifest of target project and manifest of <project1> in the following format:
+
+<Git path>  <Revision in project1> <Revision in the target OBS Project>  <Tag, accepted into project1>  <status>
+.RS 0
+
+Example of output:
+
+$ repa -p Tizen:IVI diff Tizen:Common ivi_0140508.2_ia32.xml common_20140507.5_ia32.xml
+
+platform/core/appfw/aul-1                          3644ad5459 e4f2b22012 submit/tizen/20140502.084937 pending
+.RS 0
+platform/core/appfw/shortcut                       502c7807b3 27bbf892e0 submit/tizen/20140430.020549 pending
+.RS 0
+platform/core/connectivity/nfc-manager-neard       ea952dc454 88793ab1ff
+.RS 0
+platform/core/connectivity/smartcard-service       e45ae0bdfb 7d5da70551 submit/tizen/20140506.101200 pending
+.RS 0
+platform/core/messaging/msg-service                c988cc8b55 a9eed6a734 submit/tizen/20140502.105814 pending
+.RS 0
+platform/core/system/tlm                           3ddbc6bc14 dc72779317 submit/tizen/20140430.130050
+.RS 0
+platform/framework/web/crosswalk                   42dcc1327b 4280e52757 submit/tizen/20140506.123703 pending
+.RS 0
+
+Status 'pending' means that submission is pending for the target project. In colorized mode pending submissions are colorized and 'pending' status is not shown.
+
+
+.RE
+
+.\"
+.\" The "diff" command's options
+.\"
+.RS 2
+\fBOPTIONS\fR
+.RS 2
+.B \-h, \-\-help
+.RS 2
+Print short help text about the "diff" command and exit.
+.RE
+
+
 .SH CONFIGURATION FILE
 
 .RS 2
diff --git a/repa/diff.py b/repa/diff.py
new file mode 100644 (file)
index 0000000..5863470
--- /dev/null
@@ -0,0 +1,122 @@
+#!/usr/bin/env python
+#
+# This file is part of REPA: Release Engineering Process Assistant.
+#
+# Copyright (C) 2014 Intel Corporation
+#
+# REPA is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# version 2 as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+"""
+Diff module.
+Show the difference between projects.
+"""
+
+import sys
+import os
+import json
+
+import xml.etree.ElementTree as ET
+
+from repa.common import RepaException, Colorizer, get_prerelease_projects
+from repa.obs import OBS
+from repa.main import sub_main
+
+
+def gen_data(manifest):
+    """Parse manifest. Yield git path and revision for every entry."""
+    try:
+        etree = ET.parse(manifest)
+    except ET.ParseError as err:
+        raise RepaException("Can't parse manifest %s: %s" % (manifest, err))
+
+    for project in etree.getroot().findall('project'):
+        yield project.get('path'), project.get('revision')
+
+
+def get_tag(obs, project, package, revision):
+    """Get tag from the latest accepted SR."""
+    for sreq in obs.get_srs(project, 'accepted', package):
+        descr = sreq[-1]
+        if descr and 'Commit: %s' % revision in descr:
+            return descr.split('Tag: ')[-1].split()[0]
+    return 'Not found'
+
+
+def get_pending(obs, target_proj):
+    """Get pair of git path: tag for pending prerelease projects."""
+    result = {}
+    for proj in get_prerelease_projects(obs, target_proj):
+        meta = json.loads(obs.get_description(proj))
+        for path in meta["projects"]:
+            result[path] = meta["git_tag"]
+    return result
+
+
+def diff(obs, target_project, project1, target_manifest, manifest1,
+         is_colorize=False):
+    """Show the difference between project1 and project2."""
+    colorizer = Colorizer(is_colorize)
+
+    target_data = dict(gen_data(target_manifest))
+    data1 = dict(gen_data(manifest1))
+
+    pending = get_pending(obs, target_project)
+
+    for path in sorted(data1):
+        # if submission is not pending in target project
+        # and revisions are the same - skip it
+        if path not in pending and \
+            (path in target_data and data1[path] == target_data[path]):
+            continue
+
+        package = os.path.basename(path)
+        tag1 = get_tag(obs, project1, package, data1[path])
+        target_tag = get_tag(obs, target_project, package,
+                             target_data.get(path))
+
+        if tag1 in pending.values():
+            if is_colorize:
+                tag1 = colorizer.green(tag1)
+            else:
+                tag1 += ' ready'
+        print "%-50s %s %s %-37s %-37s %s" % \
+                  (path, target_data.get(path, 'Not found ')[:10],
+                   data1[path][:10], target_tag, tag1,
+                   pending.get(path, 'Not found'))
+
+
+class Diff(object):
+    """Subcommand: diff projects."""
+
+    name = 'diff'
+    description = 'Show the difference between projects'
+    help = description
+
+    @staticmethod
+    def add_arguments(parser, _config):
+        """Adds arguments to the parser. Called from [sub_]main."""
+        parser.add_argument('project1', help='Compared OBS project')
+        parser.add_argument('manifest', help='Manifest of current project')
+        parser.add_argument('manifest1', help='Manifest of compared project')
+
+    @staticmethod
+    def run(argv):
+        """Command line entry point. Called from [sub_]main."""
+        obs = OBS(argv.apiurl, argv.apiuser, argv.apipasswd)
+        return diff(obs, argv.project, argv.project1,
+                    argv.manifest, argv.manifest1, argv.colorize)
+
+if __name__ == '__main__':
+    sys.exit(sub_main(sys.argv[1:], Diff()))
index 88719a00cc3c7996b51516dcb23a8f1320c8bb4b..88f10d87f0d1c9d66c4630e27ec1360d5a04468b 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -47,6 +47,7 @@ setup(name = "repa",
                               'accept = repa.accept:Accept',
                               'reject = repa.reject:Reject',
                               'rmgroup = repa.rmgroup:RmGroup',
-                              'info = repa.info:Info']
+                              'info = repa.info:Info',
+                              'diff = repa.diff:Diff']
       }
 )