hudson-remote-api 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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