hudson-remote-cli 0.0.1

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.
@@ -0,0 +1,144 @@
1
+ module Hudson
2
+ class Job < HudsonObject
3
+ attr_reader :name
4
+
5
+ class << self
6
+ def create(name, config_path)
7
+ name.strip!
8
+
9
+ config = File.open(config_path).read
10
+
11
+ res = send_xml_post_request(
12
+ uri_for(@@api_create_item, {:name => name}),
13
+ config
14
+ )
15
+ raise(APIError, "Error creating job #{name}: #{res.body}") if
16
+ res.class != Net::HTTPOK
17
+
18
+ Job.new(name)
19
+ end
20
+ end
21
+
22
+ def initialize(name)
23
+ name.strip!
24
+ raise "no job named #{name}!" unless Hudson.jobs.include?(name)
25
+ @name = name
26
+ load_json_api
27
+ end
28
+
29
+ def load_json_api
30
+ @api_job = "job/#{@name}/api/json"
31
+ @api_job_config = "job/#{@name}/config.xml"
32
+ @api_job_description = "job/#{@name}/description"
33
+ @api_job_build = "job/#{@name}/build"
34
+ @api_job_withparam_build = "job/#{@name}/buildWithParameters"
35
+ @api_job_delete = "job/#{@name}/doDelete"
36
+ @api_job_disable = "job/#{@name}/disable"
37
+ @api_job_enable = "job/#{@name}/enable"
38
+ end
39
+
40
+ def api(keys = nil, refresh = true)
41
+ api_base(@api_job, keys, refresh)
42
+ end
43
+
44
+ def active?
45
+ api('color')['color'].include?('anime')
46
+ end
47
+
48
+ def buildable?
49
+ api('buildable')['buildable']
50
+ end
51
+
52
+ def last_success_build
53
+ res = api('lastSuccessfulBuild[number]')['lastSuccessfulBuild']
54
+ res.nil? ? 0 : res['number']
55
+ end
56
+
57
+ def last_build
58
+ res = api('lastBuild[number]')['lastBuild']
59
+ res.nil? ? 0 : res['number']
60
+ end
61
+
62
+ def builds
63
+ res = api('builds[number]')['builds']
64
+ res.nil? ? [] : res.map { |b| b['number'] }
65
+ end
66
+
67
+ def update(config)
68
+ @config = File.open(config).read unless config.nil?
69
+ res = send_xml_post_request(uri_for(@api_job_config), @config)
70
+ @config = nil
71
+ res.is_a?(Net::HTTPSuccess) or res.is_a?(Net::HTTPRedirection)
72
+ end
73
+
74
+ def config
75
+ return @config unless @config.nil?
76
+ @config = get(uri_for(@api_job_config))
77
+ @config
78
+ end
79
+
80
+ def description
81
+ return @description unless @description.nil?
82
+ @description = get(uri_for(@api_job_description))
83
+ @description
84
+ end
85
+
86
+ def description=(description)
87
+ @description = description
88
+ send_post_request(uri_for(@api_job_description), {
89
+ :description => @description
90
+ })
91
+ end
92
+
93
+ def copy(name = nil)
94
+ name = "copy_of_#{@name}" if name.nil?
95
+ name.strip!
96
+ res = send_post_request(uri_for(@@api_create_item), {
97
+ :name => name,
98
+ :mode => 'copy',
99
+ :from => @name
100
+ })
101
+ raise(APIError, "Error copying job #{@name}: #{res.body}") if
102
+ res.class != Net::HTTPFound
103
+ Job.new(name)
104
+ end
105
+
106
+ def disable
107
+ res = send_post_request(uri_for(@api_job_disable))
108
+ res.is_a?(Net::HTTPSuccess) or res.is_a?(Net::HTTPRedirection)
109
+ end
110
+
111
+ def enable
112
+ res = send_post_request(uri_for(@api_job_enable))
113
+ res.is_a?(Net::HTTPSuccess) or res.is_a?(Net::HTTPRedirection)
114
+ end
115
+
116
+ def delete
117
+ res = send_post_request(uri_for(@api_job_delete))
118
+ res.is_a?(Net::HTTPSuccess) or res.is_a?(Net::HTTPRedirection)
119
+ end
120
+
121
+ def build(param = {})
122
+ response = nil
123
+ if param.size.eql? 0
124
+ response = send_post_request(uri_for(@api_job_build), {:delay => '0sec'})
125
+ else
126
+ param[:delay] = '0sec' unless param.has_key? :delay
127
+ response = send_post_request(uri_for(@api_job_withparam_build), param)
128
+ end
129
+ response.is_a?(Net::HTTPSuccess) or response.is_a?(Net::HTTPRedirection)
130
+ end
131
+
132
+ def build!(param = {})
133
+ build(param)
134
+ wait_for_build
135
+ end
136
+
137
+ def wait_for_build(poll_freq = 10)
138
+ loop do
139
+ break if !active? and !BuildQueue.list.include?(@name)
140
+ sleep poll_freq
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,18 @@
1
+ module Hudson
2
+ class System < HudsonObject
3
+ class << self
4
+ def api(keys = nil, refresh = false)
5
+ api_base(@@api_hudson, keys, refresh)
6
+ end
7
+
8
+ def overallLoad(keys = nil, refresh = false)
9
+ api_base(@@api_overallLoad, keys, refresh)
10
+ end
11
+
12
+ def jobs(*param)
13
+ jobs = api('jobs[name]', true)
14
+ jobs['jobs'].map { |j| j['name'] }
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ module Hudson
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,118 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'rack/utils'
4
+ require 'zlib'
5
+ require 'json'
6
+ require 'hudson-remote-cli/version'
7
+ require 'hudson-remote-cli/config'
8
+
9
+ module Hudson
10
+ autoload :System, 'hudson-remote-cli/system'
11
+ autoload :BuildQueue, 'hudson-remote-cli/build_queue'
12
+ autoload :Job, 'hudson-remote-cli/job'
13
+ autoload :Build, 'hudson-remote-cli/build'
14
+
15
+ class APIError < StandardError; end
16
+
17
+ class HudsonObject
18
+ def self.uri_for(path, query = {})
19
+ uri = URI.parse(URI.encode(File.join(Hudson[:url], path)))
20
+ uri.query = Rack::Utils.build_query(query)
21
+ uri
22
+ end
23
+
24
+ def uri_for(path, query = {})
25
+ self.class.uri_for(path, query)
26
+ end
27
+
28
+ def self.load_json_api
29
+ @@_api_cache = Hash.new
30
+
31
+ @@api_hudson = 'api/json'
32
+ @@api_create_item = 'createItem'
33
+ @@api_overallLoad = 'overallLoad/api/json'
34
+ end
35
+
36
+ load_json_api
37
+
38
+ def self.hudson_request(uri, request)
39
+ Net::HTTP.start(uri.host, uri.port) do |http|
40
+ http = Net::HTTP.new(uri.host, uri.port)
41
+ if uri.scheme == 'https'
42
+ http.use_ssl = true
43
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
44
+ end
45
+ http.request(request)
46
+ end
47
+ end
48
+
49
+ def self.get(uri)
50
+ request = Net::HTTP::Get.new(uri.request_uri)
51
+ request.basic_auth(Hudson[:user], Hudson[:password]) if
52
+ Hudson[:user] and Hudson[:password]
53
+ request['Content-Type'] = 'text/html'
54
+ response = hudson_request(uri, request)
55
+
56
+ if response.is_a?(Net::HTTPSuccess) or response.is_a?(Net::HTTPRedirection)
57
+ encoding = response.get_fields("Content-Encoding")
58
+ if encoding and encoding.include?("gzip")
59
+ return Zlib::GzipReader.new(StringIO.new(response.body)).read
60
+ else
61
+ return response.body
62
+ end
63
+ else
64
+ $stderr.puts response.body
65
+ raise APIError, "Error retrieving #{uri.request_uri}"
66
+ end
67
+ end
68
+
69
+ def get(uri)
70
+ self.class.get(uri)
71
+ end
72
+
73
+ def self.send_post_request(uri, data={})
74
+ request = Net::HTTP::Post.new(uri.request_uri)
75
+ request.basic_auth(Hudson[:user], Hudson[:password]) if
76
+ Hudson[:user] and Hudson[:password]
77
+ request.set_form_data(data)
78
+ hudson_request(uri, request)
79
+ end
80
+
81
+ def send_post_request(uri, data={})
82
+ self.class.send_post_request(uri, data)
83
+ end
84
+
85
+ def self.send_xml_post_request(uri, xml, data=nil)
86
+ request = Net::HTTP::Post.new(uri.request_uri)
87
+ request['Content-Type'] = 'text/xml'
88
+ request.basic_auth(Hudson[:user], Hudson[:password]) if
89
+ Hudson[:user] and Hudson[:password]
90
+ request.set_form_data(data) if data
91
+ request.body = xml
92
+ hudson_request(uri,request)
93
+ end
94
+
95
+ def send_xml_post_request(uri, xml, data=nil)
96
+ self.class.send_xml_post_request(uri, xml, data)
97
+ end
98
+
99
+ def self.api_base(api, keys = nil, refresh = false)
100
+ cache_key = [api, keys.nil? ? '__ALL__' : keys].join '_'
101
+
102
+ return @@_api_cache[cache_key] if
103
+ @@_api_cache.has_key?(cache_key) and not refresh
104
+
105
+ json = get(uri_for(api, keys.nil? ? {} : {:tree => keys}))
106
+ @@_api_cache[cache_key] = JSON.load(json)
107
+ @@_api_cache[cache_key]
108
+ end
109
+
110
+ def api_base(api, keys = nil, refresh = false)
111
+ self.class.api_base(api, keys, refresh)
112
+ end
113
+ end
114
+
115
+ def self.method_missing(method, *args, &block)
116
+ Hudson::System.send(method, *args, block)
117
+ end
118
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ module Hudson
4
+ describe BuildQueue do
5
+ describe '#api' do
6
+ it 'should be a Hash' do
7
+ api = BuildQueue.api
8
+ api.class.should eq(Hash)
9
+ end
10
+
11
+ it 'should be only jobs[name, color]' do
12
+ jobs = BuildQueue.api('items[task[name]]')
13
+ jobs.keys.should eq(['items'])
14
+ end
15
+ end
16
+ end
17
+ end
data/spec/config.xml ADDED
@@ -0,0 +1,258 @@
1
+ <?xml version='1.0' encoding='UTF-8'?>
2
+ <project>
3
+ <actions/>
4
+ <description></description>
5
+ <project-properties class="java.util.concurrent.ConcurrentHashMap">
6
+ <entry>
7
+ <string>blockBuildWhenDownstreamBuilding</string>
8
+ <boolean-property>
9
+ <originalValue class="boolean">false</originalValue>
10
+ <propertyOverridden>false</propertyOverridden>
11
+ </boolean-property>
12
+ </entry>
13
+ <entry>
14
+ <string>org-hudsonci-plugins-snapshotmonitor-SnapshotTrigger</string>
15
+ <trigger-property>
16
+ <propertyOverridden>false</propertyOverridden>
17
+ </trigger-property>
18
+ </entry>
19
+ <entry>
20
+ <string>org-hudsonci-plugins-snapshotmonitor-WatchedDependenciesProperty</string>
21
+ <base-property>
22
+ <propertyOverridden>false</propertyOverridden>
23
+ </base-property>
24
+ </entry>
25
+ <entry>
26
+ <string>logRotator</string>
27
+ <log-rotator-property>
28
+ <propertyOverridden>false</propertyOverridden>
29
+ </log-rotator-property>
30
+ </entry>
31
+ <entry>
32
+ <string>au-com-centrumsystems-hudson-plugin-buildpipeline-trigger-BuildPipelineTrigger</string>
33
+ <external-property>
34
+ <propertyOverridden>false</propertyOverridden>
35
+ <modified>false</modified>
36
+ </external-property>
37
+ </entry>
38
+ <entry>
39
+ <string>blockBuildWhenUpstreamBuilding</string>
40
+ <boolean-property>
41
+ <originalValue class="boolean">false</originalValue>
42
+ <propertyOverridden>false</propertyOverridden>
43
+ </boolean-property>
44
+ </entry>
45
+ <entry>
46
+ <string>scmCheckoutRetryCount</string>
47
+ <integer-property>
48
+ <propertyOverridden>false</propertyOverridden>
49
+ </integer-property>
50
+ </entry>
51
+ <entry>
52
+ <string>hudson-plugins-promoted_builds-JobPropertyImpl</string>
53
+ <base-property>
54
+ <propertyOverridden>false</propertyOverridden>
55
+ </base-property>
56
+ </entry>
57
+ <entry>
58
+ <string>org-hudsonci-maven-plugin-dependencymonitor-DependencyNotifier</string>
59
+ <external-property>
60
+ <propertyOverridden>false</propertyOverridden>
61
+ <modified>false</modified>
62
+ </external-property>
63
+ </entry>
64
+ <entry>
65
+ <string>hudson-plugins-git-GitPublisher</string>
66
+ <external-property>
67
+ <propertyOverridden>false</propertyOverridden>
68
+ <modified>false</modified>
69
+ </external-property>
70
+ </entry>
71
+ <entry>
72
+ <string>org-hudsonci-maven-plugin-artifactrecorder-ArtifactArchiver</string>
73
+ <external-property>
74
+ <propertyOverridden>false</propertyOverridden>
75
+ <modified>false</modified>
76
+ </external-property>
77
+ </entry>
78
+ <entry>
79
+ <string>hudson-tasks-Mailer</string>
80
+ <external-property>
81
+ <propertyOverridden>false</propertyOverridden>
82
+ <modified>false</modified>
83
+ </external-property>
84
+ </entry>
85
+ <entry>
86
+ <string>cleanWorkspaceRequired</string>
87
+ <boolean-property>
88
+ <originalValue class="boolean">false</originalValue>
89
+ <propertyOverridden>false</propertyOverridden>
90
+ </boolean-property>
91
+ </entry>
92
+ <entry>
93
+ <string>hudson-tasks-test-AggregatedTestResultPublisher</string>
94
+ <external-property>
95
+ <propertyOverridden>false</propertyOverridden>
96
+ <modified>false</modified>
97
+ </external-property>
98
+ </entry>
99
+ <entry>
100
+ <string>hudson-tasks-junit-JUnitResultArchiver</string>
101
+ <external-property>
102
+ <propertyOverridden>false</propertyOverridden>
103
+ <modified>false</modified>
104
+ </external-property>
105
+ </entry>
106
+ <entry>
107
+ <string>hudson-tasks-Fingerprinter</string>
108
+ <external-property>
109
+ <propertyOverridden>false</propertyOverridden>
110
+ <modified>false</modified>
111
+ </external-property>
112
+ </entry>
113
+ <entry>
114
+ <string>hudson-plugins-ansicolor-AnsiColorBuildWrapper</string>
115
+ <external-property>
116
+ <propertyOverridden>false</propertyOverridden>
117
+ <modified>true</modified>
118
+ </external-property>
119
+ </entry>
120
+ <entry>
121
+ <string>hudson-plugins-downstream_ext-DownstreamTrigger</string>
122
+ <external-property>
123
+ <propertyOverridden>false</propertyOverridden>
124
+ <modified>false</modified>
125
+ </external-property>
126
+ </entry>
127
+ <entry>
128
+ <string>hudson-tasks-JavadocArchiver</string>
129
+ <external-property>
130
+ <propertyOverridden>false</propertyOverridden>
131
+ <modified>false</modified>
132
+ </external-property>
133
+ </entry>
134
+ <entry>
135
+ <string>parametersDefinitionProperties</string>
136
+ <copy-write-list-property>
137
+ <originalValue class="hudson.util.CopyOnWriteList"/>
138
+ <propertyOverridden>false</propertyOverridden>
139
+ </copy-write-list-property>
140
+ </entry>
141
+ <entry>
142
+ <string>customWorkspace</string>
143
+ <string-property>
144
+ <propertyOverridden>false</propertyOverridden>
145
+ </string-property>
146
+ </entry>
147
+ <entry>
148
+ <string>quietPeriod</string>
149
+ <integer-property>
150
+ <propertyOverridden>false</propertyOverridden>
151
+ </integer-property>
152
+ </entry>
153
+ <entry>
154
+ <string>hudson-tasks-BuildTrigger</string>
155
+ <external-property>
156
+ <propertyOverridden>false</propertyOverridden>
157
+ <modified>false</modified>
158
+ </external-property>
159
+ </entry>
160
+ <entry>
161
+ <string>hudson-triggers-SCMTrigger</string>
162
+ <trigger-property>
163
+ <propertyOverridden>false</propertyOverridden>
164
+ </trigger-property>
165
+ </entry>
166
+ <entry>
167
+ <string>builders</string>
168
+ <describable-list-property>
169
+ <originalValue class="hudson.util.DescribableList">
170
+ <hudson.tasks.Shell>
171
+ <command>echo nice
172
+ sleep 10</command>
173
+ </hudson.tasks.Shell>
174
+ </originalValue>
175
+ <propertyOverridden>false</propertyOverridden>
176
+ </describable-list-property>
177
+ </entry>
178
+ <entry>
179
+ <string>jdk</string>
180
+ <string-property>
181
+ <propertyOverridden>false</propertyOverridden>
182
+ </string-property>
183
+ </entry>
184
+ <entry>
185
+ <string>appointedNode</string>
186
+ <base-property>
187
+ <propertyOverridden>false</propertyOverridden>
188
+ </base-property>
189
+ </entry>
190
+ <entry>
191
+ <string>hudson-plugins-perforce-PerforceTagNotifier</string>
192
+ <external-property>
193
+ <propertyOverridden>false</propertyOverridden>
194
+ <modified>false</modified>
195
+ </external-property>
196
+ </entry>
197
+ <entry>
198
+ <string>concurrentBuild</string>
199
+ <boolean-property>
200
+ <originalValue class="boolean">false</originalValue>
201
+ <propertyOverridden>false</propertyOverridden>
202
+ </boolean-property>
203
+ </entry>
204
+ <entry>
205
+ <string>hudson-tasks-ArtifactArchiver</string>
206
+ <external-property>
207
+ <propertyOverridden>false</propertyOverridden>
208
+ <modified>false</modified>
209
+ </external-property>
210
+ </entry>
211
+ <entry>
212
+ <string>org-hudsonci-maven-plugin-dependencymonitor-DependencyTrigger</string>
213
+ <trigger-property>
214
+ <propertyOverridden>false</propertyOverridden>
215
+ </trigger-property>
216
+ </entry>
217
+ <entry>
218
+ <string>scm</string>
219
+ <scm-property>
220
+ <originalValue class="hudson.scm.NullSCM"/>
221
+ <propertyOverridden>false</propertyOverridden>
222
+ </scm-property>
223
+ </entry>
224
+ <entry>
225
+ <string>hudson-triggers-TimerTrigger</string>
226
+ <trigger-property>
227
+ <propertyOverridden>false</propertyOverridden>
228
+ </trigger-property>
229
+ </entry>
230
+ <entry>
231
+ <string>org-hudsonci-maven-plugin-artifactrecorder-ArtifactFingerprinter</string>
232
+ <external-property>
233
+ <propertyOverridden>false</propertyOverridden>
234
+ <modified>false</modified>
235
+ </external-property>
236
+ </entry>
237
+ </project-properties>
238
+ <keepDependencies>false</keepDependencies>
239
+ <createdBy>im-op</createdBy>
240
+ <creationTime>1375924970937</creationTime>
241
+ <properties/>
242
+ <cascadingChildrenNames class="java.util.concurrent.CopyOnWriteArraySet"/>
243
+ <cascading-job-properties class="java.util.concurrent.CopyOnWriteArraySet">
244
+ <string>org-hudsonci-plugins-snapshotmonitor-WatchedDependenciesProperty</string>
245
+ <string>hudson-plugins-promoted_builds-JobPropertyImpl</string>
246
+ </cascading-job-properties>
247
+ <scm class="hudson.scm.NullSCM"/>
248
+ <canRoam>false</canRoam>
249
+ <disabled>false</disabled>
250
+ <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
251
+ <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
252
+ <triggers class="vector"/>
253
+ <concurrentBuild>false</concurrentBuild>
254
+ <cleanWorkspaceRequired>false</cleanWorkspaceRequired>
255
+ <builders/>
256
+ <publishers/>
257
+ <buildWrappers/>
258
+ </project>
data/spec/job_spec.rb ADDED
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+
3
+ module Hudson
4
+ describe Job, Build do
5
+ let (:job_name) { 'hudson-remote-cli-test' }
6
+ let (:job_conf) { File.join(File.dirname(__FILE__), 'config.xml') }
7
+
8
+ describe '#create' do
9
+ it 'should be a new job in test hudson' do
10
+ Hudson::Job.create job_name, job_conf
11
+ Hudson.jobs.include?(job_name).should be_true
12
+ end
13
+ end
14
+
15
+ describe 'Job Actions' do
16
+ before(:each) do
17
+ @job = Hudson::Job.new(job_name)
18
+ end
19
+
20
+ it 'should be job_name' do
21
+ @job.name.should eq(job_name)
22
+ end
23
+
24
+ context 'job enable/disable test' do
25
+ after(:each) { @job.enable }
26
+
27
+ it 'should be enabled' do
28
+ @job.enable
29
+ @job.buildable?.should be_true
30
+ end
31
+
32
+ it 'should be disabled' do
33
+ @job.disable
34
+ @job.buildable?.should be_false
35
+ end
36
+ end
37
+
38
+ context 'job description set/get' do
39
+ it 'shoud be "test success"' do
40
+ @job.description = 'test success'
41
+ @job.description.should eq('test success')
42
+ end
43
+ end
44
+
45
+ context 'job build/active/lastnumber test' do
46
+ before(:each) do
47
+ @job.wait_for_build
48
+ @last_number = @job.last_build
49
+ end
50
+
51
+ it 'should be trigger a build' do
52
+ @job.build!
53
+ last_number = @job.last_build
54
+ (last_number - @last_number).should eq(1)
55
+ end
56
+ end
57
+ end
58
+
59
+ describe 'build test' do
60
+ before(:each) do
61
+ @build = Hudson::Build.new(job_name)
62
+ end
63
+
64
+ it 'should be 1' do
65
+ @build.number.should eq(1)
66
+ end
67
+
68
+ it 'should be suit for the time in record' do
69
+ (10 - (@build.end_time - @build.start_time)).should < 2
70
+ end
71
+ end
72
+
73
+ describe '#delete' do
74
+ it 'should be no test_job more' do
75
+ @job = Hudson::Job.new(job_name)
76
+ @job.delete
77
+ Hudson.jobs.include?(job_name).should be_false
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,6 @@
1
+ require 'hudson-remote-cli'
2
+ require 'rspec'
3
+
4
+ Hudson[:url] = 'http://localhost:8235/hudson'
5
+ Hudson[:user] = 'test'
6
+ Hudson[:password] = 'test'
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ module Hudson
4
+ describe System do
5
+ describe '#api' do
6
+ it 'should be a Hash' do
7
+ api = Hudson.api
8
+ api.class.should eq(Hash)
9
+ end
10
+
11
+ it 'should be only jobs[name, color]' do
12
+ jobs = Hudson.api('jobs[name,color]')
13
+ jobs.keys.should eq(['jobs'])
14
+ end
15
+ end
16
+
17
+ describe '#overallLoad' do
18
+ it 'should be 4 keys at least' do
19
+ api = Hudson.overallLoad
20
+ (%w{busyExecutors queueLength totalExecutors totalQueueLength} -
21
+ api.keys).should eq([])
22
+ end
23
+ end
24
+ end
25
+ end