autojenkins 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.
- data/bin/mjenk +160 -0
- data/lib/autojenkins.rb +294 -0
- data/lib/test.rb +82 -0
- metadata +67 -0
data/bin/mjenk
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path(File.join(File.dirname(__FILE__), "..", "lib"))
|
4
|
+
require 'optparse'
|
5
|
+
require 'autojenkins'
|
6
|
+
|
7
|
+
URL=''
|
8
|
+
AUTH={}
|
9
|
+
TOKEN=""
|
10
|
+
|
11
|
+
mJENK = AutoJenkins::Jenkins.new(URL, AUTH)
|
12
|
+
###
|
13
|
+
# Commands
|
14
|
+
#
|
15
|
+
module AutoJenkins
|
16
|
+
class Command
|
17
|
+
attr_accessor :jenk,
|
18
|
+
:error,
|
19
|
+
:errmsg
|
20
|
+
|
21
|
+
def initialize(jenk)
|
22
|
+
@jenk = jenk
|
23
|
+
@error = 0
|
24
|
+
@errmsg = ""
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
class JobCommand < Command
|
30
|
+
def exec(hash)
|
31
|
+
|
32
|
+
jobs = @jenk.all_jobs()
|
33
|
+
|
34
|
+
if not hash.key?(:jobname)
|
35
|
+
|
36
|
+
jobs.each do |n|
|
37
|
+
puts "Name: #{n.name} -> Descr: #{n.get_description} -> URL: #{n.get_url}"
|
38
|
+
end
|
39
|
+
|
40
|
+
else
|
41
|
+
jobs.each do |n|
|
42
|
+
if n.name == hash[:jobname]
|
43
|
+
|
44
|
+
if hash.key?(:delete)
|
45
|
+
n.delete()
|
46
|
+
elsif hash.key?(:build)
|
47
|
+
out = n.launch(hash[:token])
|
48
|
+
if out
|
49
|
+
puts "Lauch successful"
|
50
|
+
else
|
51
|
+
puts "Could not launch"
|
52
|
+
end
|
53
|
+
|
54
|
+
puts n.get_url
|
55
|
+
else
|
56
|
+
puts n.get_config()
|
57
|
+
end
|
58
|
+
|
59
|
+
return
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
@error = 2
|
64
|
+
@errmsg = "Job #{hash[:jobname]} not found"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
cCommands = {
|
71
|
+
"job" => AutoJenkins::JobCommand
|
72
|
+
}
|
73
|
+
|
74
|
+
# End Commands
|
75
|
+
options = {:verbose => false,
|
76
|
+
:build => true,
|
77
|
+
:emails => "",
|
78
|
+
:auth => nil,
|
79
|
+
:token => ""}
|
80
|
+
|
81
|
+
p = OptionParser.new do |opts|
|
82
|
+
opts.banner = "Usage: mjenk CMD [options]"
|
83
|
+
|
84
|
+
opts.on("-n", "--dont-run", "Do not run after creating the job") do
|
85
|
+
options[:build] = false
|
86
|
+
end
|
87
|
+
|
88
|
+
opts.on("-v", "--verbose", "Be verbose") do
|
89
|
+
options[:verbose] = true
|
90
|
+
end
|
91
|
+
|
92
|
+
opts.on("-h", "--url URL", String, "Jenkins URL") do |u|
|
93
|
+
URL = u
|
94
|
+
end
|
95
|
+
|
96
|
+
opts.on("-c", "--config-file URL", String, "Jenkins Configuration File") do |cf|
|
97
|
+
options[:auth] = cf
|
98
|
+
end
|
99
|
+
|
100
|
+
opts.on("-j", "--jobname JOBNAME", String, "Name of the job to retrieve") do |n|
|
101
|
+
options[:jobname] = n
|
102
|
+
end
|
103
|
+
|
104
|
+
opts.on("-d", "--delete", String, "Job: If -j is given, delete the job") do |d|
|
105
|
+
options[:delete] = true
|
106
|
+
end
|
107
|
+
|
108
|
+
opts.on("-B", "--build-job", String, "Job: Launches a new build on the given job") do |tst|
|
109
|
+
options[:tests] = tst
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
p.parse!(ARGV)
|
115
|
+
|
116
|
+
if options[:auth] == nil
|
117
|
+
config = File.expand_path("~/.mjenk")
|
118
|
+
else
|
119
|
+
config = options[:auth]
|
120
|
+
end
|
121
|
+
|
122
|
+
auth = AutoJenkins::Helpers.parseConfig(config)
|
123
|
+
vCreds = [auth['USER'], auth['PASSWD']]
|
124
|
+
vURL = auth['URL']
|
125
|
+
|
126
|
+
if options[:token] == nil
|
127
|
+
# Wasn't overriden..
|
128
|
+
|
129
|
+
if auth.has_key?('TOKEN')
|
130
|
+
options[:token] = auth['TOKEN']
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
cCMD = nil
|
135
|
+
mJENK = AutoJenkins::Jenkins.new(vURL, vCreds)
|
136
|
+
|
137
|
+
# Lets process the CMD
|
138
|
+
cCMD = "job"
|
139
|
+
if ARGV.length > 0
|
140
|
+
cCMD = ARGV[0]
|
141
|
+
end
|
142
|
+
|
143
|
+
if cCommands.key?(cCMD)
|
144
|
+
cmd = cCommands[cCMD].new(mJENK)
|
145
|
+
|
146
|
+
begin
|
147
|
+
cmd.exec(options)
|
148
|
+
rescue AutoJenkins::ExUnauthorized => e1
|
149
|
+
puts e1.message
|
150
|
+
puts "Got an Unathorized error. Check your credentials."
|
151
|
+
rescue AutoJenkins::ExUndefined => e2
|
152
|
+
puts "Got an Undefined error. Maybe trying to build a disabled job?"
|
153
|
+
puts e2.message
|
154
|
+
end
|
155
|
+
|
156
|
+
if cmd.error != 0
|
157
|
+
puts cmd.errmsg
|
158
|
+
exit cmd.error
|
159
|
+
end
|
160
|
+
end
|
data/lib/autojenkins.rb
ADDED
@@ -0,0 +1,294 @@
|
|
1
|
+
#Module autojenkins
|
2
|
+
#Ruby module to talk with Jenkins using JSON API
|
3
|
+
#
|
4
|
+
|
5
|
+
require 'pp'
|
6
|
+
require 'uri'
|
7
|
+
require 'json'
|
8
|
+
require 'yaml'
|
9
|
+
require 'net/http'
|
10
|
+
require 'nokogiri'
|
11
|
+
|
12
|
+
module AutoJenkins
|
13
|
+
|
14
|
+
LOGIN = 'LOGIN'
|
15
|
+
PASSWD = 'PASSWD'
|
16
|
+
BASE_URL = "/api/json"
|
17
|
+
|
18
|
+
URLs = {
|
19
|
+
'LIST' => "#{BASE_URL}",
|
20
|
+
'JOBINFO' => "/job/%s#{BASE_URL}",
|
21
|
+
'CONFIG' => "/job/%s/config.xml",
|
22
|
+
'_ABLE' => "/job/%s/%s",
|
23
|
+
'DELETE' => "/job/%s/doDelete",
|
24
|
+
'NEWJOB' => "/createItem?name=%s",
|
25
|
+
'LAUNCH' => "/job/%s/build/api/json",
|
26
|
+
'BUILD' => "/job/%s/%i#{BASE_URL}",
|
27
|
+
}
|
28
|
+
|
29
|
+
class JenkinsException < Exception
|
30
|
+
end
|
31
|
+
|
32
|
+
class ExUnauthorized < Exception
|
33
|
+
end
|
34
|
+
|
35
|
+
class ExUndefined < Exception
|
36
|
+
end
|
37
|
+
|
38
|
+
class Helpers
|
39
|
+
|
40
|
+
def self.parseConfig(config)
|
41
|
+
tree = {}
|
42
|
+
|
43
|
+
begin
|
44
|
+
tree = YAML::parse(File.open(config)).transform
|
45
|
+
rescue
|
46
|
+
raise IOError, "Config file not found"
|
47
|
+
end
|
48
|
+
|
49
|
+
['URL', 'USER', 'PASSWD'].each do |k|
|
50
|
+
if not tree.has_key?(k)
|
51
|
+
raise IndexError.new("Key %s not found in config file" % k)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
return tree
|
56
|
+
end
|
57
|
+
|
58
|
+
def self._get_request(jurl, command, args, auth,
|
59
|
+
post_args=nil, content_type=nil)
|
60
|
+
|
61
|
+
url = Helpers._build_url(jurl, command, args)
|
62
|
+
|
63
|
+
url_str = (url.query == nil) ? url.path : "#{url.path}?#{url.query}"
|
64
|
+
|
65
|
+
if post_args.nil?:
|
66
|
+
req = Net::HTTP::Get.new(url_str)
|
67
|
+
else
|
68
|
+
req = Net::HTTP::Post.new(url_str)
|
69
|
+
req.body = post_args
|
70
|
+
end
|
71
|
+
|
72
|
+
req.basic_auth auth[LOGIN], auth[PASSWD]
|
73
|
+
|
74
|
+
res = Net::HTTP.start(url.host, url.port) {|http|
|
75
|
+
|
76
|
+
if content_type
|
77
|
+
req["Content-Type"] = content_type
|
78
|
+
end
|
79
|
+
|
80
|
+
ret = http.request(req)
|
81
|
+
|
82
|
+
if not [Net::HTTPOK, Net::HTTPFound, Net::HTTPNotFound].include? ret.class
|
83
|
+
if ret.instance_of? Net::HTTPUnauthorized
|
84
|
+
raise ExUnauthorized, "Unauthorized, check your credentials"
|
85
|
+
else
|
86
|
+
raise ExUndefined, "Http request failed!"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
ret
|
91
|
+
}
|
92
|
+
|
93
|
+
#Some requests do not return JSON. Such as config.xml
|
94
|
+
ct = res.get_fields('Content-type')
|
95
|
+
|
96
|
+
if ct.nil?
|
97
|
+
ct = "text/plain"
|
98
|
+
else
|
99
|
+
ct = ct[0]
|
100
|
+
end
|
101
|
+
|
102
|
+
ctype = ct.split(';')[0]
|
103
|
+
|
104
|
+
if ctype == 'application/javascript' or
|
105
|
+
ctype == 'application/json'
|
106
|
+
#handle JSON
|
107
|
+
begin
|
108
|
+
return JSON.parse(res.body)
|
109
|
+
rescue JSON::ParserError
|
110
|
+
return "{}"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
unless res.body.nil?
|
115
|
+
return res.body
|
116
|
+
end
|
117
|
+
|
118
|
+
return "{}"
|
119
|
+
end
|
120
|
+
|
121
|
+
def self._build_url(jurl, command, args)
|
122
|
+
return URI.parse("#{jurl}" + (URLs[command] % args))
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
class Build
|
128
|
+
attr_accessor :name,
|
129
|
+
:url,
|
130
|
+
:revision,
|
131
|
+
:branch,
|
132
|
+
:revision,
|
133
|
+
:status,
|
134
|
+
:building,
|
135
|
+
:node,
|
136
|
+
:duration
|
137
|
+
def ppretty()
|
138
|
+
print "Name: #{name}\n"
|
139
|
+
print "\tBranch: #{branch}\n"
|
140
|
+
print "\tRevision: #{revision}\n"
|
141
|
+
print "\tStatus: #{status}\n"
|
142
|
+
print "\tDuration: #{duration}\n"
|
143
|
+
print "\tNode: #{node}\n"
|
144
|
+
print "\tBuilding: #{building}\n"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
class Job
|
149
|
+
attr_accessor :info, :name, :jurl, :auth, :is_set
|
150
|
+
|
151
|
+
def initialize(name, jurl, auth, token=nil)
|
152
|
+
@name = name
|
153
|
+
@jurl = jurl
|
154
|
+
@auth = auth
|
155
|
+
@info = false
|
156
|
+
|
157
|
+
if name
|
158
|
+
@e_name = URI.escape(@name)
|
159
|
+
@info = Helpers._get_request(@jurl, 'JOBINFO', URI.escape(name), auth)
|
160
|
+
end
|
161
|
+
|
162
|
+
@is_set = @info ? true : false
|
163
|
+
end
|
164
|
+
|
165
|
+
def launch(token)
|
166
|
+
res = Helpers._get_request(@jurl, 'LAUNCH',
|
167
|
+
[@e_name, token], @auth)
|
168
|
+
return true
|
169
|
+
end
|
170
|
+
|
171
|
+
def delete()
|
172
|
+
res = Helpers._get_request(@jurl, 'DELETE',
|
173
|
+
[@e_name], @auth, post_args="delete=true")
|
174
|
+
return true
|
175
|
+
end
|
176
|
+
|
177
|
+
def get_build(buildnum)
|
178
|
+
res = Helpers._get_request(@jurl, 'BUILD',
|
179
|
+
[@e_name, buildnum], @auth)
|
180
|
+
|
181
|
+
build = Build.new()
|
182
|
+
begin
|
183
|
+
build.branch = res['actions'][1]['lastBuiltRevision']['branch'][0]['name']
|
184
|
+
build.revision = res['actions'][1]['lastBuiltRevision']['branch'][0]['SHA1']
|
185
|
+
rescue
|
186
|
+
end
|
187
|
+
|
188
|
+
build.status = res['result']
|
189
|
+
build.name = res['fullDisplayName']
|
190
|
+
build.duration = res['duration']
|
191
|
+
build.building = res['building']
|
192
|
+
build.url = res['url']
|
193
|
+
build.node = res['builtOn']
|
194
|
+
|
195
|
+
return build
|
196
|
+
end
|
197
|
+
|
198
|
+
def info_items()
|
199
|
+
return @info.keys
|
200
|
+
end
|
201
|
+
|
202
|
+
def _able_job(action)
|
203
|
+
res = Helpers._get_request(@jurl, '_ABLE', [@e_name, action], @auth, post_args={'data' => ""})
|
204
|
+
return true
|
205
|
+
end
|
206
|
+
|
207
|
+
def disable()
|
208
|
+
return _able_job('disable')
|
209
|
+
end
|
210
|
+
|
211
|
+
def enable()
|
212
|
+
return _able_job('enable')
|
213
|
+
end
|
214
|
+
|
215
|
+
def copy(jobname, enable=false)
|
216
|
+
config = get_config()
|
217
|
+
return create_job(jobname, config, enable)
|
218
|
+
end
|
219
|
+
|
220
|
+
def create_job(jobname, config, enable=false)
|
221
|
+
sts = Helpers._get_request(@jurl, 'NEWJOB', [URI.escape(jobname)],
|
222
|
+
@auth, post_args=config, content_type="application/xml")
|
223
|
+
|
224
|
+
newjob = Job.new(jobname, @jurl, @auth)
|
225
|
+
|
226
|
+
unless enable
|
227
|
+
newjob.disable()
|
228
|
+
end
|
229
|
+
|
230
|
+
return newjob
|
231
|
+
end
|
232
|
+
|
233
|
+
def get_config()
|
234
|
+
if @is_set
|
235
|
+
config_xml = Helpers._get_request(@jurl, 'CONFIG', [URI.escape(@name)], @auth)
|
236
|
+
return config_xml
|
237
|
+
end
|
238
|
+
return ""
|
239
|
+
end
|
240
|
+
|
241
|
+
def is_set?()
|
242
|
+
return @is_set
|
243
|
+
end
|
244
|
+
|
245
|
+
def method_missing(name)
|
246
|
+
|
247
|
+
name.to_s =~ /^get_(.*)$/
|
248
|
+
match = $1
|
249
|
+
|
250
|
+
unless match.nil?
|
251
|
+
if @info.has_key? $1
|
252
|
+
return @info[$1]
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
return nil
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
class Jenkins
|
261
|
+
def initialize(s_uri, auth_info, debug=false)
|
262
|
+
@debug = debug
|
263
|
+
@jurl = s_uri
|
264
|
+
@auth = {LOGIN => auth_info[0], PASSWD => auth_info[1]}
|
265
|
+
end
|
266
|
+
|
267
|
+
def get_job(jobname)
|
268
|
+
return Job.new(jobname, @jurl, @auth)
|
269
|
+
end
|
270
|
+
|
271
|
+
def delete_job(jobname)
|
272
|
+
j = get_job(jobname)
|
273
|
+
res = j.delete()
|
274
|
+
return res
|
275
|
+
end
|
276
|
+
|
277
|
+
def create_from_xml(jobname, config_xml)
|
278
|
+
newjob = Job.new(jobname, @jurl, @auth)
|
279
|
+
newjob = newjob.create_job(jobname, config_xml, enable='true')
|
280
|
+
return newjob
|
281
|
+
end
|
282
|
+
|
283
|
+
def all_jobs()
|
284
|
+
ob_jobs = []
|
285
|
+
jobs = Helpers._get_request(@jurl, 'LIST', [], @auth)
|
286
|
+
|
287
|
+
jobs['jobs'].each do |j|
|
288
|
+
ob_jobs << Job.new(j['name'], @jurl, @auth)
|
289
|
+
end
|
290
|
+
|
291
|
+
return ob_jobs
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
data/lib/test.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'autojenkins'
|
4
|
+
|
5
|
+
URL='http://jenkins.ng.com:8080'
|
6
|
+
AUTH=["neurogeek", "123qwe4r!!"]
|
7
|
+
#TOKEN=""
|
8
|
+
|
9
|
+
if URL == ''
|
10
|
+
puts "To use this functions, please configure URL and AUTH"
|
11
|
+
end
|
12
|
+
JENK = AutoJenkins::Jenkins.new(URL, AUTH)
|
13
|
+
|
14
|
+
def ListAll()
|
15
|
+
jobs = JENK.all_jobs()
|
16
|
+
if jobs
|
17
|
+
jobs.each do |jb|
|
18
|
+
print "#{jb.name} --> #{jb.get_url}\n"
|
19
|
+
print "DisplayName: #{jb.get_displayName}\n"
|
20
|
+
jb.info_items.each do |i|
|
21
|
+
puts i
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def CreateFromJob(jobname, newjobname)
|
28
|
+
job = JENK.get_job(jobname)
|
29
|
+
job2 = job.copy(newjobname)
|
30
|
+
print "#{job2.name}\n"
|
31
|
+
end
|
32
|
+
|
33
|
+
def GetBuildInfo(jobname, buildnum)
|
34
|
+
job = JENK.get_job(jobname)
|
35
|
+
bld = job.get_build(buildnum)
|
36
|
+
return bld
|
37
|
+
end
|
38
|
+
|
39
|
+
def GetConfig(jobname)
|
40
|
+
job = JENK.get_job(jobname)
|
41
|
+
return job.get_config()
|
42
|
+
end
|
43
|
+
|
44
|
+
def GetJob(jobname)
|
45
|
+
return JENK.get_job(jobname)
|
46
|
+
end
|
47
|
+
|
48
|
+
def GetInfo(jobname)
|
49
|
+
def pretty_print(entry, tab=1)
|
50
|
+
|
51
|
+
if entry.kind_of? Hash
|
52
|
+
entry.keys.each do |k|
|
53
|
+
print " " * tab * 2
|
54
|
+
print "#{k} ----> \n"
|
55
|
+
pretty_print(entry[k], tab=tab+1)
|
56
|
+
end
|
57
|
+
elsif entry == nil
|
58
|
+
print " " * tab * 2
|
59
|
+
print "[NONE]\n"
|
60
|
+
elsif entry.kind_of? Array
|
61
|
+
entry.each do |v|
|
62
|
+
pretty_print(v, tab+1)
|
63
|
+
end
|
64
|
+
else
|
65
|
+
print "\t" * tab
|
66
|
+
print "#{entry}\n"
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
job = JENK.get_job(jobname)
|
71
|
+
job.info.keys.each do |k|
|
72
|
+
print "#{k} ----> \n"
|
73
|
+
pretty_print job.info[k]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Uncomment to test functions
|
78
|
+
#ListAll()
|
79
|
+
CreateFromJob("TestJOb", "NewJob")
|
80
|
+
#puts GetBuildInfo("TestJOb", 1)
|
81
|
+
#puts GetConfig("TestJOb")
|
82
|
+
#puts GetInfo("TestJOb")
|
metadata
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: autojenkins
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 9
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: "0.1"
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Jesus Rivero
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2012-06-01 00:00:00 Z
|
18
|
+
dependencies: []
|
19
|
+
|
20
|
+
description: Library to remotely control Jenkins CI. Autojenkins can fetch, create, enable/disable and launch jobs in Jenkins
|
21
|
+
email: jesus.riveroa@gmail.com
|
22
|
+
executables:
|
23
|
+
- mjenk
|
24
|
+
extensions: []
|
25
|
+
|
26
|
+
extra_rdoc_files: []
|
27
|
+
|
28
|
+
files:
|
29
|
+
- lib/autojenkins.rb
|
30
|
+
- lib/test.rb
|
31
|
+
- bin/mjenk
|
32
|
+
homepage: https://github.com/neurogeek/autojenkins-rb
|
33
|
+
licenses:
|
34
|
+
- MIT
|
35
|
+
post_install_message:
|
36
|
+
rdoc_options: []
|
37
|
+
|
38
|
+
require_paths:
|
39
|
+
- lib
|
40
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 3
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
version: "0"
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
hash: 3
|
55
|
+
segments:
|
56
|
+
- 0
|
57
|
+
version: "0"
|
58
|
+
requirements:
|
59
|
+
- nokogiri
|
60
|
+
- json
|
61
|
+
rubyforge_project:
|
62
|
+
rubygems_version: 1.8.24
|
63
|
+
signing_key:
|
64
|
+
specification_version: 3
|
65
|
+
summary: Library to remotely control Jenkins CI
|
66
|
+
test_files: []
|
67
|
+
|