David Caro has uploaded a new change for review.

Change subject: New way of running
......................................................................

New way of running

* Only one review per commit, done by the dispatcher
* Hooks must output the result on stdout and the results will be
  aggregated
* Added more python libraries
* New hook that updates the external tracker metadata

Change-Id: Icd602596a78f7353f70eb27a18d0cafa75869cfc
Signed-off-by: David Caro <dcaro...@redhat.com>
---
A .gitignore
A hooks/bz/change-abandoned.update_tracker
M hooks/bz/change-merged.bz.set_MODIFIED
A hooks/bz/change-merged.update_tracker
M hooks/bz/patchset-created.bz.1.bug_url
M hooks/bz/patchset-created.bz.2.add_tracker
M hooks/bz/patchset-created.bz.2.set_POST
M hooks/bz/patchset-created.bz.99.review_ok
A hooks/bz/patchset-created.update_tracker
M hooks/bz/patchset-created.warn_if_not_merged_to_previous_branch
A hooks/bz/update_tracker
M hooks/hook-dispatcher
A hooks/lib/__init__.py
A hooks/lib/bz.py
M hooks/lib/bz.sh
M hooks/lib/config.py
M hooks/lib/gerrit.py
A hooks/lib/tools.py
M hooks/lib/tools.sh
19 files changed, 408 insertions(+), 93 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/gerrit-admin refs/changes/96/24496/1

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..feceb11
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+*.pyc
+*.sw?
diff --git a/hooks/bz/change-abandoned.update_tracker 
b/hooks/bz/change-abandoned.update_tracker
new file mode 120000
index 0000000..7a06791
--- /dev/null
+++ b/hooks/bz/change-abandoned.update_tracker
@@ -0,0 +1 @@
+update_tracker
\ No newline at end of file
diff --git a/hooks/bz/change-merged.bz.set_MODIFIED 
b/hooks/bz/change-merged.bz.set_MODIFIED
index 731d8c7..ff9cabe 100755
--- a/hooks/bz/change-merged.bz.set_MODIFIED
+++ b/hooks/bz/change-merged.bz.set_MODIFIED
@@ -27,13 +27,13 @@
 message=''
 current_patch_id="${change_url//*\/}"
 bug_ids=($(bz.get_bug_id "$commit"))
-echo "Processing bugs ${bug_ids[@]}"
+tools.log "Processing bugs ${bug_ids[@]}"
 for bug_id in "${bug_ids[@]}"; do
     bz.login -b "$bug_id" "$bz_user" "$bz_password"
     ### Skip downstream bugs
     product="$(bz.get_product "$bug_id")"
     if ! [[ "$product" =~ ^oVirt$ ]]; then
-        echo "::bug $bug_id::Not an oVirt bug, belongs to $product"
+        tools.log "::bug $bug_id::Not an oVirt bug, belongs to $product"
         continue
     fi
     ## Check if all the external bugs are closed
@@ -41,21 +41,24 @@
     for patch_id in $(bz.get_external_bugs "$bug_id" "$TRACKER_NAME"); do
         [[ "$current_patch_id" == "$patch_id" ]] && continue
         if $(gerrit.is_open "$patch_id"); then
-            echo "::bug $bug_id::Related patch $patch_id is still open"
+            tools.log "::bug $bug_id::Related patch $patch_id is still open"
             all_merged="false"
         fi
     done
     if [[ "$all_merged" != "true" ]]; then
-        echo "::bug $bug_id::Skipping because not all related patches are 
closed."
+        tools.log "::bug $bug_id::Skipping because not all related patches " \
+            "are closed."
         continue
     fi
     ## Modify the bug status
     if res=$(bz.update_status "$bug_id" "MODIFIED" "$commit"); then
-        echo "::bug $bug_id::Status updated on bug #$bug_id for gerrit id" \
-             "#${change_url//*\/} to MODIFIED${res:+\n    $res}"
+        tools.log "::bug $bug_id::Status updated on bug #$bug_id for gerrit " \
+            "id #${change_url//*\/} to MODIFIED${res:+\n    $res}"
+        tools.review "" "" "* #$bug_id::Set MODIFIED: OK"
     else
-        echo "::bug $bug_id::Failed to update the status of bug #$bug_id for"\
-             "gerrit id #${current_patch_id} to MODIFIED\n  $res"
+        tools.log "::bug $bug_id::Failed to update the status of bug " \
+            "#$bug_id for gerrit id #${current_patch_id} to MODIFIED\n  $res"
+        tools.review "" "" "* #$bug_id::Set MODIFIED: FAILED, $res"
     fi
 done
 bz.clean
diff --git a/hooks/bz/change-merged.update_tracker 
b/hooks/bz/change-merged.update_tracker
new file mode 120000
index 0000000..7a06791
--- /dev/null
+++ b/hooks/bz/change-merged.update_tracker
@@ -0,0 +1 @@
+update_tracker
\ No newline at end of file
diff --git a/hooks/bz/patchset-created.bz.1.bug_url 
b/hooks/bz/patchset-created.bz.1.bug_url
index 708a319..ff1e0b0 100755
--- a/hooks/bz/patchset-created.bz.1.bug_url
+++ b/hooks/bz/patchset-created.bz.1.bug_url
@@ -26,32 +26,34 @@
 message=""
 ## Check if we have to manage that branch
 if ! vindex=$(tools.is_in "$branch" "${BRANCHES[@]}"); then
-    echo "Ignoring branch $branch"
+    tools.review "" "" "* Bug-Url: IGNORE, not in monitored branch 
(${BRANCHES[@]})"
     exit 2
 fi
 bug_ids=($(bz.get_bug_id $commit))
 ## break the chain if no bug-url found
 if [[ -z "$bug_ids" ]]; then
+    tools.review "" ""\
+        "* Bug-Url: WARN, no bug url found, make sure header matches 'Bug-Url: 
' and is a valid url."
     exit 2
 fi
 bz_password="${BZ_PASS?No BZ_PASS in the config}"
 bz_user="${BZ_USER?No BZ_USER in the config}"
 bz.login "$bz_user" "$bz_password"
 failed="false"
-echo "Got bug ids ${bug_ids[@]}"
+tools.log "Got bug ids ${bug_ids[@]}"
+msg="* Bug-Url: INFO, got bugs ${bug_ids[@]}"
 for bug_id in ${bug_ids[@]}; do
     if bz.is_private "$bug_id"; then
-        echo "$bug_id::ERROR bug is private."
-        message+="* Bug-Url::$bug_id::ERROR, private bug"
+        tools.log "$bug_id::ERROR bug is private."
+        msg+="\n* #$bug_id::Bug-Url: ERROR, private bug"
         failed="true"
         continue
     fi
 done
 if [[ "$failed" == "true" ]]; then
     bz.clean
-    gerrit.review "-1" "$message"
-    echo "$message"
+    tools.log "$message" 
+    tools.review "-1" "" "$msg"
     exit 2
-else
-    conf.t_put message "$message"
 fi
+tools.review "" "" "$msg"
diff --git a/hooks/bz/patchset-created.bz.2.add_tracker 
b/hooks/bz/patchset-created.bz.2.add_tracker
index a8dd679..a405ac9 100755
--- a/hooks/bz/patchset-created.bz.2.add_tracker
+++ b/hooks/bz/patchset-created.bz.2.add_tracker
@@ -8,10 +8,13 @@
 #      bugzilla submit form.
 #        81 -> oVirt gerrit
 #        82 -> RHEV gerrit
+#
+# This hook never fails
 ###############################################################################
 source bz.sh
 source gerrit.sh
 source conf.sh
+source tools.sh
 
 add_tracker()
 {
@@ -19,16 +22,14 @@
     # Get the bug url and it's flags
     bz.login -b "$bug_id" "$bz_user" "$bz_password"
     ## Update the tracker on the bz bug
-    local message="$(conf.t_get message)"
     if ! bz.add_tracker "$bug_id" "${TRACKER_ID?}" "${change_url//*\/}"; then
         ## raise a warning error
-        message+="\n* Tracker #$bug_id: FAILED"
-        echo "WARNING: Failed to update tracker on bug #$bug_id for gerrit id 
#${change_url//*\/}"
+        echo "* Tracker #$bug_id: FAILED"
+        tools.log "WARNING: Failed to update tracker on bug #$bug_id for 
gerrit id #${change_url//*\/}"
     else
-        message+="\n* Tracker #$bug_id: OK"
-        echo "Tracker updated on bug #$bug_id for gerrit id 
#${change_url//*\/}"
+        echo "* Tracker #$bug_id: OK"
+        tools.log "Tracker updated on bug #$bug_id for gerrit id 
#${change_url//*\/}"
     fi
-    conf.t_put message "$message"
     return 0
 }
 
diff --git a/hooks/bz/patchset-created.bz.2.set_POST 
b/hooks/bz/patchset-created.bz.2.set_POST
index 8815d93..08f5ea0 100755
--- a/hooks/bz/patchset-created.bz.2.set_POST
+++ b/hooks/bz/patchset-created.bz.2.set_POST
@@ -5,6 +5,7 @@
 source bz.sh
 source gerrit.sh
 source conf.sh
+source tools.sh
 
 set_POST()
 {
@@ -15,14 +16,13 @@
     ## Update the tracker on the bz bug
     local message="$(conf.t_get message)"
     if ! res=$(bz.update_status "$bug_id" "POST" "$commit"); then
-        message+="\n* Set POST #$bug_id: FAILED, $res"
-        echo "Failed to update the status of bug #$bug_id for gerrit id 
#${change_url//*\/} to POST"
-        echo "  $res"
+        echo "* #$bug_id::Set POST: FAILED, $res"
+        tools.log "Failed to update the status of bug #$bug_id for gerrit id 
#${change_url//*\/} to POST"
+        tools.log "  $res"
     else
-        message+="\n* Set POST #$bug_id: OK${res:+, $res}"
-        echo "Status updated on bug #$bug_id for gerrit id #${change_url//*\/} 
to POST"
+        echo "* #$bug_id::Set POST: OK${res:+, $res}"
+        tools.log "Status updated on bug #$bug_id for gerrit id 
#${change_url//*\/} to POST"
     fi
-    conf.t_put message "$message"
     return 0
 }
 
diff --git a/hooks/bz/patchset-created.bz.99.review_ok 
b/hooks/bz/patchset-created.bz.99.review_ok
index ba17d45..f5eac6a 100755
--- a/hooks/bz/patchset-created.bz.99.review_ok
+++ b/hooks/bz/patchset-created.bz.99.review_ok
@@ -2,13 +2,10 @@
 source gerrit.sh
 source conf.sh
 source bz.sh
+source tools.sh
 
 ###############################################################################
 ## MAIN
-## get the config
-conf.load
-gerrit.parse_params "$@"
-message="$(conf.t_get message)"
-gerrit.review "0" "$message"
-echo "Got good review"
+tools.review "0" "0"
+tools.log "Got good review"
 bz.clean
diff --git a/hooks/bz/patchset-created.update_tracker 
b/hooks/bz/patchset-created.update_tracker
new file mode 120000
index 0000000..7a06791
--- /dev/null
+++ b/hooks/bz/patchset-created.update_tracker
@@ -0,0 +1 @@
+update_tracker
\ No newline at end of file
diff --git a/hooks/bz/patchset-created.warn_if_not_merged_to_previous_branch 
b/hooks/bz/patchset-created.warn_if_not_merged_to_previous_branch
index ab1d892..ae4156d 100755
--- a/hooks/bz/patchset-created.warn_if_not_merged_to_previous_branch
+++ b/hooks/bz/patchset-created.warn_if_not_merged_to_previous_branch
@@ -6,9 +6,12 @@
 import os
 from config import load_config
 from gerrit import Gerrit
+from tools import get_parser_pc
 
 
-class NonComparableVersions(Exception): pass
+class NonComparableVersions(Exception):
+    pass
+
 
 def is_newer(branch1, branch2):
     try:
@@ -41,44 +44,23 @@
     return branches
 
 
-def get_parser():
-    """
-    Build the parser for patchset-created hook type
-    """
-    parser = argparse.ArgumentParser(
-        description=('This dispatcher handles the'
-                     ' special tags and hook'
-                     ' execution'),
-    )
-    for arg in ('change', 'project', 'branch', 'commit'):
-        parser.add_argument('--' + arg,
-                            action='store',
-                            required=True)
-    for arg in ('author', 'is-draft', 'change-url', 'comment', 'uploader',
-                'patchset', 'topic'):
-        parser.add_argument('--' + arg,
-                            action='store',
-                            required=False)
-    parser.add_argument('-v', '--verbose', action='store_true', default=False)
-    return parser
-
-
 def give_warning(gerrit, commit, project, branches):
-    msg = ("WARNING: This patch was not merged yet on all the newer "
-           "branches\n  Missing on: %s" % ', '.join(branches))
-    gerrit.review(commit=commit, project=project, message=msg)
-    logging.error(msg)
+    long_msg = ("WARNING: This patch was not merged yet on all the newer "
+                "branches\n  Missing on: %s" % ', '.join(branches))
+    msg = ("* Merged to previous: WARN, Missing on branches %s"
+           % ', '.join(branches))
+    print msg
+    logging.error(long_msg)
 
 
 def main():
-    parser = get_parser()
+    parser = get_parser_pc()
     args, unknown = parser.parse_known_args()
     if args.verbose:
         loglevel = logging.DEBUG
     else:
         loglevel = logging.INFO
-    logging.basicConfig(stream=sys.stdout,
-                        level=loglevel,
+    logging.basicConfig(level=loglevel,
                         format='%(asctime)s::'
                         + str(os.getpid())
                         + '::%(levelname)s::%(message)s')
@@ -87,12 +69,15 @@
     stable_branches = config.get('STABLE_BRANCHES', None)
     if stable_branches is None:
         logging.info('No stable branches configured, set STABLE_BRANCHES in '
-                     'the config to the correct value')
+                     'tstream=sys.stderr,he config to the correct value')
+        print "* Merged to previous: IGNORE, No stable branches configured."
         return
     stable_branches = stable_branches.split(' ')
     if args.branch not in stable_branches:
         logging.info('Not in one of the stable branches %s, but in %s'
                      % (stable_branches, args.branch))
+        print "* Merged to previous: IGNORE, Not in stable branch (%s)" \
+            % stable_branches
         return
     gerrit = Gerrit(config['GERRIT_SRV'])
     unmerged_branches = get_unmerged_newer_branches(
diff --git a/hooks/bz/update_tracker b/hooks/bz/update_tracker
new file mode 100755
index 0000000..d9e6a5a
--- /dev/null
+++ b/hooks/bz/update_tracker
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+import os
+import logging
+import sys
+from config import load_config
+from gerrit import Gerrit
+from bz import (
+    get_bug_ids,
+    Bugzilla,
+)
+from tools import get_parser_pc
+
+
+if __name__ == '__main__':
+    parser = get_parser_pc()
+    args, unknown = parser.parse_known_args()
+    if args.verbose:
+        loglevel = logging.DEBUG
+    else:
+        loglevel = logging.INFO
+    logging.basicConfig(level=loglevel,
+                        format='%(asctime)s::'
+                        + str(os.getpid())
+                        + '::%(levelname)s::%(message)s')
+    config = load_config()
+    logging.debug("STARTING::PARAMS %s" % sys.argv)
+    bgz = Bugzilla(
+        user=config['BZ_USER'],
+        passwd=config['BZ_PASS'],
+        )
+    gerrit = Gerrit(config['GERRIT_SRV'])
+    change = gerrit.query(args.commit)[0]
+    bugs = get_bug_ids(args.commit)
+    for bug_id in bugs:
+        logging.info("%s::Updating external" % bug_id)
+        bgz.update_external(
+            bug_id,
+            change['number'],
+            config['TRACKER_ID'],
+            description=change['subject'],
+            status=change['status'],
+        )
+        print "* #%s::Update tracker: OK" % bug_id
+    if not bugs:
+        print "* Update tracker: IGNORE, no bug-urls found"
+    logging.debug("FINISHED")
diff --git a/hooks/hook-dispatcher b/hooks/hook-dispatcher
index 6efa97c..8ef7744 100755
--- a/hooks/hook-dispatcher
+++ b/hooks/hook-dispatcher
@@ -23,6 +23,15 @@
 import argparse
 import logging
 from dulwich.repo import Repo
+## Load also the hook libs
+os.environ["PATH"] = os.environ["PATH"] + ':' \
+    + os.path.dirname(os.path.realpath(__file__)) + '/lib'
+os.environ["PYTHONPATH"] = os.environ.get("PYTHONPATH", "") \
+    + ':' + os.path.dirname(os.path.realpath(__file__)) + '/lib'
+from lib import (
+    gerrit,
+    config,
+)
 
 
 def csv_to_set(csv_string):
@@ -225,6 +234,83 @@
     return chains
 
 
+def parse_stdout(stdout):
+    """
+    The expected output format is:
+
+    ############
+    code_review_value
+    verified_value
+    multiline_msg
+    ############
+
+    Where code_review_value and verified_value can be an empty line if it's
+    an abstention (no modifying the value any of the previous or next hooks
+    in the chain set)
+    If any of the lines is missing then it will assumed an abstention.
+
+        2 lines -> No message
+        1 line  -> No message and no verified value
+        0 lines -> Not changing anything
+
+    And if any of the two first lines fails when converting to integer, it
+    will assume that's the start of the message and count the non-specified
+    votes as abstentions
+
+
+    Examples:
+    ################
+    message starts here
+    -1
+    message continues
+    ################
+
+    ^^^ this will be parsed as only message and two abstentions
+
+    ################
+    -1
+    message starts here
+    0
+    message continues here
+    ################
+
+    ^^^ this will count as CR:-1, and the rest message
+
+    ################
+    0
+    message
+    ################
+
+    ^^^ This will count as CR:0 (will level any positive review) and the rest
+    message
+    """
+    result = {
+        'code_review': None,
+        'verified': None,
+        'msg': None,
+    }
+    stdout = stdout.splitlines()
+    for elem_name in ('code_review', 'verified'):
+        if not stdout:
+            return result
+        try:
+            elem = stdout.pop(0)
+            ## non-voting
+            if not elem:
+                elem = None
+            ## voting
+            else:
+                elem = int(elem)
+                result[elem_name] = elem
+        ## So we don't have a number, the message should start here
+        except ValueError:
+            ## put the line back
+            stdout.insert(0, elem)
+            break
+    result['msg'] = '\n'.join(stdout) or None
+    return result
+
+
 def run_hooks(path, hooks, chain='NONE'):
     """"
     Run the given hooks form the given path
@@ -232,13 +318,9 @@
     :param path: Path where the hooks are located
     :param hooks: hook names to run
     """
-    ## add the hooks lib dir to the path
-    os.environ["PATH"] = os.environ["PATH"] + ':' \
-        + os.path.dirname(os.path.realpath(__file__)) + '/lib'
-    os.environ["PYTHONPATH"] = os.environ.get("PYTHONPATH", "") \
-       + ':' + os.path.dirname(os.path.realpath(__file__)) + '/lib'
     hooks.sort()
     params = sys.argv[1:]
+    global_result = None
     for hook in hooks:
         cmd = [os.path.join(path, hook)]
         cmd.extend(params)
@@ -248,14 +330,26 @@
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE)
         output, error = pipe.communicate()
+        result = parse_stdout(output)
+        if global_result is None:
+            global_result = result
+        else:
+            for elem in ('code_review', 'verified'):
+                if result[elem] is not None \
+                        and result[elem] < global_result[elem]:
+                    global_result[elem] = result[elem]
+            if result['msg']:
+                global_result['msg'] = str(global_result['msg']) \
+                    + '\n' + result['msg']
         if pipe.returncode == 0:
-            logging.info(logstr + '\n' + output)
+            logging.info(logstr + '\n' + str(result))
             error and logging.warn(logstr + '\n' + error)
         else:
-            logging.warn(logstr + '\n' + output)
+            logging.warn(logstr + '\n' + str(result))
             error and logging.error(logstr + '\n' + error)
             logging.warn(chain + "::BROKEN WITH " + str(pipe.returncode))
-            return pipe.returncode
+            return global_result
+    return global_result
 
 
 def run_chains(path, hooks):
@@ -266,16 +360,17 @@
     :param hooks: hook names to run
     """
     ## add the hooks lib dir to the path
+    results = {}
     logging.info("::RUNNING HOOKS::%s", hooks)
     chains = get_chains(hooks)
     for c_name, c_hooks in chains.iteritems():
         logging.info(c_name + "::STARTING")
-        ret_code = run_hooks(path, c_hooks, c_name)
-        if ret_code == 1:
-            logging.error("ABORTING::got return code 1.")
-            return ret_code
-        elif ret_code == 0:
-            logging.info(c_name + "::FINISHED OK")
+        results[c_name] = run_hooks(path, c_hooks, c_name)
+        if results[c_name]['msg'] is None:
+            results[c_name]['msg'] = '* %s: OK' % c_name
+        logging.info(c_name + "::FINISHED with result:\n"
+                     + str(results[c_name]))
+    return results
 
 
 def get_hook_type(opts):
@@ -294,6 +389,40 @@
         return 'change-abandoned'
     else:
         return os.path.basename(__file__)
+
+
+def send_summary(change_id, project, results):
+    """
+    Parse all the results and generate the summary review
+
+    :param change_id: string to identify the change with, usually the commit
+                      hash or the change-id,patchset pair
+    :param results: Dictionary with the compiled results for all the chains
+
+    It will use the lowest values for the votes, skipping the ones that are
+    `None`, and merge all the messages into one.
+
+    For example, if it has two chains, one with CR:0 and one with CR:1, the
+    result will be CR:0.
+    """
+    msg = []
+    code_review = 0
+    verified = 0
+    ## Generate final review
+    for result in results.itervalues():
+        if result['msg']:
+            msg.append(result['msg'])
+        if result['code_review'] is not None \
+                and result['code_review'] < code_review:
+            code_review = result['code_review']
+        if result['verified'] is not None \
+                and result['verified'] < verified:
+            verified = result['verified']
+    msg = '\n'.join(msg)
+    g_server = gerrit.Gerrit(config.load_config()['GERRIT_SRV'])
+    g_server.review(change_id, project, msg, code_review, verified)
+    logging.info("  FINAL REVIEW\nCR: %d\nV: %d\nMSG:\n%s"
+                 % (code_review, verified, msg))
 
 
 def main():
@@ -318,9 +447,13 @@
                       hook_type)
     logging.info("::AVAILABLE HOOKS::%s", hooks)
     tags = {}
+    change_ident = None
     if known_args.commit:
         ## get tags from the commit message
         tags.update(get_commit_tags(known_args.commit))
+        change_ident = known_args.commit
+    elif known_args.patchset and known_args.change:
+        change_ident = "%s,%s" % (known_args.patchset, known_args.change)
     if known_args.comment:
         ## and from the gerrit comment
         tags.update(get_comment_tags(known_args.comment))
@@ -328,9 +461,14 @@
         ## Execute the functions with the tag value, the current available
         ##  hooks, and the args so they can be modified
         hooks = fun(tag_val, hooks, known_args)
-    ## keep them sorted (will be sorted again later though)
-    hooks.sort()
-    run_chains(os.path.join(os.environ['GIT_DIR'], 'hooks'), hooks)
+    ## if we found any hooks, run them
+    if hooks:
+        ## keep them sorted (will be sorted again later though)
+        hooks.sort()
+        results = run_chains(os.path.join(os.environ['GIT_DIR'], 'hooks'),
+                             hooks)
+        if change_ident:
+            send_summary(change_ident, known_args.project, results)
     logging.info("FINISHED")
 
 
diff --git a/hooks/lib/__init__.py b/hooks/lib/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/hooks/lib/__init__.py
diff --git a/hooks/lib/bz.py b/hooks/lib/bz.py
new file mode 100644
index 0000000..935bee5
--- /dev/null
+++ b/hooks/lib/bz.py
@@ -0,0 +1,95 @@
+#!/user/bin/env python
+import os
+import xmlrpclib
+import re
+from dulwich.repo import Repo
+import logging
+
+
+class Bugzilla(object):
+    def __init__(self, user=None, passwd=None,
+                 url='https://bugzilla.redhat.com/xmlrpc.cgi'):
+        self.rpc = xmlrpclib.ServerProxy(url)
+        self.user = user
+        self.passwd = passwd
+
+    def wrap(self, dictionary):
+        if self.user:
+            dictionary['Bugzilla_login'] = self.user
+        if self.passwd:
+            dictionary['Bugzilla_password'] = self.passwd
+        return dictionary
+
+    def __getattr__(self, what):
+        return getattr(self.rpc, what)
+
+    def get_external(self, bug_id, external_bug_id):
+        bug = self.rpc.Bug.get(self.wrap({
+            'ids': bug_id,
+            'extra_fields': ['external_bugs'],
+        }))
+        if not bug['bugs']:
+            logging.error("Unable to get bug %s\n%s"
+                          % (str(bug_id), bug['faults']))
+            return None
+        ## Only one response when asking by the commit hash
+        bug = bug['bugs'][0]
+        for external in bug['external_bugs']:
+            if external['ext_bz_bug_id'] == external_bug_id:
+                return external
+        return None
+
+    def update_external(self, bug_id, external_bug_id, ext_type_id,
+                        status=None, description=None):
+        orig_external = self.get_external(bug_id, external_bug_id)
+        external = {}
+        external['ext_type_id'] = ext_type_id
+        external['ext_bz_bug_id'] = external_bug_id
+        if not orig_external:
+            if description is not None:
+                external['ext_description'] = description
+            if status is not None:
+                external['ext_status'] = status
+            self.ExternalBugs.add_external_bug(self.wrap({
+                'bug_ids': [bug_id],
+                'external_bugs': [external],
+            }))
+        else:
+            if description is not None:
+                external['ext_description'] = description
+            else:
+                external['ext_description'] = orig_external['ext_description']
+            if status is not None:
+                external['ext_status'] = status
+            else:
+                external['ext_status'] = orig_external['ext_status']
+            self.ExternalBugs.update_external_bug(self.wrap(external))
+
+
+def get_bug_ids(commit):
+    """
+    Get the bug ids that were specified in the commit message
+
+    :param commit: Commit to get the message from
+    """
+    bug_regexp = re.compile(
+        r'''
+        ^Bug-Url:\ https?://bugzilla\.redhat\.com/ ## base url
+            (show_bug\.cgi\?id=)? ## optional cgi
+            (?P<bug_id>\d+) ## bug id itself
+        $''',
+        re.VERBOSE
+    )
+    revert_regexp = re.compile(
+        '^This reverts commit (?P<commit>[[:alnum:]]+)$')
+    repo = Repo(os.environ['GIT_DIR'])
+    bugs = []
+    message = repo[commit].message
+    for line in message.splitlines():
+        rev_match = revert_regexp.search(line)
+        if rev_match and rev_match.groupsdict()['commit'] != commit:
+            return get_bug_ids(rev_match.groupsdict()['commit'])
+        match = bug_regexp.search(line)
+        if match:
+            bugs.append(match.groupdict()['bug_id'])
+    return bugs
diff --git a/hooks/lib/bz.sh b/hooks/lib/bz.sh
index a17e235..ceb66ad 100644
--- a/hooks/lib/bz.sh
+++ b/hooks/lib/bz.sh
@@ -93,9 +93,9 @@
     if [[ $rc -eq 0 ]]; then
         rm -f "/tmp/update_bug_log.${bug_id}"
     else
-        echo "Error while updating bug #${bug_id} with post_data, response 
data"\
-             "at /tmp/update_bug_log.${bug_id}"
-        echo "Sent data: $post_data"
+        tools.log "Error while updating bug #${bug_id} with post_data, " \
+            "response data at /tmp/update_bug_log.${bug_id}"
+        tools.log "Sent data: $post_data"
     fi
     ## clean the old bug data from all caches, if any
     rm -f /tmp/bz_cache.*.${bug_id}.*
diff --git a/hooks/lib/config.py b/hooks/lib/config.py
index 5d28a17..7853bdd 100644
--- a/hooks/lib/config.py
+++ b/hooks/lib/config.py
@@ -58,7 +58,7 @@
             return dict.__getitem__(self, item)
         except KeyError:
             logger.error('Unable to get config value %s from any of the '
-                         'config files [%s]' % (item,', '.join(self.files)))
+                         'config files [%s]' % (item, ', '.join(self.files)))
             raise
 
 
diff --git a/hooks/lib/gerrit.py b/hooks/lib/gerrit.py
index 3b09a42..3fb6ba9 100644
--- a/hooks/lib/gerrit.py
+++ b/hooks/lib/gerrit.py
@@ -20,12 +20,12 @@
         )
 
     def generate_cmd(self, action, *options):
-        cmd =  list(self.cmd)
+        cmd = list(self.cmd)
         cmd.append(action)
         cmd.extend(options)
         return cmd
 
-    def review(self, commit, message, project, verify=None, review=None):
+    def review(self, commit, project, message, verify=None, review=None):
         gerrit_cmd = self.generate_cmd(
             'review',
             commit,
@@ -33,12 +33,12 @@
             '--project=%s' % project,
         )
         if verify is not None:
-            gerrit_cmd.append('--verify=%s' % str(verify))
+            gerrit_cmd.append('--verified=%s' % str(verify))
         if review is not None:
             gerrit_cmd.append('--code-review=%s' % str(review))
         cmd = subprocess.Popen(gerrit_cmd,
-                            stdout=subprocess.PIPE,
-                            stderr=subprocess.PIPE)
+                               stdout=subprocess.PIPE,
+                               stderr=subprocess.PIPE)
         out, err = cmd.communicate()
         if cmd.returncode:
             raise Exception("Execution of %s returned %d:\n%s"
@@ -55,8 +55,8 @@
         )
         logger.debug("Executing %s" % ' '.join(gerrit_cmd))
         cmd = subprocess.Popen(gerrit_cmd,
-                            stdout=subprocess.PIPE,
-                            stderr=subprocess.PIPE)
+                               stdout=subprocess.PIPE,
+                               stderr=subprocess.PIPE)
         out, err = cmd.communicate()
         if cmd.returncode:
             raise Exception("Execution of %s returned %d:\n%s"
diff --git a/hooks/lib/tools.py b/hooks/lib/tools.py
new file mode 100644
index 0000000..86b1e25
--- /dev/null
+++ b/hooks/lib/tools.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+import argparse
+
+
+def get_parser_pc():
+    """
+    Build the parser for patchset-created hook type
+    """
+    parser = argparse.ArgumentParser(
+        description=('This dispatcher handles the'
+                     ' special tags and hook'
+                     ' execution'),
+    )
+    for arg in ('change', 'project', 'branch', 'commit'):
+        parser.add_argument('--' + arg,
+                            action='store',
+                            required=True)
+    for arg in ('author', 'is-draft', 'change-url', 'comment', 'uploader',
+                'patchset', 'topic'):
+        parser.add_argument('--' + arg,
+                            action='store',
+                            required=False)
+    parser.add_argument('-v', '--verbose', action='store_true', default=False)
+    return parser
diff --git a/hooks/lib/tools.sh b/hooks/lib/tools.sh
index 09fbde7..3d73688 100644
--- a/hooks/lib/tools.sh
+++ b/hooks/lib/tools.sh
@@ -63,3 +63,22 @@
     local length="${2:-10}"
     echo "$what" | md5sum | head -c$length
 }
+
+
+## log to stderr
+tools.log()
+{
+    echo "$@" >&2
+}
+
+
+## print a review message with the format expeted by the hook dispatcher
+tools.review()
+{
+    local cr="${1}"
+    local ver="${2}"
+    local msg="${3}"
+    echo "$cr"
+    echo "$ver"
+    echo -e "$msg"
+}


-- 
To view, visit http://gerrit.ovirt.org/24496
To unsubscribe, visit http://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Icd602596a78f7353f70eb27a18d0cafa75869cfc
Gerrit-PatchSet: 1
Gerrit-Project: gerrit-admin
Gerrit-Branch: master
Gerrit-Owner: David Caro <dcaro...@redhat.com>
_______________________________________________
Engine-patches mailing list
Engine-patches@ovirt.org
http://lists.ovirt.org/mailman/listinfo/engine-patches

Reply via email to