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.
Files changed (4) hide show
  1. data/bin/mjenk +160 -0
  2. data/lib/autojenkins.rb +294 -0
  3. data/lib/test.rb +82 -0
  4. 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
@@ -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
+