hudson-remote-api 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -12,6 +12,9 @@ Hudson[:url] = 'http://localhost:8080'
12
12
  Hudson[:user] = 'hudson'
13
13
  Hudson[:password] = 'password'
14
14
 
15
+ # To turn off checking for crumbIssuer
16
+ Hudson[:crumb] = false
17
+
15
18
  # List all Hudson jobs
16
19
  Hudson::Job.list
17
20
 
@@ -26,3 +29,22 @@ puts j.last_build
26
29
  j.repository_url = { :url => 'https://github.com/beeplove/hudson-remote-api-mkhan.git', :branch => 'origin/master' }
27
30
  # or, only to change branch
28
31
  j.repository_url = { :branch => 'origin/master' }
32
+
33
+ # Set build trigger
34
+ j.triggers = { 'hudson.triggers.SCMTrigger' => '* * * * *'}
35
+ # or, using shortcut
36
+ j.triggers = { 'SCMTrigger' => '* * * * *', 'TimerTrigger' => '0 22 * * *'}
37
+
38
+ # To add or update a trigger in existing triggers
39
+ j.triggers = j.triggers.merge({ 'hudson.triggers.TimerTrigger' => '0 22 * * *'})
40
+ # Avoid using shortcut form when to edit a trigger in existing triggers
41
+
42
+ # To delete existing triggers
43
+ j.triggers = {}
44
+ # or,
45
+ j.triggers = nil
46
+
47
+ # To view current trigger
48
+ j.triggers
49
+ # would return hash containing trigger name in key and trigger spec in value.
50
+ # Example of returned hash: {"hudson.triggers.TimerTrigger"=>"0 22 * * *", "hudson.triggers.SCMTrigger"=>"* * * * *"}
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.0
1
+ 0.6.0
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "hudson-remote-api"
8
- s.version = "0.5.0"
8
+ s.version = "0.6.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Dru Ibarra"]
12
- s.date = "2012-06-26"
12
+ s.date = "2012-07-14"
13
13
  s.description = "Connect to Hudson's remote web API"
14
14
  s.email = "Druwerd@gmail.com"
15
15
  s.extra_rdoc_files = [
@@ -105,24 +105,25 @@ module Hudson
105
105
  end
106
106
 
107
107
  def self.fetch_crumb
108
- body = get_xml(url_for '/crumbIssuer/api/xml')
109
- doc = REXML::Document.new(body)
110
-
111
- crumbValue = doc.elements['/defaultCrumbIssuer/crumb'] or begin
112
- $stderr.puts "Failure fetching crumb value from server"
113
- return
114
- end
115
-
116
- crumbName = doc.elements['/defaultCrumbIssuer/crumbRequestField'] or begin
117
- $stderr.puts "Failure fetching crumb field name from server"
118
- return
108
+ if Hudson[:crumb]
109
+ body = get_xml(url_for '/crumbIssuer/api/xml')
110
+ doc = REXML::Document.new(body)
111
+
112
+ crumbValue = doc.elements['/defaultCrumbIssuer/crumb'] or begin
113
+ $stderr.puts "Failure fetching crumb value from server"
114
+ return
115
+ end
116
+
117
+ crumbName = doc.elements['/defaultCrumbIssuer/crumbRequestField'] or begin
118
+ $stderr.puts "Failure fetching crumb field name from server"
119
+ return
120
+ end
121
+
122
+ @@apiCrumb = Struct.new(:name,:value).new(crumbName.text,crumbValue.text)
119
123
  end
120
-
121
- @@apiCrumb = Struct.new(:name,:value).new(crumbName.text,crumbValue.text)
122
124
  rescue
123
125
  $stderr.puts "Failure fetching crumb xml"
124
126
  end
125
-
126
127
  end
127
128
  end
128
129
 
@@ -2,7 +2,7 @@ require File.dirname(__FILE__) + '/multicast.rb'
2
2
 
3
3
  module Hudson
4
4
  # set default settings
5
- @@settings = {:url => 'http://localhost:8080', :user => nil, :password => nil, :version => nil}
5
+ @@settings = {:url => 'http://localhost:8080', :user => nil, :password => nil, :version => nil, :crumb => true}
6
6
 
7
7
  def self.[](param)
8
8
  return @@settings[param]
@@ -1,10 +1,7 @@
1
1
  module Hudson
2
2
  # This class provides an interface to Hudson jobs
3
3
  class Job < HudsonObject
4
-
5
4
  attr_accessor :name, :config, :repository_url, :repository_urls, :repository_browser_location, :description
6
- attr_reader :color, :last_build, :last_completed_build, :last_failed_build, :last_stable_build, :last_successful_build, :last_unsuccessful_build, :next_build_number
7
- attr_reader :builds_list
8
5
 
9
6
  SVN_SCM_CONF = <<-SVN_SCM_STRING
10
7
  <scm class="hudson.scm.SubversionSCM">
@@ -23,59 +20,58 @@ SVN_SCM_CONF = <<-SVN_SCM_STRING
23
20
  </scm>
24
21
  SVN_SCM_STRING
25
22
 
26
-
27
- # List all Hudson jobs
28
- def self.list()
29
- xml = get_xml(@@hudson_xml_api_path)
30
-
31
- jobs = []
32
- jobs_doc = REXML::Document.new(xml)
33
- jobs_doc.each_element("hudson/job") do |job|
34
- jobs << job.elements["name"].text
35
- end
36
- jobs
37
- end
38
-
39
- # List all jobs in active execution
40
- def self.list_active
41
- xml = get_xml(@@hudson_xml_api_path)
42
-
43
- active_jobs = []
44
- jobs_doc = REXML::Document.new(xml)
45
- jobs_doc.each_element("hudson/job") do |job|
46
- if job.elements["color"].text.include?("anime")
47
- active_jobs << job.elements["name"].text
48
- end
49
- end
50
- active_jobs
51
- end
52
-
53
- def self.get(job_name)
54
- job_name.strip!
55
- if list.include?(job_name)
56
- Job.new(job_name)
57
- else
58
- nil
23
+ # Class methods
24
+ class <<self
25
+ # List all Hudson jobs
26
+ def list()
27
+ xml = get_xml(@@hudson_xml_api_path)
28
+
29
+ jobs = []
30
+ jobs_doc = REXML::Document.new(xml)
31
+ jobs_doc.each_element("hudson/job") do |job|
32
+ jobs << job.elements["name"].text
33
+ end
34
+ jobs
35
+ end
36
+
37
+ # List all jobs in active execution
38
+ def list_active
39
+ xml = get_xml(@@hudson_xml_api_path)
40
+
41
+ active_jobs = []
42
+ jobs_doc = REXML::Document.new(xml)
43
+ jobs_doc.each_element("hudson/job") do |job|
44
+ if job.elements["color"].text.include?("anime")
45
+ active_jobs << job.elements["name"].text
46
+ end
47
+ end
48
+ active_jobs
49
+ end
50
+
51
+ def get(job_name)
52
+ job_name.strip!
53
+ list.include?(job_name) ? Job.new(job_name) : nil
59
54
  end
55
+
56
+ def create(name, config=nil)
57
+ config ||= File.open(File.dirname(__FILE__) + '/new_job_config.xml').read
58
+
59
+ response = send_post_request(@@xml_api_create_item_path, {:name=>name, :mode=>"hudson.model.FreeStyleProject", :config=>config})
60
+ raise(APIError, "Error creating job #{name}: #{response.body}") if response.class != Net::HTTPFound
61
+ Job.get(name)
62
+ end
63
+
60
64
  end
61
-
65
+
66
+ # Instance methods
62
67
  def initialize(name, config=nil)
63
68
  name.strip!
64
69
  Hudson::Job.fetch_crumb
65
- if Job.list.include?(name) # job already in Jenkins
66
- @name = name
67
- load_xml_api
68
- load_config
69
- load_info
70
- self
71
- else
72
- j = Job.create(name, config)
73
- @name = j.name
74
- load_xml_api
75
- load_config
76
- load_info
77
- self
78
- end
70
+ # Creates the job in Hudson if it doesn't already exist
71
+ @name = Job.list.include?(name) ? name : Job.create(name, config).name
72
+ load_xml_api
73
+ load_config
74
+ self
79
75
  end
80
76
 
81
77
  def load_xml_api
@@ -92,6 +88,9 @@ SVN_SCM_STRING
92
88
  def load_config
93
89
  @config = get_xml(@xml_api_config_path)
94
90
  @config_doc = REXML::Document.new(@config)
91
+
92
+ @info = get_xml(@xml_api_path)
93
+ @info_doc = REXML::Document.new(@info)
95
94
 
96
95
  @repository_urls = []
97
96
  if @config_doc.elements["/project/description"]
@@ -123,25 +122,46 @@ SVN_SCM_STRING
123
122
  end
124
123
  end
125
124
 
126
- def load_info()
127
- @info = get_xml(@xml_api_path)
128
- @info_doc = REXML::Document.new(@info)
129
-
130
- if @info_doc.elements["/freeStyleProject"]
131
- @color = @info_doc.elements["/freeStyleProject/color"].text if @info_doc.elements["/freeStyleProject/color"]
132
- @last_build = @info_doc.elements["/freeStyleProject/lastBuild/number"].text if @info_doc.elements["/freeStyleProject/lastBuild/number"]
133
- @last_completed_build = @info_doc.elements["/freeStyleProject/lastCompletedBuild/number"].text if @info_doc.elements["/freeStyleProject/lastCompletedBuild/number"]
134
- @last_failed_build = @info_doc.elements["/freeStyleProject/lastFailedBuild/number"].text if @info_doc.elements["/freeStyleProject/lastFailedBuild/number"]
135
- @last_stable_build = @info_doc.elements["/freeStyleProject/lastStableBuild/number"].text if @info_doc.elements["/freeStyleProject/lastStableBuild/number"]
136
- @last_successful_build = @info_doc.elements["/freeStyleProject/lastSuccessfulBuild/number"].text if @info_doc.elements["/freeStyleProject/lastSuccessfulBuild/number"]
137
- @last_unsuccessful_build = @info_doc.elements["/freeStyleProject/lastUnsuccessfulBuild/number"].text if @info_doc.elements["/freeStyleProject/lastUnsuccessfulBuild/number"]
138
- @next_build_number = @info_doc.elements["/freeStyleProject/nextBuildNumber"].text if @info_doc.elements["/freeStyleProject/nextBuildNumber"]
139
- end
140
-
141
- if !@info_doc.elements["/freeStyleProject/build"].nil?
142
- @builds_list = []
143
- @info_doc.elements.each("/freeStyleProject/build"){|e| @builds_list << e.elements["number"].text }
144
- end
125
+ def free_style_project?
126
+ !@info_doc.elements["/freeStyleProject"].nil?
127
+ end
128
+
129
+ def color
130
+ @info_doc.elements["/freeStyleProject/color"].text if free_style_project? && @info_doc.elements["/freeStyleProject/color"]
131
+ end
132
+
133
+ def last_build
134
+ @info_doc.elements["/freeStyleProject/lastBuild/number"].text if free_style_project? && @info_doc.elements["/freeStyleProject/lastBuild/number"]
135
+ end
136
+
137
+ def last_completed_build
138
+ @info_doc.elements["/freeStyleProject/lastCompletedBuild/number"].text if free_style_project? && @info_doc.elements["/freeStyleProject/lastCompletedBuild/number"]
139
+ end
140
+
141
+ def last_failed_build
142
+ @info_doc.elements["/freeStyleProject/lastFailedBuild/number"].text if free_style_project? && @info_doc.elements["/freeStyleProject/lastFailedBuild/number"]
143
+ end
144
+
145
+ def last_stable_build
146
+ @info_doc.elements["/freeStyleProject/lastStableBuild/number"].text if free_style_project? && @info_doc.elements["/freeStyleProject/lastStableBuild/number"]
147
+ end
148
+
149
+ def last_successful_build
150
+ @info_doc.elements["/freeStyleProject/lastSuccessfulBuild/number"].text if free_style_project? && @info_doc.elements["/freeStyleProject/lastSuccessfulBuild/number"]
151
+ end
152
+
153
+ def last_unsuccessful_build
154
+ @info_doc.elements["/freeStyleProject/lastUnsuccessfulBuild/number"].text if free_style_project? && @info_doc.elements["/freeStyleProject/lastUnsuccessfulBuild/number"]
155
+ end
156
+
157
+ def next_build_number
158
+ @info_doc.elements["/freeStyleProject/nextBuildNumber"].text if free_style_project? && @info_doc.elements["/freeStyleProject/nextBuildNumber"]
159
+ end
160
+
161
+ def builds_list
162
+ builds_list = []
163
+ @info_doc.elements.each("/freeStyleProject/build"){|e| builds_list << e.elements["number"].text } unless @info_doc.elements["/freeStyleProject/build"].nil?
164
+ builds_list
145
165
  end
146
166
 
147
167
  def active?
@@ -156,14 +176,6 @@ SVN_SCM_STRING
156
176
  end
157
177
  end
158
178
 
159
- def self.create(name, config=nil)
160
- config = File.open(File.dirname(__FILE__) + '/new_job_config.xml').read if config.nil?
161
-
162
- response = send_post_request(@@xml_api_create_item_path, {:name=>name, :mode=>"hudson.model.FreeStyleProject", :config=>config})
163
- raise(APIError, "Error creating job #{name}: #{response.body}") if response.class != Net::HTTPFound
164
- Job.get(name)
165
- end
166
-
167
179
  # Create a new job on Hudson server based on the current job object
168
180
  def copy(new_job=nil)
169
181
  new_job = "copy_of_#{@name}" if new_job.nil?
@@ -202,7 +214,6 @@ SVN_SCM_STRING
202
214
  end
203
215
 
204
216
  @config = @config_doc.to_s
205
-
206
217
  update
207
218
  end
208
219
 
@@ -242,6 +253,51 @@ SVN_SCM_STRING
242
253
  update
243
254
  end
244
255
 
256
+ def generate_trigger trigger, spec_text
257
+ spec = REXML::Element.new("spec")
258
+ spec.text = spec_text.to_s
259
+ trigger.elements << spec
260
+ trigger
261
+ end
262
+ private :generate_trigger
263
+
264
+ def triggers= opts={}
265
+ opts = {} if opts.nil?
266
+ if triggers = @config_doc.elements["/project/triggers[@class='vector']"]
267
+ triggers.elements.delete_all '*'
268
+ opts.each do |key, value|
269
+ trigger_name = key.to_s
270
+ trigger_name = 'hudson.triggers.' + trigger_name unless Regexp.new(/^hudson\.triggers\./).match(trigger_name)
271
+ if trigger = triggers.elements[trigger_name]
272
+ if spec = trigger.elements['spec']
273
+ spec.text = value.to_s
274
+ else
275
+ triggers.elements << generate_trigger(trigger, value)
276
+ end
277
+ else
278
+ triggers.elements << generate_trigger(REXML::Element.new(trigger_name), value)
279
+ end
280
+ end
281
+ # Todo: before calling update, @config need to be assigned with @config_doc.to_s,
282
+ # let it be done by update.
283
+ @config = @config_doc.to_s
284
+ update
285
+ else
286
+ $stderr.puts "triggers not found in configuration, triggers assignment ignored."
287
+ end
288
+ end
289
+
290
+ def triggers
291
+ results = {}
292
+ if triggers = @config_doc.elements["/project/triggers[@class='vector']"]
293
+ triggers.elements.to_a.each do |trigger|
294
+ spec_text = trigger.elements['spec'].text
295
+ results[trigger.name.to_s] = spec_text.to_s
296
+ end
297
+ end
298
+ results
299
+ end
300
+
245
301
  def url
246
302
  File.join( Hudson[:url], 'job', name) + '/'
247
303
  end
@@ -21,10 +21,17 @@ class TestHudsonConfig < Test::Unit::TestCase
21
21
  assert_equal(Hudson[:user], "test")
22
22
  assert_equal(Hudson[:password], "test")
23
23
  assert_equal(Hudson[:version], "1.00")
24
+ assert_equal(Hudson[:crumb], true)
24
25
  end
25
26
 
26
27
  def test_auto_config
27
28
  assert_nothing_thrown{ Hudson.auto_config }
28
29
  end
30
+
31
+ def test_when_crumb_is_false
32
+ new_settings = {:url => 'test.com', :user => 'test', :password => 'test', :version => '1.00', :crumb => false}
33
+ Hudson.settings = new_settings
34
+ assert_equal(Hudson[:crumb], false)
35
+ end
29
36
 
30
- end
37
+ end
@@ -22,6 +22,7 @@ class TestHudsonJob < Test::Unit::TestCase
22
22
  new_job = Hudson::Job.create(new_job_name)
23
23
  assert new_job
24
24
  assert_equal(new_job.name, new_job_name)
25
+ assert_equal(true, new_job.triggers.empty?, "New job should have empty triggers")
25
26
  assert new_job.delete
26
27
  end
27
28
 
@@ -81,4 +82,37 @@ class TestHudsonJob < Test::Unit::TestCase
81
82
  job = Hudson::Job.get("test_job")
82
83
  assert job.builds_list.kind_of?(Array)
83
84
  end
85
+
86
+ def test_triggers_set
87
+ job_name = 'build_triggers'
88
+ job = Hudson::Job.create(job_name)
89
+
90
+ job.triggers = { "hudson.triggers.SCMTrigger" => '* * * * *' }
91
+ assert_equal(1, job.triggers.size, "Failed to set triggers with 1 trigger.")
92
+ assert_equal({"hudson.triggers.SCMTrigger" => '* * * * *'}, job.triggers, "Failed to set triggers with 1 trigger.")
93
+
94
+ assert job.delete
95
+ end
96
+
97
+ def test_triggers_set_using_shortcut
98
+ job_name = 'build_triggers'
99
+ job = Hudson::Job.create(job_name)
100
+
101
+ job.triggers = { "SCMTrigger" => '* * * * *', 'TimerTrigger' => '0 22 * * *' }
102
+ assert_equal(2, job.triggers.size, "Failed to set triggers using shortcut.")
103
+ assert_equal({"hudson.triggers.SCMTrigger"=>"* * * * *", "hudson.triggers.TimerTrigger"=>"0 22 * * *"}, job.triggers, "Failed to set triggers using shortcut.")
104
+
105
+ assert job.delete
106
+ end
107
+
108
+ def test_triggers_delete
109
+ job_name = 'build_triggers'
110
+ job = Hudson::Job.create(job_name)
111
+
112
+ job.triggers = { "SCMTrigger" => '* * * * *', 'TimerTrigger' => '0 22 * * *' }
113
+ job.triggers = {}
114
+ assert_equal(true, job.triggers.empty?, "Failed to delete triggers.")
115
+
116
+ assert job.delete
117
+ end
84
118
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hudson-remote-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-06-26 00:00:00.000000000 Z
12
+ date: 2012-07-14 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: Connect to Hudson's remote web API
15
15
  email: Druwerd@gmail.com