1
by Martin Mrazik
initial commit |
1 |
from launchpadlib.launchpad import Launchpad |
2 |
from launchpadlib.credentials import UnencryptedFileCredentialStore |
|
3 |
from credentials import AuthorizeRequestTokenWithConsole |
|
12.4.1
by Allan LeSage
Minor organizational suggestions. |
4 |
from mergeproposalreview import MergeProposalReview |
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
5 |
from settings import (ALLOWED_USERS, LAUNCHPAD_REVIEW_TYPE, JENKINS_URL, |
6 |
JENKINS_PASSWORD, JENKINS_USER, LAUNCHPAD_LOGIN, |
|
7 |
PUBLIC_JENKINS_URL, URLS_TO_HIDE, CREDENTIAL_STORE_PATH, |
|
8 |
LP_ENV, LP_APP, logger) |
|
27.2.1
by Martin Mrazik
Log unathorized access instead of printing python stack traces |
9 |
from lazr.restfulclient.errors import Unauthorized |
6
by Martin Mrazik
better error handling when starting a job fails + getting rid of urllib |
10 |
import jenkins |
30.1.4
by Martin Mrazik
removed unnecessary import and improved the tests a bit |
11 |
from xml.dom.minidom import parseString |
1
by Martin Mrazik
initial commit |
12 |
import re |
31.1.3
by Martin Mrazik
correct imports |
13 |
from jsonjenkins import JSONJenkins |
63
by Martin Mrazik
adding a rebuild link |
14 |
from textwrap import dedent |
12.7.1
by Allan LeSage
Per sergiusens' advice, correct getMergeProposalAttributes missing changes and eliminate hard-coded lognames. |
15 |
|
70
by Martin Mrazik
ppe8 fix |
16 |
|
1
by Martin Mrazik
initial commit |
17 |
class LaunchpadAgent(): |
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
18 |
_launchpad = None |
1
by Martin Mrazik
initial commit |
19 |
|
20 |
def __init__(self): |
|
21 |
self._launchpad = self.login() |
|
22 |
||
22.2.5
by Martin Mrazik
correct calling of launchpadAgent functions |
23 |
def get_launchpad(self): |
24 |
return self._launchpad |
|
25 |
||
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
26 |
def change_mp_status(self, mp, status, message, subject, |
12.2.1
by Martin Mrazik
Getting rid of one jenkins message during the autolanding process. |
27 |
revision, vote=None): |
12.3.2
by Martin Mrazik
MP locking now implemented outside of launchpad to get rid of excess votes |
28 |
mp.setStatus(status=status, revid=revision) |
12.2.1
by Martin Mrazik
Getting rid of one jenkins message during the autolanding process. |
29 |
if vote: |
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
30 |
mp.createComment(review_type=LAUNCHPAD_REVIEW_TYPE, |
31 |
subject=subject, content=message, vote=vote) |
|
12.3.7
by Martin Mrazik
Refactoring into class + fixed unit tests |
32 |
review = MergeProposalReview(mp) |
33 |
review.unlock() |
|
12.2.1
by Martin Mrazik
Getting rid of one jenkins message during the autolanding process. |
34 |
else: |
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
35 |
mp.createComment(review_type=LAUNCHPAD_REVIEW_TYPE, |
12.2.1
by Martin Mrazik
Getting rid of one jenkins message during the autolanding process. |
36 |
subject=subject, content=message) |
37 |
||
1
by Martin Mrazik
initial commit |
38 |
def get_merge_proposal(self, branch_name, merge_proposal_link): |
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
39 |
lp_branch = branch_name.replace('https://code.launchpad.net/', 'lp:') |
40 |
lp_branch = lp_branch.replace('https://code.staging.launchpad.net/', |
|
41 |
'lp://staging/') |
|
42 |
logger.debug('fetching branch: ' + lp_branch) |
|
1
by Martin Mrazik
initial commit |
43 |
branch = self._launchpad.branches.getByUrl(url=lp_branch) |
44 |
if not branch: |
|
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
45 |
logger.debug('branch "' + branch_name + '" doesn\'t exist') |
1
by Martin Mrazik
initial commit |
46 |
return None |
47 |
||
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
48 |
logger.debug('mp_link: ' + merge_proposal_link) |
49 |
||
1
by Martin Mrazik
initial commit |
50 |
for mp in branch.landing_targets: |
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
51 |
logger.debug('mp.web_link: ' + mp.web_link) |
1
by Martin Mrazik
initial commit |
52 |
if mp.web_link == merge_proposal_link: |
53 |
return mp |
|
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
54 |
|
1
by Martin Mrazik
initial commit |
55 |
return None |
56 |
||
57 |
def get_launchpad_user(self): |
|
58 |
return self._launchpad.people(LAUNCHPAD_LOGIN) |
|
59 |
||
60 |
@staticmethod
|
|
61 |
def get_branch_from_mp(merge_proposal): |
|
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
62 |
m = re.search('(.*)\+merge/[0-9]+$', merge_proposal) |
1
by Martin Mrazik
initial commit |
63 |
if m: |
64 |
return m.group(1) |
|
65 |
return None |
|
66 |
||
30.1.1
by Martin Mrazik
If the project is published then change the URL to public jenkins |
67 |
@staticmethod
|
68 |
def is_job_published(job): |
|
69 |
if not job: |
|
70 |
logger.debug('is_job_published: no job specified') |
|
71 |
return False |
|
72 |
||
30.1.2
by Martin Mrazik
specifing the right JENKINS_URL from settings |
73 |
jenkins_server = jenkins.Jenkins(JENKINS_URL, |
30.1.1
by Martin Mrazik
If the project is published then change the URL to public jenkins |
74 |
JENKINS_USER, JENKINS_PASSWORD) |
75 |
try: |
|
76 |
config = jenkins_server.get_job_config(job) |
|
77 |
dom = parseString(config) |
|
78 |
elements = dom.getElementsByTagName( |
|
79 |
'hudson.plugins.build__publisher.BuildPublisher') |
|
30.1.5
by Martin Mrazik
Fixing some issues revelead by code review |
80 |
# if the above element was found then the job is configured to be
|
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
81 |
# published. Return true if the length of the array is > 0 and
|
30.1.5
by Martin Mrazik
Fixing some issues revelead by code review |
82 |
# False otherwise
|
83 |
return elements.length > 0 |
|
30.1.1
by Martin Mrazik
If the project is published then change the URL to public jenkins |
84 |
except: |
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
85 |
logger.info('Unable to fetch or parse job configuration for: ' + |
30.1.5
by Martin Mrazik
Fixing some issues revelead by code review |
86 |
job) |
30.1.1
by Martin Mrazik
If the project is published then change the URL to public jenkins |
87 |
return False |
30.1.5
by Martin Mrazik
Fixing some issues revelead by code review |
88 |
|
1
by Martin Mrazik
initial commit |
89 |
@staticmethod
|
90 |
def hide_jenkins_url(url): |
|
30.1.1
by Martin Mrazik
If the project is published then change the URL to public jenkins |
91 |
job = None |
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
92 |
# Match the jenkins JOB_URL and extract the IP/hostname
|
30.1.5
by Martin Mrazik
Fixing some issues revelead by code review |
93 |
# (^http[s]?://.*) and the name of jenkins job.
|
94 |
# The following format is expected
|
|
95 |
# http://10.0.0.1:8080/job/my-jenkins-job/7/
|
|
96 |
m = re.match('^(http[s]?://.*)/job/([^/]*)/.*$', url) |
|
30.1.1
by Martin Mrazik
If the project is published then change the URL to public jenkins |
97 |
if m: |
98 |
jenkins_ip = m.group(1) |
|
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
99 |
job = m.group(2) |
30.1.5
by Martin Mrazik
Fixing some issues revelead by code review |
100 |
# if there was a match in the regexp above then both
|
101 |
# job and jenkins_ip must be set. It is enough to check just for job
|
|
30.1.1
by Martin Mrazik
If the project is published then change the URL to public jenkins |
102 |
if job and LaunchpadAgent.is_job_published(job): |
103 |
return url.replace(jenkins_ip, PUBLIC_JENKINS_URL) |
|
104 |
||
1
by Martin Mrazik
initial commit |
105 |
for ip, replacement in URLS_TO_HIDE: |
106 |
url = url.replace(ip, replacement) |
|
107 |
return url |
|
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
108 |
|
1
by Martin Mrazik
initial commit |
109 |
@staticmethod
|
110 |
def login(): |
|
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
111 |
credential_store = UnencryptedFileCredentialStore( |
112 |
CREDENTIAL_STORE_PATH) |
|
113 |
authorization_engine = AuthorizeRequestTokenWithConsole(LP_ENV, LP_APP) |
|
114 |
return Launchpad.login_with(LP_APP, LP_ENV, |
|
115 |
credential_store=credential_store, |
|
116 |
authorization_engine=authorization_engine) |
|
117 |
||
1
by Martin Mrazik
initial commit |
118 |
|
50.2.1
by Martin Mrazik
Check for commit message before starting the build |
119 |
class UpdateMergeProposalException(Exception): |
120 |
pass
|
|
121 |
||
122 |
||
123 |
def build_state(args): |
|
124 |
"""Return a tuple containing merge proposal state, message, and
|
|
125 |
subject."""
|
|
126 |
||
127 |
if 'merge_failed' in args and args['merge_failed']: |
|
128 |
return ('Needs review', |
|
129 |
"FAILED: Autolanding.\n" + |
|
130 |
"Merging failed. More details in the following jenkins job:\n", |
|
131 |
"Re: [Merge] autolanding failed (merge failed)") |
|
132 |
elif 'push_failed' in args and args['push_failed']: |
|
133 |
return ('Needs review', |
|
134 |
"FAILED: Autolanding.\n" + |
|
135 |
"Pushing failed. More details in the following jenkins job:\n", |
|
136 |
"Re: [Merge] autolanding failed (push failed)") |
|
137 |
elif 'landing_failed' in args and args['landing_failed']: |
|
138 |
return ('Needs review', |
|
139 |
"FAILED: Autolanding.\n" + |
|
140 |
"More details in the following jenkins job:\n", |
|
141 |
"Re: [Merge] autolanding failed") |
|
142 |
elif 'merged' in args and args['merged']: |
|
143 |
return ('Merged', |
|
144 |
"PASSED: Autolanding.\nBranch merged.", |
|
145 |
"Re: [Merge] branch merged") |
|
146 |
elif 'commit_message_empty' in args and args['commit_message_empty']: |
|
147 |
return ('Needs review', |
|
148 |
"FAILED: Autolanding.\nNo commit message was specified.\n", |
|
149 |
"Re: [Merge] autolanding failed",) |
|
150 |
elif 'unapproved_changes' in args and args['unapproved_changes']: |
|
151 |
return ('Needs review', |
|
152 |
"FAILED: Autolanding.\n" + |
|
153 |
"Unapproved changes made after approval.\n", |
|
154 |
"Re: [Merge] autolanding failed",) |
|
58.1.1
by Martin Mrazik
one more try to fix the ghost-revision issue |
155 |
elif 'invalid_revid' in args and args['invalid_revid']: |
156 |
return ('Needs review', |
|
157 |
"FAILED: Autolanding.\n" + |
|
58.1.2
by Martin Mrazik
added a hint about permissions |
158 |
"Approved revid is not set in launchpad " + |
159 |
"(maybe a permission problem?).\n", |
|
58.1.1
by Martin Mrazik
one more try to fix the ghost-revision issue |
160 |
"Re: [Merge] autolanding failed",) |
50.2.1
by Martin Mrazik
Check for commit message before starting the build |
161 |
else: |
162 |
raise UpdateMergeProposalException("unrecognized parameters") |
|
163 |
||
164 |
||
27.1.3
by Jenkins User
moving approve/disapprove/needs fixing to a class constant and fixing all the occurences |
165 |
class LaunchpadVote(): |
166 |
APPROVE = 'Approve' |
|
167 |
DISAPPROVE = 'Disapprove' |
|
168 |
NEEDS_FIXING = 'Needs Fixing' |
|
1
by Martin Mrazik
initial commit |
169 |
|
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
170 |
|
1
by Martin Mrazik
initial commit |
171 |
class LaunchpadTrigger(): |
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
172 |
_launchpad = None |
1
by Martin Mrazik
initial commit |
173 |
_launchpad_user = None |
31.1.2
by Martin Mrazik
Console links for VoteOnMergeProposal as well |
174 |
_json_jenkins = None |
1
by Martin Mrazik
initial commit |
175 |
|
176 |
def __init__(self): |
|
22.2.5
by Martin Mrazik
correct calling of launchpadAgent functions |
177 |
self._launchpadAgent = LaunchpadAgent() |
178 |
self._launchpad = self._launchpadAgent.get_launchpad() |
|
3
by Martin Mrazik
fixed wrong indenting |
179 |
self._launchpad_user = self._launchpad.people(LAUNCHPAD_LOGIN) |
1
by Martin Mrazik
initial commit |
180 |
|
181 |
def get_launchpad_user(self): |
|
182 |
return self._launchpad_user |
|
183 |
||
22.2.1
by Martin Mrazik
first attempt to migrate autoland.sh to autoland.py |
184 |
def get_branch_from_mp(self, merge_proposal): |
22.2.5
by Martin Mrazik
correct calling of launchpadAgent functions |
185 |
return self._launchpadAgent.get_branch_from_mp(merge_proposal) |
22.2.1
by Martin Mrazik
first attempt to migrate autoland.sh to autoland.py |
186 |
|
187 |
def hide_jenkins_url(self, url): |
|
22.2.5
by Martin Mrazik
correct calling of launchpadAgent functions |
188 |
return self._launchpadAgent.hide_jenkins_url(url) |
22.2.1
by Martin Mrazik
first attempt to migrate autoland.sh to autoland.py |
189 |
|
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
190 |
def change_mp_status(self, mp, status, message, subject, |
22.2.1
by Martin Mrazik
first attempt to migrate autoland.sh to autoland.py |
191 |
revision, vote=None): |
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
192 |
return self._launchpadAgent.change_mp_status(mp, status, message, |
193 |
subject, revision, vote) |
|
22.2.1
by Martin Mrazik
first attempt to migrate autoland.sh to autoland.py |
194 |
|
22.2.6
by Martin Mrazik
adding get_merge_proposal |
195 |
def get_merge_proposal(self, branch_name, merge_proposal_link): |
196 |
return self._launchpadAgent.get_merge_proposal(branch_name, |
|
197 |
merge_proposal_link) |
|
198 |
||
31.1.2
by Martin Mrazik
Console links for VoteOnMergeProposal as well |
199 |
def approve_mp(self, mp, revision, build_url): |
31.1.9
by Martin Mrazik
Fixed few whitespace issues |
200 |
state = 'PASSED: Continuous integration, rev:' + str(revision) |
31.1.2
by Martin Mrazik
Console links for VoteOnMergeProposal as well |
201 |
logger.debug(state) |
68
by Martin Mrazik
not adding the rebuild link when doing autolanding (approve/disapprove_mp methods are being called when autolanding) |
202 |
content = self.format_message_for_mp_update(build_url, |
69
by Martin Mrazik
correct handling of autolanding |
203 |
state + "\n") |
31.1.2
by Martin Mrazik
Console links for VoteOnMergeProposal as well |
204 |
mp.createComment(review_type=LAUNCHPAD_REVIEW_TYPE, |
205 |
vote=LaunchpadVote.APPROVE, subject=state, |
|
67
by Martin Mrazik
fixed a forgotten custom formatting + fixed the tests |
206 |
content=content) |
31.1.2
by Martin Mrazik
Console links for VoteOnMergeProposal as well |
207 |
review = MergeProposalReview(mp) |
208 |
review.unlock() |
|
209 |
||
210 |
def disapprove_mp(self, mp, revision, build_url): |
|
31.1.9
by Martin Mrazik
Fixed few whitespace issues |
211 |
state = 'FAILED: Continuous integration, rev:' + str(revision) |
31.1.2
by Martin Mrazik
Console links for VoteOnMergeProposal as well |
212 |
logger.debug(state) |
68
by Martin Mrazik
not adding the rebuild link when doing autolanding (approve/disapprove_mp methods are being called when autolanding) |
213 |
content = self.format_message_for_mp_update(build_url, |
69
by Martin Mrazik
correct handling of autolanding |
214 |
state + "\n") |
31.1.2
by Martin Mrazik
Console links for VoteOnMergeProposal as well |
215 |
mp.createComment(review_type=LAUNCHPAD_REVIEW_TYPE, |
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
216 |
vote=LaunchpadVote.NEEDS_FIXING, subject=state, |
67
by Martin Mrazik
fixed a forgotten custom formatting + fixed the tests |
217 |
content=content) |
31.1.2
by Martin Mrazik
Console links for VoteOnMergeProposal as well |
218 |
review = MergeProposalReview(mp) |
219 |
review.unlock() |
|
220 |
||
31.1.11
by Martin Mrazik
renamed get_json_jenkins to _get_json_jenkins |
221 |
def _get_json_jenkins(self, url): |
31.1.2
by Martin Mrazik
Console links for VoteOnMergeProposal as well |
222 |
if not self._json_jenkins: |
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
223 |
self._json_jenkins = JSONJenkins(url, JENKINS_USER, |
31.1.2
by Martin Mrazik
Console links for VoteOnMergeProposal as well |
224 |
JENKINS_PASSWORD) |
225 |
return self._json_jenkins |
|
226 |
||
227 |
def get_executed_test_runs(self, url): |
|
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
228 |
jenkins = self._get_json_jenkins(url) |
31.1.2
by Martin Mrazik
Console links for VoteOnMergeProposal as well |
229 |
job = jenkins.get_json_data(url) |
230 |
ret = "\nExecuted test runs:" |
|
231 |
for run in job['runs']: |
|
31.1.7
by Martin Mrazik
workarounding some jenkins weirdness when there are more runs than reality |
232 |
if run['number'] == job['number']: |
233 |
result = jenkins.get_json_data(run['url'])['result'] |
|
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
234 |
ret = (ret + "\n " + result + ': ' + |
31.1.7
by Martin Mrazik
workarounding some jenkins weirdness when there are more runs than reality |
235 |
self.hide_jenkins_url(run['url']) + 'console') |
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
236 |
return ret |
31.1.2
by Martin Mrazik
Console links for VoteOnMergeProposal as well |
237 |
|
68
by Martin Mrazik
not adding the rebuild link when doing autolanding (approve/disapprove_mp methods are being called when autolanding) |
238 |
def format_message_for_mp_update(self, build_url, message=None, |
239 |
include_rebuild_link=True): |
|
63
by Martin Mrazik
adding a rebuild link |
240 |
formatted_message = dedent('''\ |
64
by Martin Mrazik
removed coverity |
241 |
{message}{build_url}{executed_test_runs} |
68
by Martin Mrazik
not adding the rebuild link when doing autolanding (approve/disapprove_mp methods are being called when autolanding) |
242 |
''') |
243 |
if include_rebuild_link: |
|
244 |
formatted_message = formatted_message + dedent('''\ |
|
66
by Martin Mrazik
adding a newline for better readability |
245 |
|
63
by Martin Mrazik
adding a rebuild link |
246 |
Click here to trigger a rebuild:
|
247 |
{build_url}/rebuild/? |
|
248 |
''') |
|
44.3.1
by Martin Mrazik
refactoring message generation for autolanding/ci so both messages are consistent |
249 |
if not message: |
250 |
message = '' |
|
50.2.1
by Martin Mrazik
Check for commit message before starting the build |
251 |
if not build_url: |
252 |
build_url_message = '' |
|
253 |
executed_test_runs_message = '' |
|
254 |
else: |
|
255 |
build_url_message = self.hide_jenkins_url(build_url) |
|
256 |
executed_test_runs_message = self.get_executed_test_runs( |
|
257 |
build_url) |
|
44.3.1
by Martin Mrazik
refactoring message generation for autolanding/ci so both messages are consistent |
258 |
formatted_message = formatted_message.format( |
44.3.3
by Martin Mrazik
fixed comments from MP |
259 |
message=message, |
50.2.1
by Martin Mrazik
Check for commit message before starting the build |
260 |
build_url=build_url_message, |
261 |
executed_test_runs=executed_test_runs_message) |
|
44.3.1
by Martin Mrazik
refactoring message generation for autolanding/ci so both messages are consistent |
262 |
return formatted_message |
263 |
||
4
by Martin Mrazik
fixed few minor things |
264 |
def get_latest_review(self, mp): |
1
by Martin Mrazik
initial commit |
265 |
revision = 0 |
266 |
launchpad_user = self.get_launchpad_user() |
|
267 |
for comment in mp.all_comments: |
|
268 |
if comment.author.name == launchpad_user.name: |
|
269 |
if comment.vote_tag == LAUNCHPAD_REVIEW_TYPE: |
|
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
270 |
m = re.search( |
271 |
'^(PASSED|FAILED): Continuous integration, rev:(\d+)', |
|
272 |
comment.message_body) |
|
1
by Martin Mrazik
initial commit |
273 |
if m and (int(m.group(2)) > revision): |
274 |
revision = int(m.group(2)) |
|
275 |
return revision |
|
276 |
||
277 |
def latest_candidate_validated(self, mp): |
|
4
by Martin Mrazik
fixed few minor things |
278 |
latest_review = self.get_latest_review(mp) |
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
279 |
logger.debug('Latest_review is revision: ' + str(latest_review)) |
1
by Martin Mrazik
initial commit |
280 |
if latest_review >= mp.source_branch.revision_count: |
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
281 |
logger.debug('Skipping this MP. Current revision: ' + |
12.5.1
by Martin Mrazik
replacing prints by logging |
282 |
str(mp.source_branch.revision_count)) |
1
by Martin Mrazik
initial commit |
283 |
return True |
284 |
return False |
|
285 |
||
286 |
def testing_in_progress(self, mp): |
|
12.3.7
by Martin Mrazik
Refactoring into class + fixed unit tests |
287 |
review = MergeProposalReview(mp) |
288 |
if review.is_locked(): |
|
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
289 |
logger.debug('Skipping this MP. It is curently being ' + |
12.5.1
by Martin Mrazik
replacing prints by logging |
290 |
'tested by Jenkins.') |
12.3.2
by Martin Mrazik
MP locking now implemented outside of launchpad to get rid of excess votes |
291 |
return True |
292 |
return False |
|
1
by Martin Mrazik
initial commit |
293 |
|
35.1.1
by Martin Mrazik
Trigger autolanding if the MP is Approved by trusted person |
294 |
def user_allowed_to_trigger_jobs(self, lp_user): |
1
by Martin Mrazik
initial commit |
295 |
allowed_people = ALLOWED_USERS |
35.1.1
by Martin Mrazik
Trigger autolanding if the MP is Approved by trusted person |
296 |
if lp_user.name in allowed_people: |
1
by Martin Mrazik
initial commit |
297 |
return True |
35.1.1
by Martin Mrazik
Trigger autolanding if the MP is Approved by trusted person |
298 |
for membership in lp_user.memberships_details: |
1
by Martin Mrazik
initial commit |
299 |
if membership.team.name in allowed_people: |
300 |
return True |
|
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
301 |
logger.debug('User "' + lp_user.name + |
12.5.1
by Martin Mrazik
replacing prints by logging |
302 |
'" not allowed to trigger jobs') |
1
by Martin Mrazik
initial commit |
303 |
return False |
304 |
||
305 |
def start_jenkins_job(self, jenkins_url, jenkins_job, mp): |
|
56.1.1
by Martin Mrazik
jenkins should authenticate when triggering jobs |
306 |
j = jenkins.Jenkins(jenkins_url, JENKINS_USER, JENKINS_PASSWORD) |
6
by Martin Mrazik
better error handling when starting a job fails + getting rid of urllib |
307 |
jenkins_params = { |
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
308 |
'landing_candidate': 'bzr+ssh://' + LAUNCHPAD_LOGIN + |
309 |
'@bazaar.launchpad.net/' + mp.source_branch.unique_name, |
|
310 |
'merge_proposal': mp.web_link, |
|
311 |
'candidate_revision': mp.source_branch.revision_count |
|
12.3.2
by Martin Mrazik
MP locking now implemented outside of launchpad to get rid of excess votes |
312 |
}
|
7
by Martin Mrazik
ignore non-buildable jobs |
313 |
|
314 |
job_info = j.get_job_info(jenkins_job) |
|
315 |
if not job_info['buildable']: |
|
27.2.7
by Martin Mrazik
Fixed typo (Jobs vs Job) + removed unused variable (error) |
316 |
logger.debug('Job is not buildable (probably disabled)') |
7
by Martin Mrazik
ignore non-buildable jobs |
317 |
return False |
318 |
||
27.2.4
by Martin Mrazik
moving the logic to the right place (start_jenkins_job) + proper return value handling |
319 |
try: |
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
320 |
mp.nominateReviewer(reviewer=self.get_launchpad_user(), |
27.2.4
by Martin Mrazik
moving the logic to the right place (start_jenkins_job) + proper return value handling |
321 |
review_type=LAUNCHPAD_REVIEW_TYPE) |
27.2.7
by Martin Mrazik
Fixed typo (Jobs vs Job) + removed unused variable (error) |
322 |
except Unauthorized: |
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
323 |
logger.info('You are not authorized to review this merge ' + |
27.2.4
by Martin Mrazik
moving the logic to the right place (start_jenkins_job) + proper return value handling |
324 |
'proposal. CI process will now be stopped.') |
325 |
return False |
|
326 |
||
12.3.7
by Martin Mrazik
Refactoring into class + fixed unit tests |
327 |
review = MergeProposalReview(mp) |
328 |
review.lock() |
|
6
by Martin Mrazik
better error handling when starting a job fails + getting rid of urllib |
329 |
try: |
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
330 |
logger.debug('Starting job: ' + str(jenkins_params)) |
6
by Martin Mrazik
better error handling when starting a job fails + getting rid of urllib |
331 |
j.build_job(jenkins_job, jenkins_params) |
332 |
except jenkins.JenkinsException: |
|
12.6.1
by Allan LeSage
Everyone shares a logging config. |
333 |
logger.debug('Starting a job failed') |
6
by Martin Mrazik
better error handling when starting a job fails + getting rid of urllib |
334 |
message = "ERROR: failed to start a jenkins job" |
335 |
mp.createComment(review_type=LAUNCHPAD_REVIEW_TYPE, |
|
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
336 |
vote='Disapprove', subject=message, |
6
by Martin Mrazik
better error handling when starting a job fails + getting rid of urllib |
337 |
content=message) |
1
by Martin Mrazik
initial commit |
338 |
|
339 |
def trigger_ci_building(self, mps, jenkins_job, jenkins_url): |
|
27.2.5
by Martin Mrazik
propper ret value handling |
340 |
result = True |
1
by Martin Mrazik
initial commit |
341 |
for mp in mps: |
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
342 |
|
35.1.1
by Martin Mrazik
Trigger autolanding if the MP is Approved by trusted person |
343 |
if not self.user_allowed_to_trigger_jobs(mp.registrant): |
1
by Martin Mrazik
initial commit |
344 |
continue
|
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
345 |
|
1
by Martin Mrazik
initial commit |
346 |
if self.testing_in_progress(mp): |
347 |
continue
|
|
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
348 |
|
1
by Martin Mrazik
initial commit |
349 |
if self.latest_candidate_validated(mp): |
350 |
continue
|
|
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
351 |
|
27.2.5
by Martin Mrazik
propper ret value handling |
352 |
ret = self.start_jenkins_job(jenkins_url, jenkins_job, mp) |
353 |
result = ret and result |
|
354 |
return result |
|
1
by Martin Mrazik
initial commit |
355 |
|
356 |
def unapproved_prerequisite_exists(self, mp): |
|
357 |
prereq = mp.prerequisite_branch |
|
358 |
target_branch = mp.target_branch |
|
359 |
if prereq: |
|
360 |
#x is a merge-proposal object
|
|
361 |
merges = [x for x in prereq.landing_targets |
|
362 |
if x.target_branch.web_link == target_branch.web_link and |
|
363 |
x.queue_status != u'Superseded'] |
|
364 |
if len(merges) == 0: |
|
12.6.1
by Allan LeSage
Everyone shares a logging config. |
365 |
logger.debug('No proposals of prerequisite branch exists') |
1
by Martin Mrazik
initial commit |
366 |
return True |
367 |
elif len(merges) > 1: |
|
22.1.1
by Martin Mrazik
Fixed few typos + created tests to avoid similar regressions |
368 |
logger.debug('Too many proposals exists for prerequisite') |
1
by Martin Mrazik
initial commit |
369 |
return True |
370 |
elif len(merges) == 1: |
|
371 |
if merges[0].queue_status != u'Merged': |
|
12.6.1
by Allan LeSage
Everyone shares a logging config. |
372 |
logger.debug('Prerequisite not yet merged') |
1
by Martin Mrazik
initial commit |
373 |
return True |
374 |
#TODO
|
|
375 |
# skip also branches with changes made after approval
|
|
376 |
return False |
|
377 |
||
50.2.1
by Martin Mrazik
Check for commit message before starting the build |
378 |
def is_commit_message_set(self, mp, jenkins_job, jenkins_url): |
379 |
jenkins = self._get_json_jenkins(jenkins_url) |
|
50.2.3
by Martin Mrazik
fixed url |
380 |
job_config = jenkins.get_json_data(jenkins_url + '/job/' + |
381 |
jenkins_job + '/') |
|
50.2.1
by Martin Mrazik
Check for commit message before starting the build |
382 |
use_description = False |
383 |
try: |
|
384 |
for param in job_config['property'][1]['parameterDefinitions']: |
|
385 |
if param['name'] == 'use_description_for_commit' and \ |
|
386 |
param['defaultParameterValue']['value']: |
|
387 |
use_description = True |
|
388 |
break
|
|
389 |
except: |
|
50.2.2
by Martin Mrazik
added tests for is_commit_message_set |
390 |
logger.debug("Unable to get \"use_description_for_commit\" " + |
50.2.1
by Martin Mrazik
Check for commit message before starting the build |
391 |
"configuration for %s" % (jenkins_job)) |
50.2.4
by Martin Mrazik
added some debugging output |
392 |
logger.debug('Job is configured to fallback to description: %s' % |
393 |
(use_description)) |
|
50.2.1
by Martin Mrazik
Check for commit message before starting the build |
394 |
return bool(self.get_commit_message(mp, use_description)) |
395 |
||
1
by Martin Mrazik
initial commit |
396 |
def trigger_al_building(self, mps, jenkins_job, jenkins_url): |
27.2.5
by Martin Mrazik
propper ret value handling |
397 |
result = True |
1
by Martin Mrazik
initial commit |
398 |
for mp in mps: |
44.1.1
by Martin Mrazik
pep8izing |
399 |
#ignore this merge proposal if both registrant and reviewer
|
35.1.4
by Martin Mrazik
added logging when registrand and reviewer are both not allowed to run jobs and also changed the if statement to another equivalent form |
400 |
#are not trusted (aka belong to canonical)
|
35.1.1
by Martin Mrazik
Trigger autolanding if the MP is Approved by trusted person |
401 |
#check if mp.reviewer actually exists just to be sure (it should
|
402 |
# as we are autolanding and thus the MP needs to be approved)
|
|
403 |
if mp.reviewer is None: |
|
404 |
continue
|
|
35.1.4
by Martin Mrazik
added logging when registrand and reviewer are both not allowed to run jobs and also changed the if statement to another equivalent form |
405 |
if not (self.user_allowed_to_trigger_jobs(mp.registrant) or |
406 |
self.user_allowed_to_trigger_jobs(mp.reviewer)): |
|
44.1.1
by Martin Mrazik
pep8izing |
407 |
logger.debug('Skiping this merge proposal as neither ' + |
35.1.4
by Martin Mrazik
added logging when registrand and reviewer are both not allowed to run jobs and also changed the if statement to another equivalent form |
408 |
'registrant nor reviewer is allowed to ' + |
409 |
'trigger jenkins jobs') |
|
1
by Martin Mrazik
initial commit |
410 |
continue
|
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
411 |
|
50.2.1
by Martin Mrazik
Check for commit message before starting the build |
412 |
if not self.is_commit_message_set(mp, jenkins_job, jenkins_url): |
50.2.4
by Martin Mrazik
added some debugging output |
413 |
logger.debug('No commit message was set. Interrupting ' + |
414 |
'autolanding for "%s".' % (mp.web_link)) |
|
50.2.1
by Martin Mrazik
Check for commit message before starting the build |
415 |
mp_state, message, subject = build_state( |
416 |
{'commit_message_empty': True}) |
|
417 |
self.change_mp_status(mp, mp_state, message, subject, |
|
418 |
mp.source_branch.revision_count, |
|
419 |
LaunchpadVote.NEEDS_FIXING) |
|
420 |
continue
|
|
421 |
||
1
by Martin Mrazik
initial commit |
422 |
if self.testing_in_progress(mp): |
423 |
continue
|
|
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
424 |
|
1
by Martin Mrazik
initial commit |
425 |
if self.unapproved_prerequisite_exists(mp): |
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
426 |
logger.debug('Unapproved prerequisite exists for ' + str(mp)) |
1
by Martin Mrazik
initial commit |
427 |
continue
|
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
428 |
|
429 |
logger.debug('Going to trigger merge: ' + str(mp.source_branch)) |
|
27.2.3
by Martin Mrazik
Unauthorized handling for al jobs as well |
430 |
|
27.2.5
by Martin Mrazik
propper ret value handling |
431 |
ret = self.start_jenkins_job(jenkins_url, jenkins_job, mp) |
432 |
result = ret and result |
|
433 |
return result |
|
27.2.6
by Martin Mrazik
missing newline |
434 |
|
1
by Martin Mrazik
initial commit |
435 |
def get_merge_proposals(self, branch_name, state): |
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
436 |
branch = self._launchpad.branches.getByUrl(url=branch_name) |
1
by Martin Mrazik
initial commit |
437 |
if not branch: |
35.1.2
by Martin Mrazik
Explicit export + pep8 compliance |
438 |
logger.debug('Branch "' + branch_name + '" doesn\'t exist') |
1
by Martin Mrazik
initial commit |
439 |
return [] |
22.1.1
by Martin Mrazik
Fixed few typos + created tests to avoid similar regressions |
440 |
mps = [] |
1
by Martin Mrazik
initial commit |
441 |
for mp in branch.landing_candidates: |
442 |
if mp.queue_status in state: |
|
443 |
mps.append(mp) |
|
444 |
return mps |
|
445 |
||
22.2.1
by Martin Mrazik
first attempt to migrate autoland.sh to autoland.py |
446 |
def get_reviews(self, mp): |
447 |
reviews = [] |
|
448 |
for vote in mp.votes: |
|
449 |
if vote.comment: |
|
450 |
reviews.append('%s;%s' % (vote.reviewer.display_name, |
|
451 |
vote.comment.vote)) |
|
452 |
if len(reviews) == 0: |
|
453 |
return None |
|
454 |
||
455 |
return reviews |
|
456 |
||
50.2.1
by Martin Mrazik
Check for commit message before starting the build |
457 |
def get_commit_message(self, mp, use_description): |
458 |
message = None |
|
459 |
if mp.commit_message: |
|
460 |
message = mp.commit_message |
|
461 |
elif use_description and mp.description: |
|
462 |
message = mp.description |
|
463 |
return message |
|
464 |
||
22.2.13
by Martin Mrazik
removed closeBugs.py; improved error handling |
465 |
def close_bugs(self, mp): |
466 |
for b in mp.source_branch.linked_bugs_collection: |
|
467 |
for task in b.bug_tasks_collection: |
|
37.2.1
by Martin Mrazik
fixing bug when unrelated bug tasks were set to fix commited |
468 |
if task.target.web_link == mp.target_branch.project.web_link: |
22.2.13
by Martin Mrazik
removed closeBugs.py; improved error handling |
469 |
task.status = 'Fix Committed' |
470 |
task.lp_save() |