1 # $Id$ |
2 |
3 require 'hudson_api_error' |
4 require 'hudson_exceptions' |
5 require 'rexml_helper' |
6 include RexmlHelper |
7 |
8 class HudsonBuild < ActiveRecord::Base |
9 unloadable |
10 has_many :changesets, :class_name => 'HudsonBuildChangeset', :dependent => :destroy |
11 has_one :test_result, :class_name => 'HudsonBuildTestResult', :dependent => :destroy |
12 has_many :artifacts, :class_name => 'HudsonBuildArtifact', :dependent => :destroy |
13 belongs_to :job, :class_name => 'HudsonJob', :foreign_key => 'hudson_job_id' |
14 belongs_to :author, :class_name => 'User', :foreign_key => 'caused_by' |
15 |
16 # ��������������������������� |
17 validates_presence_of :hudson_job_id, :number |
18 |
19 # ��������������������������� |
20 validates_uniqueness_of :number, :scope => :hudson_job_id |
21 |
22 acts_as_event :title => Proc.new {|o| |
23 retval = "#{l(:label_build)} #{o.job.name} #{o.number}: #{o.result}" unless o.building? |
24 retval = "#{l(:label_build)} #{o.job.name} #{o.number}: #{l(:notice_building)}" if o.building? |
25 retval |
26 }, |
27 :description => Proc.new{|o| |
28 items = [] |
29 items << o.test_result.description_for_activity if o.test_result != nil |
30 items << HudsonBuildChangeset.description_for_activity(o.changesets) if o.changesets.length > 0 |
31 items.join("; ") |
32 }, |
33 :datetime => :finished_at |
34 |
35 acts_as_activity_provider :type => 'hudson', |
36 :timestamp => "#{HudsonBuild.table_name}.finished_at", |
37 :author_key => "#{HudsonBuild.table_name}.caused_by", |
38 :find_options => {:include => {:job => :project}}, |
39 :permission => :view_hudson |
40 |
41 include HudsonHelper |
42 extend RexmlHelper |
43 |
44 def project |
45 return nil unless job |
46 return job.project |
47 end |
48 |
49 def event_url(options ={}) |
50 return url_for(:user) |
51 end |
52 |
53 def url_for(type = :user) |
54 return "" unless self.job |
55 return "" unless self.job.settings |
56 return "" unless (self.job.name && self.job.name.length > 0) |
57 return "#{self.job.settings.url_for(type)}job/#{self.job.name}/#{self.number}" |
58 end |
59 |
60 def building? |
61 return true if "true" == self.building |
62 return false |
63 end |
64 |
65 def update_by_api(elem) |
66 return unless elem |
67 self.number = get_element_value(elem, "number") |
68 self.result = get_element_value(elem, "result") |
69 self.finished_at = Time.at(get_element_value(elem, "timestamp").to_f / 1000) |
70 self.building = get_element_value(elem, "building") |
71 self.caused_by = 1 # Redmine Admin |
72 self.error = "" |
73 end |
74 |
75 def update_by_rss(elem) |
76 info = HudsonBuild.parse_rss(elem) |
77 self.number = info[:number] unless self.number |
78 return unless info[:number].to_i == self.number |
79 self.result = info[:result] |
80 self.finished_at = info[:published] |
81 self.building = info[:building] |
82 self.caused_by = 1 |
83 self.error = "" |
84 end |
85 |
86 def add_changesets_from_xml(element) |
87 element.children.each do |child| |
88 next if child.is_a?(REXML::Text) |
89 next if "changeSet" != child.name |
90 child.children.each do |item| |
91 next if item.is_a?(REXML::Text) |
92 next if "item" != item.name |
93 changeset = new_changeset(item) |
94 changeset.save |
95 self.changesets << changeset |
96 end |
97 end |
98 end |
99 |
100 def add_testresult_from_xml(element) |
101 test_result = nil |
102 element.children.each do |child| |
103 next if child.is_a?(REXML::Text) |
104 next if "action" != child.name |
105 next if "testReport" != get_element_value(child, "urlName") |
106 test_result = new_test_result(child) |
107 test_result.save |
108 self.test_result = test_result |
109 break |
110 end |
111 end |
112 |
113 def add_artifact_from_xml(element) |
114 element.children.each do |child| |
115 next if child.is_a?(REXML::Text) |
116 next if "artifact" != child.name |
117 artifact = new_artifact(child) |
118 artifact.save |
119 self.artifacts << artifact |
120 end |
121 end |
122 |
123 def new_test_result(elem) |
124 retval = HudsonBuildTestResult.new |
125 retval.hudson_build_id = self.id |
126 retval.fail_count = get_element_value(elem, "failCount") |
127 retval.skip_count = get_element_value(elem, "skipCount") |
128 retval.total_count = get_element_value(elem, "totalCount") |
129 return retval |
130 end |
131 |
132 def new_changeset(elem) |
133 retval = HudsonBuildChangeset.new |
134 retval.hudson_build_id = self.id |
135 retval.repository_id = self.project.repository.id |
136 retval.revision = get_revision_no(elem) |
137 return retval |
138 end |
139 |
140 def new_artifact(elem) |
141 retval = HudsonBuildArtifact.new |
142 retval.hudson_build_id = self.id |
143 retval.display_path = get_element_value(elem,"displayPath") |
144 retval.file_name = get_element_value(elem, "fileName") |
145 retval.relative_path = get_element_value(elem, "relativePath") |
146 return retval |
147 end |
148 |
149 def get_revision_no(elem) |
150 retval = get_element_value(elem, "revision") |
151 return retval if retval != "" |
152 retval = get_element_value(elem, "rev") # for mercurial or hudson 1.340 |
153 return retval |
154 end |
155 |
156 end |
157 |
158 def HudsonBuild.count_of(job) |
159 return 0 unless job |
160 return 0 unless job.is_a?(HudsonJob) |
161 return HudsonBuild.count_by_sql(["select count(*) from #{HudsonBuild.table_name} where hudson_job_id = ?", job.id]) |
162 end |
163 |
164 def HudsonBuild.parse_rss(entry) |
165 params = get_element_value(entry, "title").scan(/(.*)#(.*)\s\((.*)\)/)[0] |
166 retval = {} |
167 retval[:name] = params[0].strip |
168 retval[:number] = params[1] |
169 retval[:result] = params[2] |
170 retval[:url] = "#{entry.elements['link'].attributes['href']}" |
171 retval[:published] = Time.xmlschema(get_element_value(entry, "published")).localtime |
172 retval[:building] = "false" |
173 return retval |
174 end |
175 |
176 def HudsonBuild.exists_number?(job_id, number) |
177 |
178 return false unless job_id |
179 return false unless number |
180 |
181 return HudsonBuild.exists?(["#{HudsonBuild.table_name}.hudson_job_id = ? AND #{HudsonBuild.table_name}.number = ?", job_id, number]) |
182 |
183 end |
184 |
185 def HudsonBuild.to_be_updated?(job_id, number) |
186 return !HudsonBuild.exists?(["#{HudsonBuild.table_name}.hudson_job_id = ? AND #{HudsonBuild.table_name}.number = ? AND #{HudsonBuild.table_name}.building = 'false'", job_id, number]) |
187 end |
188 |
189 def HudsonBuild.find_by_changeset(changeset) |
190 return HudsonNoBuild.new() unless changeset |
191 retval = HudsonBuild.find(:all, |
192 :order=>"#{HudsonBuild.table_name}.number", |
193 :conditions=> ["#{HudsonBuildChangeset.table_name}.repository_id = ? and #{HudsonBuildChangeset.table_name}.revision = ?", changeset.repository.id, changeset.revision], |
194 :joins=> "INNER JOIN #{HudsonBuildChangeset.table_name} ON #{HudsonBuildChangeset.table_name}.hudson_build_id = #{HudsonBuild.table_name}.id") |
195 return retval |
196 end |
197 |
198 class HudsonNoBuild |
199 attr_reader :hudson_job_id, :number, :error, :building, :url, :result, :artifacts |
200 |
201 def initialize |
202 @hudson_job_id = "" |
203 @number = "" |
204 @error = "" |
205 @building = "" |
206 @url = "" |
207 @result = "" |
208 @artifacts = [] |
209 end |
210 |
211 def building? |
212 return false |
213 end |
214 |
215 end |