Merge lp:~afrantzis/jenkins-launchpad-plugin/triggered-builds into lp:jenkins-launchpad-plugin

Proposed by Alexandros Frantzis
Status: Merged
Approved by: Michał Sawicz
Approved revision: 133
Merged at revision: 129
Proposed branch: lp:~afrantzis/jenkins-launchpad-plugin/triggered-builds
Merge into: lp:jenkins-launchpad-plugin
Diff against target: 244 lines (+130/-3)
3 files modified
jlp/jenkinsutils.py (+57/-1)
tests/__init__.py (+56/-1)
tests/test_jenkinsutils.py (+17/-1)
To merge this branch: bzr merge lp:~afrantzis/jenkins-launchpad-plugin/triggered-builds
Reviewer Review Type Date Requested Status
Michał Sawicz Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+284117@code.launchpad.net

Commit message

Include triggered builds in reported downstream builds when creating MP vote message.

This is required because new versions of the triggered build plugin don't report triggered builds as downstream.

Description of the change

Include triggered builds in reported downstream builds when creating MP vote message.

This is required because new versions of the triggered build plugin don't report triggered builds as downstream.

To test locally run the following script from within the project directory:

import jlp.jenkinsutils
import sys

print(jlp.jenkinsutils.get_executed_test_runs_message(sys.argv[1]))

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
130. By Alexandros Frantzis

Fix PEP8 complaints

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
131. By Alexandros Frantzis

More PEP8 fixes for trusty

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
132. By Alexandros Frantzis

Fix variable initialization

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michał Sawicz (saviq) :
review: Needs Fixing
133. By Alexandros Frantzis

Added comment for upstream project check

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michał Sawicz (saviq) wrote :

Replying to self: set() could break ordering, so using lists is better here.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'jlp/jenkinsutils.py'
2--- jlp/jenkinsutils.py 2016-01-08 11:09:40 +0000
3+++ jlp/jenkinsutils.py 2016-02-05 12:29:45 +0000
4@@ -59,6 +59,12 @@
5 return all_params
6
7
8+def _deduplicate_builds(seq):
9+ """ Internal method to remove duplicate builds from a build list"""
10+ seen = set()
11+ return [x for x in seq if str(x) not in seen and not seen.add(str(x))]
12+
13+
14 def get_running_builds(job, job_params={}):
15 """ For a given jenkins job return a list of currently active builds
16 :param job: name of the jenkins job
17@@ -446,6 +452,49 @@
18 return builds
19
20
21+def get_triggered_builds(jenkins, jenkins_url, job_name, build_number):
22+ """For a given build find all the triggered builds and return them in
23+ a list where each element is of this form:
24+ {
25+ 'project':
26+ 'number':
27+ 'url':
28+ 'result':
29+ }
30+ """
31+ return_data = []
32+
33+ json_request = jenkins_url + job_name + '/' + str(build_number) +\
34+ '/api/json?depth=2&tree=actions[triggeredBuilds[' +\
35+ 'number,url,result,actions[causes[upstreamBuild,' +\
36+ 'upstreamProject]]]]'
37+ triggered_builds = jenkins.get_json_data(json_request, append_api=False)
38+
39+ for action in triggered_builds.get('actions', []):
40+ for triggered_build in action.get('triggeredBuilds', []):
41+ causes = {}
42+ for triggered_action in triggered_build.get('actions', []):
43+ if 'causes' in triggered_action:
44+ causes = triggered_action['causes'][0]
45+ break
46+
47+ # Ensure the build was triggered by the correct upstream project.
48+ # This avoids including older builds that are erroneously listed
49+ # by jenkins in some cases (e.g., for rebuilds).
50+ if (causes.get('upstreamProject', None) != job_name or
51+ causes.get('upstreamBuild', None) != build_number):
52+ continue
53+
54+ tb_jenkins_url, tb_job_name, tb_build_number = \
55+ parse_jenkins_url(triggered_build['url'])
56+ return_data.append({'project': tb_job_name,
57+ 'number': triggered_build['number'],
58+ 'url': normalize_url(triggered_build['url']),
59+ 'result': triggered_build['result']})
60+
61+ return return_data
62+
63+
64 def get_downstream_builds(jenkins, jenkins_url, job_name, build_number,
65 depth=0):
66 """For a given build find all the downstream builds and return them in
67@@ -467,6 +516,13 @@
68 job_url = jenkins_url + job_name
69 downstream_projects = get_downstream_projects(jenkins, job_url)
70 return_data = []
71+
72+ # Get the triggered builds which in newer versions of the relevant plugin
73+ # are not included as normal downstream builds
74+ return_data += \
75+ get_triggered_builds(jenkins, jenkins_url, job_name, build_number)
76+
77+ # Add normal downstream builds
78 json_request = '/api/json?depth=2&tree=builds[number,url,result,' + \
79 'actions[causes[upstreamBuild,upstreamProject]]]'
80 for project in downstream_projects:
81@@ -508,7 +564,7 @@
82 return_data = [p for p in return_data
83 if not p['project'] in jobs_blacklisted_from_messages]
84
85- return return_data
86+ return _deduplicate_builds(return_data)
87
88
89 def get_downstream_projects(jenkins, job_url):
90
91=== modified file 'tests/__init__.py'
92--- tests/__init__.py 2013-03-13 15:08:30 +0000
93+++ tests/__init__.py 2016-02-05 12:29:45 +0000
94@@ -3,6 +3,8 @@
95 job_data = {
96 'downstreamProjects': [{'name': 'job-multiconfig'},
97 {'name': 'job-freestyle'}]}
98+ job_build_data = {}
99+
100 job_multiconfig_build_data = {
101 u'builds': [{u'actions': [{u'causes':
102 [{u'upstreamBuild': 3,
103@@ -76,7 +78,20 @@
104 u'result': u'UNSTABLE',
105 u'url': u'http://10.0.0.1:8080/job/' +
106 'freestyle-downstream/12/'}],
107- u'artifacts': []}
108+ u'artifacts': [],
109+ u'actions': [
110+ {u'triggeredBuilds': [{
111+ u'actions': [{
112+ u'causes': [{
113+ u'upstreamBuild': 12,
114+ u'upstreamProject':
115+ u'freestyle-downstream'}]},
116+ {}],
117+ u'number': 66,
118+ 'artifacts': [],
119+ u'result': u'FAILURE',
120+ u'url': u'http://10.0.0.1:8080/job/' +
121+ 'freestyle-triggered/66/'}]}]}
122
123 job_freestyle_downstream_data = {
124 'downstreamProjects': []}
125@@ -85,11 +100,22 @@
126 'result': 'UNSTABLE',
127 'artifacts': []}
128
129+ job_freestyle_triggered_data = {
130+ 'downstreamProjects': []}
131+
132+ job_freestyle_triggered_run_data = {
133+ 'result': 'FAILURE',
134+ 'artifacts': []}
135+
136+ job_freestyle_triggered_build_data = {}
137+
138 def get_json_data(self, url, append_api=True):
139 url = url.rstrip('/')
140 print url
141 if url == 'http://10.0.0.1:8080/job/my-job':
142 return self.job_data
143+ elif url == 'http://10.0.0.1:8080/job/my-job/3':
144+ return self.job_run_data
145 elif url == 'http://10.0.0.1:8080/job/job-multiconfig':
146 return self.job_multiconfig_data
147 elif url == 'http://10.0.0.1:8080/job/job-multiconfig/3':
148@@ -100,6 +126,10 @@
149 return self.job_freestyle_downstream_data
150 elif url == 'http://10.0.0.1:8080/job/freestyle-downstream/12':
151 return self.job_freestyle_downstream_run_data
152+ elif url == 'http://10.0.0.1:8080/job/freestyle-triggered':
153+ return self.job_freestyle_triggered_data
154+ elif url == 'http://10.0.0.1:8080/job/freestyle-triggered/66':
155+ return self.job_freestyle_triggered_run_data
156 elif url == 'http://10.0.0.1:8080/job/job-freestyle/7':
157 return self.job_freestyle_build_data
158 elif url == 'http://10.0.0.1:8080/job/job-multiconfig/' + \
159@@ -120,3 +150,28 @@
160 'json?depth=2&tree=builds[number,url,result,' +\
161 'actions[causes[upstreamBuild,upstreamProject]]]':
162 return self.job_freestyle_downstream_build_data
163+ elif url == 'http://10.0.0.1:8080/job/my-job/3' +\
164+ '/api/json?depth=2&tree=actions[triggeredBuilds[' +\
165+ 'number,url,result,actions[causes[upstreamBuild,' +\
166+ 'upstreamProject]]]]':
167+ return self.job_build_data
168+ elif url == 'http://10.0.0.1:8080/job/job-multiconfig/3' +\
169+ '/api/json?depth=2&tree=actions[triggeredBuilds[' +\
170+ 'number,url,result,actions[causes[upstreamBuild,' +\
171+ 'upstreamProject]]]]':
172+ return self.job_multiconfig_build_data
173+ elif url == 'http://10.0.0.1:8080/job/job-freestyle/7' +\
174+ '/api/json?depth=2&tree=actions[triggeredBuilds[' +\
175+ 'number,url,result,actions[causes[upstreamBuild,' +\
176+ 'upstreamProject]]]]':
177+ return self.job_freestyle_build_data
178+ elif url == 'http://10.0.0.1:8080/job/freestyle-downstream/12' +\
179+ '/api/json?depth=2&tree=actions[triggeredBuilds[' +\
180+ 'number,url,result,actions[causes[upstreamBuild,' +\
181+ 'upstreamProject]]]]':
182+ return self.job_freestyle_downstream_build_data
183+ elif url == 'http://10.0.0.1:8080/job/freestyle-triggered/66' +\
184+ '/api/json?depth=2&tree=actions[triggeredBuilds[' +\
185+ 'number,url,result,actions[causes[upstreamBuild,' +\
186+ 'upstreamProject]]]]':
187+ return self.job_freestyle_triggered_build_data
188
189=== modified file 'tests/test_jenkinsutils.py'
190--- tests/test_jenkinsutils.py 2016-01-08 11:09:40 +0000
191+++ tests/test_jenkinsutils.py 2016-02-05 12:29:45 +0000
192@@ -542,6 +542,10 @@
193 'number': 12,
194 'url': 'http://10.0.0.1:8080/job/freestyle-downstream/12/',
195 'result': 'UNSTABLE', },
196+ {'project': 'freestyle-triggered',
197+ 'number': 66,
198+ 'url': 'http://10.0.0.1:8080/job/freestyle-triggered/66/',
199+ 'result': 'FAILURE', },
200 ]
201 self.assertEqual(ret, expected)
202
203@@ -567,6 +571,10 @@
204 'number': 12,
205 'url': 'http://10.0.0.1:8080/job/freestyle-downstream/12/',
206 'result': 'UNSTABLE', },
207+ {'project': 'freestyle-triggered',
208+ 'number': 66,
209+ 'url': 'http://10.0.0.1:8080/job/freestyle-triggered/66/',
210+ 'result': 'FAILURE', },
211 ]
212 self.assertEqual(ret, expected)
213
214@@ -585,6 +593,10 @@
215 'number': 7,
216 'url': 'http://10.0.0.1:8080/job/job-freestyle/7/',
217 'result': 'SUCCESS', },
218+ {'project': 'freestyle-triggered',
219+ 'number': 66,
220+ 'url': 'http://10.0.0.1:8080/job/freestyle-triggered/66/',
221+ 'result': 'FAILURE', },
222 ]
223 self.assertEqual(ret, expected)
224
225@@ -637,7 +649,10 @@
226 'result': u'SUCCESS', },
227 {'output': 'http://10.0.0.1:8080/job/freestyle-downstream/' +
228 '12/console',
229- 'result': 'UNSTABLE'}
230+ 'result': 'UNSTABLE'},
231+ {'output': 'http://10.0.0.1:8080/job/freestyle-triggered/' +
232+ '66/console',
233+ 'result': 'FAILURE'},
234 ]
235 self.assertEqual(ret, expected)
236
237@@ -1084,6 +1099,7 @@
238 deb: http://{hostname}/job/job-freestyle/7/artifact/work/""" +
239 """output/*zip*/output.zip
240 UNSTABLE: http://{hostname}/job/freestyle-downstream/12
241+ FAILURE: http://{hostname}/job/freestyle-triggered/66/console
242 Coverity artifacts:
243 http://{hostname}/job/job-multiconfig/./distribution=""" +
244 """raring,flavor=amd64/3/artifact/results/coverity/CID_10895.html

Subscribers

People subscribed via source and target branches