The implementation is tricky, it's better to use a shared method.
We can push this only after libtaskotron commit 355c415d4f9 is released and deployed.
| lbrabec | |
| tflink |
| task-rpmlint |
The implementation is tricky, it's better to use a shared method.
We can push this only after libtaskotron commit 355c415d4f9 is released and deployed.
tested manually, works
| No Linters Available |
| No Unit Test Coverage |
| Buildable 812 | |
| Build 812: arc lint + arc unit |
In D984#18794, @tflink wrote:Are there plans to change the other tasks we maintain, as well?
I don't see this code being used anywhere else, so I don't know about any other tasks that need adjusting. But if I missed something, please yell.
| Path | Packages | |||
|---|---|---|---|---|
| M | run_rpmlint.py (30 lines) |
| Commit | Tree | Parents | Author | Summary | Date |
|---|---|---|---|---|---|
| 590991442e05 | b249a7a0fd9f | 42fddc3a8ee0 | Kamil Páral | use libtaskotron.os_utils.popen_rt() to execute command (Show More…) | Aug 23 2016, 10:22 AM |
| 1 | import logging | 1 | import logging | ||
|---|---|---|---|---|---|
| 2 | if __name__ == '__main__': | 2 | if __name__ == '__main__': | ||
| 3 | # Set up logging ASAP to see potential problems during import. | 3 | # Set up logging ASAP to see potential problems during import. | ||
| 4 | # Don't set it up when not running as the main script, someone else handles | 4 | # Don't set it up when not running as the main script, someone else handles | ||
| 5 | # that then. | 5 | # that then. | ||
| 6 | logging.basicConfig() | 6 | logging.basicConfig() | ||
| 7 | 7 | | |||
| 8 | import subprocess | 8 | import subprocess | ||
| 9 | import os | 9 | import os | ||
| 10 | import re | 10 | import re | ||
| 11 | from collections import namedtuple | 11 | from collections import namedtuple | ||
| 12 | 12 | | |||
| 13 | from libtaskotron import check | 13 | from libtaskotron import check | ||
| 14 | from libtaskotron import os_utils | ||||
| 14 | 15 | | |||
| 15 | log = logging.getLogger('rpmlint') | 16 | log = logging.getLogger('rpmlint') | ||
| 16 | log.setLevel(logging.DEBUG) | 17 | log.setLevel(logging.DEBUG) | ||
| 17 | log.addHandler(logging.NullHandler()) | 18 | log.addHandler(logging.NullHandler()) | ||
| 18 | 19 | | |||
| 19 | # outcome = str keyword, output = list of str, errors = int, warnings = int | 20 | # outcome = str keyword, output = str, errors = int, warnings = int | ||
| 20 | Result = namedtuple('Result', ['outcome', 'output', 'errors', 'warnings']) | 21 | Result = namedtuple('Result', ['outcome', 'output', 'errors', 'warnings']) | ||
| 21 | 22 | | |||
| 22 | 23 | | |||
| 23 | def run(koji_build, workdir='.', artifactsdir='artifacts'): | 24 | def run(koji_build, workdir='.', artifactsdir='artifacts'): | ||
| 24 | '''The main method to run from Taskotron''' | 25 | '''The main method to run from Taskotron''' | ||
| 25 | 26 | | |||
| 26 | if not os.path.exists(artifactsdir): | 27 | if not os.path.exists(artifactsdir): | ||
| 27 | os.makedirs(artifactsdir) | 28 | os.makedirs(artifactsdir) | ||
| ▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Line(s) | |||||
| 72 | summary = 'rpmlint %s for %s (%s)' % (detail.outcome, koji_build, detail.note) | 73 | summary = 'rpmlint %s for %s (%s)' % (detail.outcome, koji_build, detail.note) | ||
| 73 | log.info(summary) | 74 | log.info(summary) | ||
| 74 | 75 | | |||
| 75 | # store logs | 76 | # store logs | ||
| 76 | log_path = os.path.join(artifactsdir, '%s.log' % koji_build) | 77 | log_path = os.path.join(artifactsdir, '%s.log' % koji_build) | ||
| 77 | log.debug('Saving log to: %s', log_path) | 78 | log.debug('Saving log to: %s', log_path) | ||
| 78 | with open(log_path, 'w') as log_file: | 79 | with open(log_path, 'w') as log_file: | ||
| 79 | log_file.write('##### SRPMs #####\n') | 80 | log_file.write('##### SRPMs #####\n') | ||
| 80 | log_file.writelines(srpm_result.output) | 81 | log_file.write(srpm_result.output) | ||
| 81 | log_file.write('\n') | 82 | log_file.write('\n') | ||
| 82 | log_file.write('##### RPMs #####\n') | 83 | log_file.write('##### RPMs #####\n') | ||
| 83 | log_file.writelines(rpm_result.output) | 84 | log_file.write(rpm_result.output) | ||
| 84 | log_file.write('\n') | 85 | log_file.write('\n') | ||
| 85 | log_file.write(summary) | 86 | log_file.write(summary) | ||
| 86 | detail.artifact = log_path | 87 | detail.artifact = log_path | ||
| 87 | 88 | | |||
| 88 | output = check.export_YAML(detail) | 89 | output = check.export_YAML(detail) | ||
| 89 | return output | 90 | return output | ||
| 90 | 91 | | |||
| 91 | 92 | | |||
| 92 | def run_rpmlint(command): | 93 | def run_rpmlint(command): | ||
| 93 | '''Run rpmlint command. | 94 | '''Run rpmlint command. | ||
| 94 | :param list command: command to run through subprocess.Popen | 95 | :param list command: command to run through subprocess.Popen | ||
| 95 | :rtype: Results | 96 | :rtype: Results | ||
| 96 | ''' | 97 | ''' | ||
| 97 | log.debug('Running command: %s', command) | 98 | log.debug('Running command: %s', command) | ||
| 98 | proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1, | 99 | try: | ||
| 99 | close_fds=True) | 100 | output, _ = os_utils.popen_rt(command) | ||
| 100 | output = [] | 101 | returncode = 0 | ||
| 101 | for line in iter(proc.stdout.readline, ''): | | |||
| 102 | print line, | | |||
| 103 | output.append(line) | | |||
| 104 | stdout, stderr = proc.communicate() | | |||
| 105 | assert not stdout and not stderr, "Extra stdout or stderr found, this shouldn't happen" | | |||
| 106 | | ||||
| 107 | if proc.returncode == 0: | | |||
| 108 | outcome = 'PASSED' | 102 | outcome = 'PASSED' | ||
| 109 | else: | 103 | except subprocess.CalledProcessError as e: | ||
| 110 | if proc.returncode == 64 or proc.returncode == 66: | 104 | output = e.output | ||
| 105 | returncode = e.returncode | ||||
| 106 | if returncode in (64, 66): | ||||
| 111 | outcome = 'FAILED' | 107 | outcome = 'FAILED' | ||
| 112 | else: | 108 | else: | ||
| 113 | outcome = 'ABORTED' | 109 | outcome = 'ABORTED' | ||
| 114 | 110 | | |||
| 115 | errors = 0 | 111 | errors = 0 | ||
| 116 | warnings = 0 | 112 | warnings = 0 | ||
| 117 | match = re.search(r'(\d+) errors, (\d+) warnings', output[-1] if output else '') | 113 | match = re.search(r'(\d+) errors, (\d+) warnings', output.splitlines()[-1] if output else '') | ||
| 118 | if match: | 114 | if match: | ||
| 119 | errors = int(match.group(1)) | 115 | errors = int(match.group(1)) | ||
| 120 | warnings = int(match.group(2)) | 116 | warnings = int(match.group(2)) | ||
| 121 | if not match and outcome != 'ABORTED': | 117 | if not match and outcome != 'ABORTED': | ||
| 122 | errmsg = ('No rpmlint stats on the last line, should not happen. Exit code %d, output:\n%s' | 118 | errmsg = ('No rpmlint stats on the last line, should not happen. Exit code %d, output:\n%s' | ||
| 123 | % (proc.returncode, ''.join(output))) | 119 | % (returncode, output)) | ||
| 124 | log.critical(errmsg) | 120 | log.critical(errmsg) | ||
| 125 | assert False, 'No rpmlint stats on the last line' | 121 | assert False, 'No rpmlint stats on the last line' | ||
| 126 | 122 | | |||
| 127 | result = Result(outcome=outcome, output=output, errors=errors, warnings=warnings) | 123 | result = Result(outcome=outcome, output=output, errors=errors, warnings=warnings) | ||
| 128 | return result | 124 | return result | ||
| 129 | 125 | | |||
| 130 | 126 | | |||
| 131 | if __name__ == '__main__': | 127 | if __name__ == '__main__': | ||
| 132 | output = run('local-run') | 128 | output = run('local-run') | ||
| 133 | print output | 129 | print output | ||