jenkins-remote-api_1 1.0.3

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.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/Gemfile +10 -0
  4. data/Gemfile.lock +27 -0
  5. data/LICENSE +19 -0
  6. data/README.md +177 -0
  7. data/Rakefile +10 -0
  8. data/fixtures/vcr_cassettes/TestHudsonBuildQueue_test_list.yml +38 -0
  9. data/fixtures/vcr_cassettes/TestHudsonBuild_setup.yml +407 -0
  10. data/fixtures/vcr_cassettes/TestHudsonBuild_test_build_info.yml +222 -0
  11. data/fixtures/vcr_cassettes/TestHudsonClient_test_build_job_.yml +52 -0
  12. data/fixtures/vcr_cassettes/TestHudsonClient_test_build_job_with_parameters_.yml +52 -0
  13. data/fixtures/vcr_cassettes/TestHudsonClient_test_build_queue_info.yml +38 -0
  14. data/fixtures/vcr_cassettes/TestHudsonClient_test_create_item_.yml +52 -0
  15. data/fixtures/vcr_cassettes/TestHudsonClient_test_delete_job_.yml +52 -0
  16. data/fixtures/vcr_cassettes/TestHudsonClient_test_job_build_info.yml +39 -0
  17. data/fixtures/vcr_cassettes/TestHudsonClient_test_job_config_info.yml +48 -0
  18. data/fixtures/vcr_cassettes/TestHudsonJob_test_build.yml +409 -0
  19. data/fixtures/vcr_cassettes/TestHudsonJob_test_build_with_params.yml +189 -0
  20. data/fixtures/vcr_cassettes/TestHudsonJob_test_builds_list.yml +192 -0
  21. data/fixtures/vcr_cassettes/TestHudsonJob_test_copy.yml +410 -0
  22. data/fixtures/vcr_cassettes/TestHudsonJob_test_create.yml +257 -0
  23. data/fixtures/vcr_cassettes/TestHudsonJob_test_desc_update.yml +281 -0
  24. data/fixtures/vcr_cassettes/TestHudsonJob_test_get.yml +191 -0
  25. data/fixtures/vcr_cassettes/TestHudsonJob_test_job_with_spaces.yml +260 -0
  26. data/fixtures/vcr_cassettes/TestHudsonJob_test_list.yml +39 -0
  27. data/fixtures/vcr_cassettes/TestHudsonJob_test_list_active.yml +39 -0
  28. data/fixtures/vcr_cassettes/TestHudsonJob_test_new.yml +561 -0
  29. data/fixtures/vcr_cassettes/TestHudsonJob_test_scm_url.yml +728 -0
  30. data/fixtures/vcr_cassettes/TestHudsonJob_test_triggers_delete.yml +588 -0
  31. data/fixtures/vcr_cassettes/TestHudsonJob_test_triggers_set.yml +346 -0
  32. data/fixtures/vcr_cassettes/TestHudsonJob_test_triggers_set_using_shortcut.yml +346 -0
  33. data/fixtures/vcr_cassettes/TestHudsonJob_test_url.yml +156 -0
  34. data/fixtures/vcr_cassettes/TestHudsonJob_test_wipe_out_workspace.yml +685 -0
  35. data/jenkins-remote-api_1.gemspec +23 -0
  36. data/lib/jenkins-remote-api_1.rb +5 -0
  37. data/lib/jenkins-remote-api_1/build.rb +24 -0
  38. data/lib/jenkins-remote-api_1/build_queue.rb +16 -0
  39. data/lib/jenkins-remote-api_1/client.rb +179 -0
  40. data/lib/jenkins-remote-api_1/errors.rb +3 -0
  41. data/lib/jenkins-remote-api_1/hudson_xml_api.rb +61 -0
  42. data/lib/jenkins-remote-api_1/job.rb +212 -0
  43. data/lib/jenkins-remote-api_1/multicast.rb +22 -0
  44. data/lib/jenkins-remote-api_1/new_job_config.xml +16 -0
  45. data/lib/jenkins-remote-api_1/parser/build_info.rb +37 -0
  46. data/lib/jenkins-remote-api_1/parser/build_queue_info.rb +19 -0
  47. data/lib/jenkins-remote-api_1/parser/job_config_info.rb +99 -0
  48. data/lib/jenkins-remote-api_1/parser/job_info.rb +62 -0
  49. data/lib/jenkins-remote-api_1/parser/multicast.rb +28 -0
  50. data/lib/jenkins-remote-api_1/parser/server_info.rb +49 -0
  51. data/lib/jenkins-remote-api_1/settings.rb +31 -0
  52. data/lib/jenkins-remote-api_1/version.rb +3 -0
  53. data/lib/jenkins-remote-api_1/xml_writer/job_config_info.rb +113 -0
  54. data/test/test_helper.rb +10 -0
  55. data/test/test_hudson_build.rb +28 -0
  56. data/test/test_hudson_build_queue.rb +11 -0
  57. data/test/test_hudson_client.rb +65 -0
  58. data/test/test_hudson_job.rb +162 -0
  59. data/test/test_hudson_multicast.rb +9 -0
  60. data/test/test_hudson_settings.rb +70 -0
  61. metadata +112 -0
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "jenkins-remote-api_1/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "jenkins-remote-api_1"
7
+ s.version = Hudson::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Dru Ibarra", "Armen"]
10
+ s.email = ["Druwerd@gmail.com", "armen0089@gmail.com"]
11
+ s.homepage = ""
12
+ s.summary = %q{Connect to Jenkins's remote web API}
13
+ s.description = %q{Connect to Jenkins's remote web API}
14
+
15
+ #s.add_development_dependency "rspec"
16
+
17
+ s.rubyforge_project = "jenkins-remote-api_1"
18
+
19
+ s.files = `git ls-files`.split("\n")
20
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
21
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
22
+ s.require_paths = ["lib"]
23
+ end
@@ -0,0 +1,5 @@
1
+ require 'uri'
2
+ require 'net/http'
3
+ require_relative './jenkins-remote-api_1/client.rb'
4
+
5
+ Dir[File.dirname(__FILE__) + '/jenkins-remote-api_1/**/*.rb'].each {|file| require file }
@@ -0,0 +1,24 @@
1
+ module Hudson
2
+ class Build
3
+ attr_reader :number, :job, :revisions, :result, :culprit
4
+
5
+ def initialize(job, build_number=nil)
6
+ @job = Job.new(job) if job.kind_of?(String)
7
+ @job = job if job.kind_of?(Hudson::Job)
8
+ @number = build_number || @job.last_build
9
+ @revisions = {}
10
+ load_build_info
11
+ end
12
+
13
+ private
14
+ def load_build_info
15
+ build_info_xml = Hudson.client.job_build_info(self.job.name, self.number)
16
+ build_info_parser = Hudson::Parser::BuildInfo.new(build_info_xml)
17
+
18
+ @result = build_info_parser.result
19
+ @revisions = build_info_parser.revisions
20
+ @culprit = build_info_parser.culprit
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,16 @@
1
+ module Hudson
2
+ # This class provides an interface to Hudson's build queue
3
+ class BuildQueue
4
+
5
+ class << self
6
+ def list
7
+ xml = Hudson.client.build_queue_info
8
+ build_queue_info_parser = Hudson::Parser::BuildQueueInfo.new(xml)
9
+
10
+ build_queue_info_parser.items
11
+ end
12
+ end
13
+
14
+ end
15
+
16
+ end
@@ -0,0 +1,179 @@
1
+ require File.dirname(__FILE__) + '/multicast.rb'
2
+
3
+ module Hudson
4
+
5
+ class << self
6
+ def client(config_settings={})
7
+ @client ||= Client.new(config_settings)
8
+ end
9
+ end
10
+
11
+ class Client
12
+ attr_reader :configuration, :xml_api
13
+
14
+ def initialize(config_settings={})
15
+ @configuration = ::Hudson::Settings.new(config_settings).configuration
16
+ @xml_api = ::Hudson::XmlApi.new(self.configuration)
17
+ #fetch_crumb
18
+ end
19
+
20
+ def auto_configure
21
+ xml_response = ::Hudson.discover
22
+ if xml_response
23
+ mulitcast_parser = ::Hudson::Parser::Multicast.new(xml_response)
24
+ self.configuration.host = mulitcast_parser.url || self.configuration.host
25
+ self.configuration.version = mulitcast_parser.version || self.configuration.version
26
+ puts "found Hudson version #{mulitcast_parser.version} @ #{mulitcast_parser.url}"
27
+ return !mulitcast_parser.url.nil?
28
+ end
29
+ end
30
+
31
+ def job_build_info(job_name, build_number)
32
+ get_xml(self.xml_api.build_info_url(job_name, build_number))
33
+ end
34
+
35
+ def build_queue_info
36
+ get_xml(self.xml_api.build_queue_info_url)
37
+ end
38
+
39
+ def build_job!(job_name, delay=0)
40
+ send_post_request(self.xml_api.build_url(job_name), {:delay => "#{delay}sec"})
41
+ end
42
+
43
+ def build_job_with_parameters!(job_name, params, delay=0)
44
+ send_post_request(self.xml_api.build_with_parameters_url(job_name), {:delay => "#{delay}sec"}.merge(params) )
45
+ end
46
+
47
+ def job_config_info(job_name)
48
+ get_xml(self.xml_api.job_config_url(job_name))
49
+ end
50
+
51
+ def create_item!(params)
52
+ send_post_request(self.xml_api.create_item_url, params)
53
+ end
54
+
55
+ def delete_job!(job_name)
56
+ send_post_request(self.xml_api.delete_url(job_name))
57
+ end
58
+
59
+ def disable_job!(job_name)
60
+ send_post_request(self.xml_api.disable_url(job_name))
61
+ end
62
+
63
+ def enable_job!(job_name)
64
+ send_post_request(self.xml_api.enable_url(job_name))
65
+ end
66
+
67
+ def job_info(job_name)
68
+ get_xml(self.xml_api.job_info_url(job_name))
69
+ end
70
+
71
+ def server_info
72
+ get_xml(self.xml_api.server_info_url)
73
+ end
74
+
75
+ def update_job_config!(job_name, config)
76
+ send_xml_post_request(self.xml_api.job_config_url(job_name), config)
77
+ end
78
+
79
+ def wipeout_job_workspace!(job_name)
80
+ send_post_request(self.xml_api.wipeout_workspace_url(job_name))
81
+ end
82
+
83
+ private
84
+ def patch_bad_git_xml(xml)
85
+ xml.gsub(/<(\/?)origin\/([_a-zA-Z0-9\-\.]+)>/, '<\1origin-\2>')
86
+ end
87
+
88
+ def http_class
89
+ if self.configuration.proxy_host && self.configuration.proxy_port
90
+ ::Net::HTTP::Proxy(self.configuration.proxy_host, self.configuration.proxy_port)
91
+ else
92
+ ::Net::HTTP
93
+ end
94
+ end
95
+
96
+ def hudson_request(uri,request)
97
+ http_class.start(uri.host, uri.port) do |http|
98
+ http = http_class.new(uri.host, uri.port)
99
+ if uri.scheme == 'https'
100
+ http.use_ssl = true
101
+ http.verify_mode = ::OpenSSL::SSL::VERIFY_NONE
102
+ end
103
+ http.request(request)
104
+ end
105
+ end
106
+
107
+ def get_xml(url)
108
+ uri = ::URI.parse(URI.encode(url))
109
+ request = http_class::Get.new(uri.path).tap do |r|
110
+ r.basic_auth(self.configuration.user, self.configuration.password) if self.configuration.user && self.configuration.password
111
+ r['Content-Type'] = "text/xml"
112
+ end
113
+
114
+ response = hudson_request(uri,request)
115
+
116
+ if response.is_a?(::Net::HTTPSuccess) or response.is_a?(::Net::HTTPRedirection)
117
+ encoding = response.get_fields("Content-Encoding")
118
+ if encoding and encoding.include?("gzip")
119
+ return ::Zlib::GzipReader.new(::StringIO.new(response.body)).read
120
+ else
121
+ return response.body
122
+ end
123
+ else
124
+ $stderr.puts response.body
125
+ raise APIError, "Error retrieving #{uri.path}"
126
+ end
127
+ end
128
+
129
+ def send_post_request(url, data={})
130
+ uri = ::URI.parse(::URI.encode(url))
131
+ request = http_class::Post.new(uri.path).tap do |r|
132
+ r.basic_auth(self.configuration.user, self.configuration.password) if self.configuration.user && self.configuration.password
133
+ r.set_form_data(data)
134
+ r.add_field(crumb.name, crumb.value) if crumb
135
+ end
136
+
137
+ hudson_request(uri,request)
138
+ end
139
+
140
+ def send_xml_post_request(url, xml, data=nil)
141
+ uri = ::URI.parse(URI.encode(url))
142
+ path = uri.query ? "#{uri.path}?#{uri.query}" : uri.path
143
+ request = http_class::Post.new(path).tap do |r|
144
+ r.basic_auth(self.configuration.user, self.configuration.password) if self.configuration.user && self.configuration.password
145
+ r.set_form_data(data) if data
146
+ r.add_field(crumb.name, crumb.value) if crumb
147
+ r.body = xml
148
+ end
149
+
150
+ hudson_request(uri,request)
151
+ end
152
+
153
+ def crumb
154
+ @@apiCrumb ||= nil
155
+ end
156
+
157
+ def fetch_crumb
158
+ if self.configuration.crumb
159
+ body = get_xml(self.xml_api.crumb_url)
160
+ doc = ::REXML::Document.new(body)
161
+
162
+ crumbValue = doc.elements['/defaultCrumbIssuer/crumb'] or begin
163
+ $stderr.puts "Failure fetching crumb value from server"
164
+ return
165
+ end
166
+
167
+ crumbName = doc.elements['/defaultCrumbIssuer/crumbRequestField'] or begin
168
+ $stderr.puts "Failure fetching crumb field name from server"
169
+ return
170
+ end
171
+
172
+ @@apiCrumb = ::Struct.new(:name,:value).new(crumbName.text,crumbValue.text)
173
+ end
174
+ rescue
175
+ $stderr.puts "Failure fetching crumb xml"
176
+ end
177
+
178
+ end
179
+ end
@@ -0,0 +1,3 @@
1
+ module Hudson
2
+ class APIError < StandardError; end
3
+ end
@@ -0,0 +1,61 @@
1
+ module Hudson
2
+ class XmlApi
3
+ attr_accessor :host
4
+
5
+ def initialize(configuration)
6
+ self.host = configuration.host
7
+ end
8
+
9
+ def build_url(job_name)
10
+ File.join(self.host, "job/#{job_name}/build")
11
+ end
12
+
13
+ def build_info_url(job_name, build_number)
14
+ File.join(self.host, "job/#{job_name}/#{build_number}/api/xml")
15
+ end
16
+
17
+ def build_queue_info_url
18
+ File.join(self.host, "queue/api/xml")
19
+ end
20
+
21
+ def build_with_parameters_url(job_name)
22
+ File.join(self.host, "job/#{job_name}/buildWithParameters")
23
+ end
24
+
25
+ def create_item_url
26
+ File.join(self.host, "createItem")
27
+ end
28
+
29
+ def crumb_url
30
+ File.join(self.host, "/crumbIssuer/api/xml")
31
+ end
32
+
33
+ def delete_url(job_name)
34
+ File.join(self.host, "job/#{job_name}/doDelete")
35
+ end
36
+
37
+ def disable_url(job_name)
38
+ File.join(self.host, "job/#{job_name}/disable")
39
+ end
40
+
41
+ def enable_url(job_name)
42
+ File.join(self.host, "job/#{job_name}/enable")
43
+ end
44
+
45
+ def job_info_url(job_name)
46
+ File.join(self.host, "job/#{job_name}/api/xml")
47
+ end
48
+
49
+ def job_config_url(job_name)
50
+ File.join(self.host, "job/#{job_name}/config.xml")
51
+ end
52
+
53
+ def server_info_url
54
+ File.join(self.host, "api/xml")
55
+ end
56
+
57
+ def wipeout_workspace_url(job_name)
58
+ File.join(self.host, "job/#{job_name}/doWipeOutWorkspace")
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,212 @@
1
+ module Hudson
2
+ # This class provides an interface to Hudson jobs
3
+ class Job
4
+ attr_accessor :name, :config, :repository_url, :repository_urls, :repository_browser_location, :description, :parameterized_job
5
+
6
+ # Class methods
7
+ class <<self
8
+ # List all Hudson jobs
9
+ def list()
10
+ xml = Hudson.client.server_info
11
+ server_info_parser = Hudson::Parser::ServerInfo.new(xml)
12
+
13
+ server_info_parser.jobs
14
+ end
15
+
16
+ # List all jobs in active execution
17
+ def list_active
18
+ xml = Hudson.client.server_info
19
+ server_info_parser = Hudson::Parser::ServerInfo.new(xml)
20
+
21
+ server_info_parser.active_jobs
22
+ end
23
+
24
+ def get(job_name)
25
+ job_name.strip!
26
+ list.include?(job_name) ? Job.new(job_name) : nil
27
+ end
28
+
29
+ def create(name, config=nil)
30
+ config ||= File.open(File.dirname(__FILE__) + '/new_job_config.xml').read
31
+ response = Hudson.client.create_item!({:name=>name, :mode=>"hudson.model.FreeStyleProject", :config=>config})
32
+ raise(APIError, "Error creating job #{name}: #{response.body}") if response.class != Net::HTTPFound
33
+ Job.get(name)
34
+ end
35
+
36
+ end
37
+
38
+ # Instance methods
39
+ def initialize(name, config=nil)
40
+ name.strip!
41
+ # Creates the job in Hudson if it doesn't already exist
42
+ @name = Job.list.include?(name) ? name : Job.create(name, config).name
43
+ load_config
44
+ self
45
+ end
46
+
47
+ # Load data from Hudson's Job configuration settings into class variables
48
+ def load_config
49
+ @config = Hudson.client.job_config_info(self.name)
50
+ @config_info_parser = Hudson::Parser::JobConfigInfo.new(@config)
51
+ @config_writer = Hudson::XmlWriter::JobConfigInfo.new(self.name, @config)
52
+
53
+ @info = Hudson.client.job_info(self.name)
54
+ @job_info_parser = Hudson::Parser::JobInfo.new(@info)
55
+
56
+ @description = @config_info_parser.description
57
+ @parameterized_job = @config_info_parser.parameterized?
58
+ @git = @config_info_parser.git_repo?
59
+ @repository_url = @git ? @config_info_parser.git_repository : @config_info_parser.svn_repository
60
+ @repostory_urls = @config_info_parser.svn_repository_urls
61
+ @repository_browser_location = @config_info_parser.scm_broswer_location
62
+ end
63
+ alias :reload_config :load_config
64
+
65
+ def free_style_project?
66
+ @free_style_project ||= @job_info_parser.free_style_project?
67
+ end
68
+
69
+ def color
70
+ @color ||= @job_info_parser.color
71
+ end
72
+
73
+ def last_build
74
+ @job_info_parser.last_build
75
+ end
76
+
77
+ def last_completed_build
78
+ @job_info_parser.last_completed_build
79
+ end
80
+
81
+ def last_failed_build
82
+ @job_info_parser.last_failed_build
83
+ end
84
+
85
+ def last_stable_build
86
+ @job_info_parser.last_stable_build
87
+ end
88
+
89
+ def last_successful_build
90
+ @job_info_parser.last_successful_build
91
+ end
92
+
93
+ def last_unsuccessful_build
94
+ @job_info_parser.last_unsuccessful_build
95
+ end
96
+
97
+ def next_build_number
98
+ @job_info_parser.next_build_number
99
+ end
100
+
101
+ def builds_list
102
+ @job_info_parser.builds
103
+ end
104
+
105
+ def active?
106
+ Job.list_active.include?(self.name)
107
+ end
108
+
109
+ def wait_for_build_to_finish(poll_freq=10)
110
+ loop do
111
+ puts "waiting for all #{@name} builds to finish"
112
+ sleep poll_freq # wait
113
+ break if !active? and !BuildQueue.list.include?(@name)
114
+ end
115
+ end
116
+
117
+ # Create a new job on Hudson server based on the current job object
118
+ def copy(new_job=nil)
119
+ new_job = "copy_of_#{@name}" if new_job.nil?
120
+ response = Hudson.client.create_item!({:name=>new_job, :mode=>"copy", :from=>@name})
121
+ raise(APIError, "Error copying job #{@name}: #{response.body}") if response.class != Net::HTTPFound
122
+ Job.new(new_job)
123
+ end
124
+
125
+ # Set the repository url and update on Hudson server
126
+ def repository_url=(repository_url)
127
+ #return false if @repository_url.nil?
128
+ if @git
129
+ @config_writer.git_repository_url = repository_url
130
+ else
131
+ @config_writer.svn_repository_url = repository_url
132
+ end
133
+ reload_config
134
+ end
135
+
136
+ def repository_urls=(repository_urls)
137
+ @config_writer.repository_urls = repository_urls
138
+ reload_config
139
+ end
140
+
141
+ # Set the repository browser location and update on Hudson server
142
+ def repository_browser_location=(repository_browser_location)
143
+ if @git
144
+ @config_writer.git_repository_browser_location = repository_browser_location
145
+ else
146
+ @config_writer.svn_repository_browser_location = repository_browser_location
147
+ end
148
+ reload_config
149
+ end
150
+
151
+ # Set the job description and update on Hudson server
152
+ def description=(description)
153
+ @config_writer.description = description
154
+ reload_config
155
+ end
156
+
157
+ def triggers= opts={}
158
+ @config_writer.triggers = opts
159
+ reload_config
160
+ end
161
+
162
+ def triggers
163
+ @config_info_parser.triggers
164
+ end
165
+
166
+ #def url
167
+ # File.join( Hudson[:url], 'job', self.name) + '/'
168
+ #end
169
+
170
+ # Start building this job on Hudson server
171
+ def build(params={})
172
+ if @parameterized_job
173
+ response = Hudson.client.build_job_with_parameters!(self.name, params)
174
+ else
175
+ response = Hudson.client.build_job!(self.name)
176
+ end
177
+
178
+ yield(response) if block_given?
179
+ response.is_a?(Net::HTTPSuccess) or response.is_a?(Net::HTTPRedirection)
180
+ end
181
+
182
+ def disable()
183
+ response = Hudson.client.disable_job!(self.name)
184
+ response.is_a?(Net::HTTPSuccess) or response.is_a?(Net::HTTPRedirection)
185
+ end
186
+
187
+ def enable()
188
+ response = Hudson.client.enable_job!(self.name)
189
+ response.is_a?(Net::HTTPSuccess) or response.is_a?(Net::HTTPRedirection)
190
+ end
191
+
192
+ # Delete this job from Hudson server
193
+ def delete()
194
+ response = Hudson.client.delete_job!(self.name)
195
+ response.is_a?(Net::HTTPSuccess) or response.is_a?(Net::HTTPRedirection)
196
+ end
197
+
198
+ def wipe_out_workspace()
199
+ wait_for_build_to_finish
200
+
201
+ if !active?
202
+ response = Hudson.client.wipeout_job_workspace!(self.name)
203
+ else
204
+ response = false
205
+ end
206
+ response.is_a?(Net::HTTPSuccess) or response.is_a?(Net::HTTPRedirection)
207
+ end
208
+
209
+
210
+
211
+ end
212
+ end