Properly check the result of CI precheck
authorFrederic Massart <fred@moodle.com>
Tue, 20 Jan 2015 11:50:32 +0000 (19:50 +0800)
committerFrederic Massart <fred@moodle.com>
Tue, 20 Jan 2015 11:50:32 +0000 (19:50 +0800)
mdk/ci.py
mdk/commands/precheck.py

index 60b0133..ffbf227 100644 (file)
--- a/mdk/ci.py
+++ b/mdk/ci.py
@@ -33,6 +33,11 @@ C = Conf()
 class CI(object):
     """Wrapper for Jenkins"""
 
+    SUCCESS = 'S'
+    FAILURE = 'F'
+    ERROR = 'E'
+    WARNING = 'W'
+
     _jenkins = None
     url = None
     token = None
@@ -61,7 +66,7 @@ class CI(object):
         self._jenkins = jenkins.Jenkins(self.url)
 
     def precheckRemoteBranch(self, remote, branch, integrateto, issue=None):
-        """Runs the precheck job and returns the build object"""
+        """Runs the precheck job and returns the outcome"""
         params = {
             'remote': remote,
             'branch': branch,
@@ -75,13 +80,70 @@ class CI(object):
         try:
             invoke = job.invoke(build_params=params, securitytoken=self.token, invoke_pre_check_delay=0)
             invoke.block_until_not_queued(60, 2)
-        except JenkinsAPIException:
-            raise CIException('Failed to invoke the build, check your permissions.')
         except TimeOut:
             raise CIException('The build has been in queue for more than 60s. Aborting, please refer to: %s' % job.baseurl)
+        except JenkinsAPIException:
+            raise CIException('Failed to invoke the build, check your permissions.')
 
         build = invoke.get_build()
-        return build
+
+        logging.info('Waiting for the build to complete, please wait...')
+        build.block_until_complete(3)
+
+        # Checking the build
+        outcome = CI.SUCCESS
+        infos = {'url': build.baseurl}
+
+        if build.is_good():
+            logging.debug('Build complete, checking precheck results...')
+
+            output = build.get_console()
+            result = self.parseSmurfResult(output)
+            if not result:
+                outcome = CI.FAILURE
+            else:
+                outcome = result['smurf']['result']
+                infos = dict(infos.items() + result.items())
+
+        else:
+            outcome = CI.FAILURE
+
+        return (outcome, infos)
+
+    def parseSmurfResult(self, output):
+        """Parse the smurt result"""
+        result = {}
+
+        for line in output.splitlines():
+            if not line.startswith('SMURFRESULT'):
+                continue
+
+            line = line.replace('SMURFRESULT: ', '')
+            (smurf, rest) = line.split(':')
+            elements = [smurf]
+            elements.extend(rest.split(';'))
+            for element in elements:
+                data = element.split(',')
+
+                errors = int(data[2])
+                warnings = int(data[3])
+
+                if errors > 0:
+                    outcome = CI.ERROR
+                elif warnings > 0:
+                    outcome = CI.WARNING
+                else:
+                    outcome = CI.SUCCESS
+
+                result[data[0]] = {
+                    'errors': errors,
+                    'warnings': warnings,
+                    'result': outcome
+                }
+
+            break
+
+        return result
 
 
 class CIException(Exception):
index d724e4d..823160f 100644 (file)
@@ -93,16 +93,44 @@ class PrecheckCommand(Command):
         try:
             # TODO Remove that ugly hack to get the read-only remote.
             logging.info('Invoking the build on the CI server...')
-            build = ci.precheckRemoteBranch(self.C.get('repositoryUrl'), branch, against, 'MDL-%s' % issue)
+            (outcome, infos) = ci.precheckRemoteBranch(self.C.get('repositoryUrl'), branch, against, 'MDL-%s' % issue)
         except CIException as e:
             raise e
 
-        logging.info('Waiting for the build to complete, please wait...')
-        build.block_until_complete(3)
 
-        if build.is_good():
+        if outcome == CI.FAILURE:
+            logging.warning('Build failed, please refer to:\n  %s', infos.url)
+            sys.exit(self.FAILED)
+
+        elif outcome == CI.SUCCESS:
             logging.info('Precheck passed, good work!')
             sys.exit(0)
+
+
+        # If we get here, that was a fail.
+        if outcome == CI.ERROR:
+            logging.info('Precheck FAILED with ERRORS.')
         else:
-            logging.warning('Precheck failed, refer to:\n  %s', build.baseurl)
-            sys.exit(self.FAILED)
+            logging.info('Precheck FAILED with WARNINGS.')
+        logging.info('')
+
+        mapping = {
+            'phplint': 'PHP Lint',
+            'php': 'PHP coding style',
+            'js': 'Javascript',
+            'css': 'CSS',
+            'phpdoc': 'PHP Doc',
+            'commit': 'Commit message',
+            'savepoint': 'Update/Upgrade',
+            'thirdparty': 'Third party'
+        }
+
+        for key in mapping:
+            details = infos.get(key)
+
+            symbol = ' ' if details['result'] == CI.SUCCESS else ('!' if details['result'] == CI.WARNING else 'X')
+            print '  [{}] {}     ({} errors, {} warnings)'.format(symbol, mapping.get(key, key), details.get('errors'), details.get('warnings'))
+
+        logging.info('')
+        logging.info('More details at: %s', infos.get('url'))
+        sys.exit(self.FAILED)