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 |