1 # $Id$ |
2 |
3 require 'hudson_build' |
4 require 'hudson_api_error' |
5 require 'hudson_exceptions' |
6 |
7 class HudsonJob < ActiveRecord::Base |
8 unloadable |
9 has_many :health_reports, :class_name => 'HudsonHealthReport', :dependent => :destroy |
10 has_one :job_settings, :class_name => 'HudsonJobSettings', :dependent => :destroy |
11 belongs_to :project, :foreign_key => 'project_id' |
12 belongs_to :settings, :class_name => 'HudsonSettings', :foreign_key => 'hudson_id' |
13 |
14 attr_reader :hudson_api_errors |
15 |
16 # ��������������������������� |
17 validates_presence_of :project_id, :hudson_id, :name |
18 |
19 include HudsonHelper |
20 include RexmlHelper |
21 |
22 def initialize(attributes = nil) |
23 super attributes |
24 self.job_settings = HudsonJobSettings.new |
25 @hudson_api_errors = [] |
26 end |
27 |
28 def after_find |
29 @hudson_api_errors = [] |
30 end |
31 |
32 def after_save |
33 self.job_settings = HudsonJobSettings.new unless self.job_settings |
34 self.job_settings.hudson_job_id = self.id |
35 self.job_settings.save! |
36 end |
37 |
38 def url_for(type = :user) |
39 return "" unless self.settings |
40 return "" unless (self.name && self.name.length > 0) |
41 return "#{self.settings.url_for(type)}job/#{self.name}" |
42 end |
43 |
44 def build_url_for(type = :user) |
45 return "" if url_for(type) == "" |
46 return "#{url_for(type)}/build" |
47 end |
48 |
49 def rss_url_for(type = :user) |
50 return "" if url_for(type) == "" |
51 return "#{url_for(type)}/rssAll" |
52 end |
53 |
54 def config_url_for(type = :user) |
55 return "" if url_for(type) == "" |
56 return "#{url_for(type)}/config.xml" |
57 end |
58 |
59 def api_url_for(type = :user) |
60 return "" if url_for(type) == "" |
61 return "#{url_for(type)}/api" |
62 end |
63 |
64 def get_build(number) |
65 return HudsonNoBuild.new unless number |
66 |
67 retval = HudsonBuild.find(:first, :conditions => ["#{HudsonBuild.table_name}.hudson_job_id = ? AND #{HudsonBuild.table_name}.number = ?", |
68 self.id, number]) |
69 retval = HudsonNoBuild.new unless retval |
70 return retval |
71 end |
72 |
73 def latest_build |
74 latest_build = get_build(self.latest_build_number) |
75 return latest_build |
76 end |
77 |
78 def destroy_builds |
79 HudsonBuild.destroy_all(["#{HudsonBuild.table_name}.hudson_job_id = ?", self.id]) |
80 end |
81 |
82 def update_by_xml(element) |
83 self.description = get_element_value(element, "description") |
84 self.state = get_element_value(element, "color") |
85 latest_build_info = element.elements["lastBuild"] |
86 self.latest_build_number = get_element_value(latest_build_info, "number") if latest_build_info |
87 end |
88 |
89 def update_health_report_by_xml(element) |
90 hr_index = 0 |
91 element.elements.each("healthReport") do |hReport| |
92 report = nil |
93 if hr_index < self.health_reports.length |
94 report = self.health_reports[hr_index] |
95 end |
96 |
97 unless report |
98 report = HudsonHealthReport.new |
99 report.job = self |
100 report.hudson_job_id = self.id |
101 self.health_reports << report |
102 end |
103 |
104 report.update_by_xml(hReport) |
105 report.save |
106 |
107 hr_index += 1 |
108 end |
109 end |
110 |
111 def request_build |
112 clear_hudson_api_errors |
113 api_url = build_url_for(:plugin) |
114 open_hudson_api(api_url, self.settings.auth_user, self.settings.auth_password) |
115 rescue HudsonApiException => error |
116 @hudson_api_errors << HudsonApiError.new(self.class.name, "request_build '#{self.name}'", error) |
117 end |
118 |
119 def fetch_recent_builds |
120 clear_hudson_api_errors |
121 api_uri = rss_url_for(:plugin) |
122 content = open_hudson_api(api_uri, self.settings.auth_user, self.settings.auth_password) |
123 |
124 doc = REXML::Document.new content |
125 retval = [] |
126 doc.elements.each("//entry") do |entry| |
127 buildinfo = HudsonBuild.parse_rss(entry) |
128 retval << buildinfo |
129 end |
130 return retval |
131 rescue HudsonApiException => error |
132 @hudson_api_errors << HudsonApiError.new(self.class.name, "fetch_recent_builds '#{self.name}'", error) |
133 end |
134 |
135 def fetch_builds |
136 clear_hudson_api_errors |
137 |
138 return unless do_fetch? |
139 |
140 fetch_summary unless self.settings.get_build_details |
141 fetch_detail if self.settings.get_build_details |
142 |
143 latest_build = get_build(self.latest_build_number) |
144 add_latest_build if latest_build.is_a? HudsonNoBuild |
145 |
146 rescue HudsonApiException => error |
147 @hudson_api_errors << HudsonApiError.new(self.class.name, "fetch_builds '#{self.name}'", error) |
148 end |
149 |
150 private |
151 def clear_hudson_api_errors |
152 @hudson_api_errors = [] |
153 end |
154 |
155 def fetch_summary |
156 api_url = rss_url_for(:plugin) |
157 begin |
158 content = open_hudson_api(api_url, self.settings.auth_user, self.settings.auth_password) |
159 rescue HudsonApiException => error |
160 raise error |
161 end |
162 |
163 doc = REXML::Document.new content |
164 doc.elements.each("//entry") do |entry| |
165 buildinfo = HudsonBuild.parse_rss(entry) |
166 |
167 next unless HudsonBuildRotator.can_store?(self, buildinfo[:number]) |
168 next unless HudsonBuild.to_be_updated?(self.id, buildinfo[:number]) |
169 |
170 build = get_build(buildinfo[:number]) |
171 |
172 if build.is_a?(HudsonNoBuild) |
173 build = new_build |
174 end |
175 |
176 build.update_by_rss(entry) |
177 build.save |
178 |
179 end |
180 end |
181 |
182 def fetch_detail |
183 |
184 api_url = "#{api_url_for(:plugin)}/xml/?depth=1" |
185 api_url << "&exclude=//build/changeSet/item/path" |
186 api_url << "&exclude=//build/changeSet/item/addedPath" |
187 api_url << "&exclude=//build/changeSet/item/modifiedPath" |
188 api_url << "&exclude=//build/changeSet/item/deletedPath" |
189 api_url << "&exclude=//build/culprit" |
190 api_url << "&exclude=//module" |
191 api_url << "&exclude=//firstBuild&exclude=//lastBuild" |
192 api_url << "&exclude=//lastCompletedBuild" |
193 api_url << "&exclude=//lastFailedBuild" |
194 api_url << "&exclude=//lastStableBuild" |
195 api_url << "&exclude=//lastSuccessfulBuild" |
196 api_url << "&exclude=//downstreamProject" |
197 api_url << "&exclude=//upstreamProject" |
198 content = "" |
199 begin |
200 content = open_hudson_api(api_url, self.settings.auth_user, self.settings.auth_password) |
201 rescue HudsonApiException => error |
202 raise error |
203 end |
204 |
205 begin |
206 doc = REXML::Document.new content |
207 rescue REXML::ParseException => error |
208 raise HudsonApiException.new(error) |
209 end |
210 |
211 doc.elements.each("//build") do |buildelem| |
212 build_number = get_element_value(buildelem, "number") |
213 |
214 next unless HudsonBuildRotator.can_store?(self, build_number) |
215 next unless HudsonBuild.to_be_updated?(self.id, build_number) |
216 |
217 build = get_build(build_number) |
218 |
219 if build.is_a?(HudsonNoBuild) |
220 build = new_build |
221 end |
222 |
223 build.update_by_api(buildelem) |
224 build.save |
225 |
226 # ������������������������������������ |
227 build.add_changesets_from_xml buildelem if self.project.repository != nil |
228 |
229 # ������������������������������ |
230 build.add_testresult_from_xml buildelem |
231 |
232 # ������������������������ |
233 build.add_artifact_from_xml buildelem |
234 |
235 end |
236 |
237 end |
238 |
239 def do_fetch? |
240 latest_build = get_build(self.latest_build_number) |
241 return true if latest_build.is_a? HudsonNoBuild |
242 return true if latest_build.building? |
243 |
244 return false |
245 end |
246 |
247 def add_latest_build |
248 build = new_build |
249 build.number = self.latest_build_number |
250 build.result = "" |
251 build.finished_at = "" |
252 build.building = "true" |
253 build.caused_by = 1 |
254 build.error = "" |
255 build.save |
256 end |
257 |
258 def new_build |
259 retval = HudsonBuild.new() |
260 retval.job = self |
261 retval.hudson_job_id = self.id |
262 return retval |
263 end |
264 |
265 end |
266 |
267 class HudsonNoJob |
268 attr_reader :settings, :hudson_id, :project_id, :id, :name, :latest_build_number, :created_at, :updated_at, :description, :state, :job_settings |
269 |
270 def initialize(args = nil) |
271 @id = "" |
272 @project_id = "" |
273 @name = "" |
274 @latest_build_number = "" |
275 @created_at = "" |
276 @updated_at = "" |
277 @description = "" |
278 @state = "" |
279 @job_settings = nil |
280 @settings = nil |
281 |
282 return unless args |
283 |
284 @settings = args[:settings] |
285 @name = args[:name] |
286 |
287 end |
288 |
289 def url_for(type = :user) |
290 return "" unless self.settings |
291 return "" unless (self.name && self.name.length > 0) |
292 return "#{self.settings.url_for(type)}job/#{self.name}" |
293 end |
294 |
295 end |
296 |