jenkins_job 0.1.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/.gitignore +4 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +25 -0
- data/README.rdoc +46 -0
- data/Rakefile +26 -0
- data/bin/jenkins_job +3 -0
- data/jenkins_job.gemspec +26 -0
- data/lib/jenkins_job.rb +9 -0
- data/lib/jenkins_job/cli.rb +78 -0
- data/lib/jenkins_job/project.rb +92 -0
- data/lib/jenkins_job/version.rb +3 -0
- data/test/cli_test.rb +202 -0
- data/test/fixtures/foo_master/config.xml +111 -0
- data/test/fixtures/foo_production/config.xml +111 -0
- data/test/project_test.rb +115 -0
- data/test/test_helper.rb +20 -0
- metadata +160 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
jenkins_job (0.1.0)
|
5
|
+
thor (~> 0.14.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
fakefs (0.3.1)
|
11
|
+
mocha (0.9.4)
|
12
|
+
rake
|
13
|
+
rake (0.8.7)
|
14
|
+
shoulda (2.11.3)
|
15
|
+
thor (0.14.6)
|
16
|
+
|
17
|
+
PLATFORMS
|
18
|
+
ruby
|
19
|
+
|
20
|
+
DEPENDENCIES
|
21
|
+
bundler (~> 1.0.0)
|
22
|
+
fakefs (~> 0.3.1)
|
23
|
+
jenkins_job!
|
24
|
+
mocha
|
25
|
+
shoulda
|
data/README.rdoc
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
= jenkins_job
|
2
|
+
|
3
|
+
jenkins_job is a cli tool that simplifies the management of Jenkins/Hudson jobs.
|
4
|
+
|
5
|
+
Most of my projects have more than one branch to keep tested.
|
6
|
+
I prefer to have a Jenkins' job per branch, instead of having a single job with
|
7
|
+
multiple branches. This leads to have multiple similar jobs to maintain.
|
8
|
+
|
9
|
+
== Common workflow
|
10
|
+
|
11
|
+
Make sure your job name contains a reference to the branch being tested, e.g.:
|
12
|
+
<project_name>_<branch_name>
|
13
|
+
|
14
|
+
Manage the project testing the master/trunk branch using the web interface.
|
15
|
+
Then clone it and change the branch to be tested using the following command:
|
16
|
+
jenkins_job clone foo_master --original_branch master --new_branch production
|
17
|
+
|
18
|
+
The previous command uses the foo_master job as a reference and creates a
|
19
|
+
new project named foo_production testing the production branch.
|
20
|
+
|
21
|
+
Note well: the command must be executed within the directory containing
|
22
|
+
Jenkins' jobs, which is <jenkins_home>/jobs.
|
23
|
+
|
24
|
+
== Other features
|
25
|
+
jenkins_job can help you in other ways.
|
26
|
+
|
27
|
+
== Disable/enable projects
|
28
|
+
It's possible to enable or disable multiple projects with a simple command:
|
29
|
+
jenkins_job enable|disable project1 project2 project3 ...
|
30
|
+
|
31
|
+
Thanks to bash file globbing you can change all the 'master' projects by typing:
|
32
|
+
jenkins_job enable|disable foo_*
|
33
|
+
|
34
|
+
== Copy settings to other projects
|
35
|
+
Suppose you have configured irc notifications for one of your projects and you
|
36
|
+
want to copy this configuration to other jobs.
|
37
|
+
|
38
|
+
Irc notification plugin saves its settings under the following xml node:
|
39
|
+
project/hudson.plugins.ircbot.IrcPublisher
|
40
|
+
|
41
|
+
Just execute this command:
|
42
|
+
jenkins_job copy_setting hudson.plugins.ircbot.IrcPublisher foo_master bar_master
|
43
|
+
|
44
|
+
Now the bar_master project will have the irc notifications turned on and
|
45
|
+
configured like foo_master.
|
46
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
require 'rake/testtask'
|
5
|
+
Bundler::GemHelper.install_tasks
|
6
|
+
|
7
|
+
task :default => "test"
|
8
|
+
|
9
|
+
require 'rake/testtask'
|
10
|
+
Rake::TestTask.new(:test) do |test|
|
11
|
+
test.libs << 'lib' << 'test'
|
12
|
+
test.pattern = 'test/**/*_test.rb'
|
13
|
+
test.verbose = true
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'Generate documentation.'
|
17
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
18
|
+
rdoc.rdoc_dir = 'rdoc'
|
19
|
+
rdoc.title = 'jenkins_job'
|
20
|
+
rdoc.options << '--line-numbers' << "--main" << "README.rdoc"
|
21
|
+
rdoc.rdoc_files.include('README.rdoc')
|
22
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
23
|
+
end
|
24
|
+
|
25
|
+
desc "Clean files generated by rake tasks"
|
26
|
+
task :clobber => [:clobber_rdoc]
|
data/bin/jenkins_job
ADDED
data/jenkins_job.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path("../lib/jenkins_job/version", __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "jenkins_job"
|
6
|
+
s.version = JenkinsJob::VERSION
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.authors = ['Flavio Castelli']
|
9
|
+
s.email = ['flavio@castelli.name']
|
10
|
+
# s.homepage = "https://features.opensuse.org/311133"
|
11
|
+
s.summary = "Simple cli tool created to manage Jenkins job files."
|
12
|
+
s.description = "Handle your Jenkins job configuration files in an easier way."
|
13
|
+
|
14
|
+
s.required_rubygems_version = ">= 1.3.6"
|
15
|
+
s.rubyforge_project = "jenkins_job"
|
16
|
+
|
17
|
+
s.add_dependency "thor", "~>0.14.0"
|
18
|
+
|
19
|
+
s.add_development_dependency "bundler", "~>1.0.0"
|
20
|
+
s.add_development_dependency "fakefs", "~>0.3.1"
|
21
|
+
s.add_development_dependency "mocha"
|
22
|
+
s.add_development_dependency "shoulda"
|
23
|
+
s.files = `git ls-files`.split("\n")
|
24
|
+
s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
|
25
|
+
s.require_path = 'lib'
|
26
|
+
end
|
data/lib/jenkins_job.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
require File.expand_path('../jenkins_job/cli', __FILE__)
|
5
|
+
require File.expand_path('../jenkins_job/project', __FILE__)
|
6
|
+
|
7
|
+
module JenkinsJob
|
8
|
+
autoload :Version, File.expand_path('../jenkins_job/version', __FILE__)
|
9
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module JenkinsJob
|
4
|
+
class Cli < Thor
|
5
|
+
include Thor::Actions
|
6
|
+
|
7
|
+
desc "clone PROJECT [PROJECT, ...]", "Clone project changing its name and its branch"
|
8
|
+
method_option :original_branch, :default => 'master', :type => :string
|
9
|
+
method_option :new_branch, :type => :string
|
10
|
+
method_option :force, :type => :boolean
|
11
|
+
def clone *project
|
12
|
+
force = options[:force] || false
|
13
|
+
project.each do |p|
|
14
|
+
# calculate the new dir
|
15
|
+
new_dir = p.gsub(options[:original_branch],
|
16
|
+
options[:new_branch])
|
17
|
+
|
18
|
+
if File.exists?(new_dir)
|
19
|
+
if !force && !yes?("#{new_dir} already exists, delete it? [y/n]")
|
20
|
+
say "#{p} -> #{new_dir} - skipped by user choice", :yellow
|
21
|
+
next
|
22
|
+
else
|
23
|
+
FileUtils.rm_rf(new_dir)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
FileUtils.mkdir_p new_dir
|
28
|
+
config_lines = File.open(File.join(p, 'config.xml'), 'r').readlines
|
29
|
+
File.open(File.join(new_dir, 'config.xml'), 'w') do |file|
|
30
|
+
config_lines.each do |line|
|
31
|
+
file.write(line.gsub(options[:original_branch],
|
32
|
+
options[:new_branch]))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
say "#{p} -> #{new_dir} - done", :green
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
desc "clean PROJECT [PROJECT, ...]",
|
40
|
+
"Remove all the useless files generated by hudson from the specified projects"
|
41
|
+
def clean *project
|
42
|
+
project.each do |p|
|
43
|
+
Project.new(p).clean
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
desc "disable PROJECT [PROJECT, ...]", "disable the specified projects"
|
48
|
+
def disable *project
|
49
|
+
project.each do |p|
|
50
|
+
Project.new(p).disable
|
51
|
+
puts "#{p} - disabled"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
desc "enable PROJECT [PROJECT, ...]", "enable the specified projects"
|
56
|
+
def enable *project
|
57
|
+
project.each do |p|
|
58
|
+
Project.new(p).enable
|
59
|
+
puts "#{p} - enabled"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
desc "copy_setting SOURCE_PROJECT SETTING_NAME DEST_PROJECT",
|
64
|
+
"Copy SETTING_NAME from SOURCE_PROJECT to DEST_PROJECT"
|
65
|
+
def copy_setting source_project, setting_name, dest_project
|
66
|
+
source = Project.new(source_project)
|
67
|
+
dest = Project.new(dest_project)
|
68
|
+
|
69
|
+
setting = source.setting(setting_name)
|
70
|
+
if setting.nil?
|
71
|
+
say "#{source_project} doesn't contain the specified setting", :red
|
72
|
+
else
|
73
|
+
dest.update_setting(setting_name, setting)
|
74
|
+
say "#{setting_name} copied", :green
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
|
3
|
+
module JenkinsJob
|
4
|
+
class Project
|
5
|
+
|
6
|
+
def initialize dir
|
7
|
+
raise "#{dir} does not exists" unless File.exists? dir
|
8
|
+
raise "#{dir} is not a directory" unless File.directory?(dir)
|
9
|
+
|
10
|
+
@config_file = File.join(dir, 'config.xml')
|
11
|
+
if !File.exists?(@config_file)
|
12
|
+
raise "#{dir} doesn't contain config.xml file"
|
13
|
+
end
|
14
|
+
@dir = dir
|
15
|
+
end
|
16
|
+
|
17
|
+
# removes all the useless files generated by hudson, keeps only config.xml
|
18
|
+
def clean
|
19
|
+
Dir.chdir(@dir) do
|
20
|
+
['builds', 'last*', '*.log', 'next*'].each do |pattern|
|
21
|
+
FileUtils.rm_rf Dir.glob(pattern)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def enabled?
|
27
|
+
!disabled?
|
28
|
+
end
|
29
|
+
|
30
|
+
def disabled?
|
31
|
+
doc = REXML::Document.new(File.open(@config_file, 'r'))
|
32
|
+
disabled_node = doc.elements['project/disabled']
|
33
|
+
return false if disabled_node.nil?
|
34
|
+
return true if disabled_node.text.downcase == 'true'
|
35
|
+
false
|
36
|
+
end
|
37
|
+
|
38
|
+
# disable the project
|
39
|
+
def disable
|
40
|
+
doc = REXML::Document.new(File.open(@config_file, 'r'))
|
41
|
+
disabled_node = doc.elements['project/disabled']
|
42
|
+
if disabled_node.nil?
|
43
|
+
disabled_node = REXML::Element.new 'disabled'
|
44
|
+
disabled_node.text = 'true'
|
45
|
+
project_node = doc.elements['project']
|
46
|
+
project_node << disabled_node
|
47
|
+
else
|
48
|
+
if !disabled_node.text.downcase == 'true'
|
49
|
+
return
|
50
|
+
else
|
51
|
+
disabled_node.text = 'true'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
File.open(@config_file, File::WRONLY|File::TRUNC) do |file|
|
55
|
+
doc.write file
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# enable the project
|
60
|
+
def enable
|
61
|
+
doc = REXML::Document.new(File.open(@config_file, 'r'))
|
62
|
+
disabled_node = doc.elements['project/disabled']
|
63
|
+
if !disabled_node.nil?
|
64
|
+
disabled_node.text = "false"
|
65
|
+
File.open(@config_file, File::WRONLY|File::TRUNC) do |file|
|
66
|
+
doc.write file
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns a REXML::Element identified by project/name
|
72
|
+
def setting name
|
73
|
+
doc = REXML::Document.new(File.open(@config_file, 'r'))
|
74
|
+
doc.elements["project/#{name}"]
|
75
|
+
end
|
76
|
+
|
77
|
+
# Update setting
|
78
|
+
# name: the name of the setting
|
79
|
+
# node: the REXML::Element to insert
|
80
|
+
def update_setting name, node
|
81
|
+
doc = REXML::Document.new(File.open(@config_file, 'r'))
|
82
|
+
old = doc.elements["project/#{name}"]
|
83
|
+
doc.root.elements.delete(old) unless old.nil?
|
84
|
+
project_node = doc.elements['project']
|
85
|
+
project_node << node
|
86
|
+
File.open(@config_file, File::WRONLY|File::TRUNC) do |file|
|
87
|
+
doc.write file
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
data/test/cli_test.rb
ADDED
@@ -0,0 +1,202 @@
|
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
|
3
|
+
class CliTest < Test::Unit::TestCase
|
4
|
+
context 'clean' do
|
5
|
+
should 'handle more than one project per time' do
|
6
|
+
projects = %w(foo bar foobar)
|
7
|
+
|
8
|
+
fake_project = mock()
|
9
|
+
fake_project.expects(:clean)\
|
10
|
+
.times(projects.size)\
|
11
|
+
.returns(true)
|
12
|
+
|
13
|
+
projects.each do |p|
|
14
|
+
JenkinsJob::Project.expects(:new).with(p)\
|
15
|
+
.returns(fake_project)
|
16
|
+
end
|
17
|
+
cmd = ['clean'] + projects
|
18
|
+
capture(:stdout) do
|
19
|
+
JenkinsJob::Cli.start(cmd)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'disable' do
|
25
|
+
should 'handle more than one project per time' do
|
26
|
+
projects = %w(foo bar foobar)
|
27
|
+
|
28
|
+
fake_project = mock()
|
29
|
+
fake_project.expects(:disable)\
|
30
|
+
.times(projects.size)\
|
31
|
+
.returns(true)
|
32
|
+
|
33
|
+
projects.each do |p|
|
34
|
+
JenkinsJob::Project.expects(:new).with(p)\
|
35
|
+
.returns(fake_project)
|
36
|
+
end
|
37
|
+
cmd = ['disable'] + projects
|
38
|
+
capture(:stdout) do
|
39
|
+
JenkinsJob::Cli.start(cmd)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'enable' do
|
45
|
+
should 'handle more than one project per time' do
|
46
|
+
projects = %w(foo bar foobar)
|
47
|
+
|
48
|
+
fake_project = mock()
|
49
|
+
fake_project.expects(:enable)\
|
50
|
+
.times(projects.size)\
|
51
|
+
.returns(true)
|
52
|
+
|
53
|
+
projects.each do |p|
|
54
|
+
JenkinsJob::Project.expects(:new).with(p)\
|
55
|
+
.returns(fake_project)
|
56
|
+
end
|
57
|
+
cmd = ['enable'] + projects
|
58
|
+
capture(:stdout) do
|
59
|
+
JenkinsJob::Cli.start(cmd)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'clone' do
|
65
|
+
setup do
|
66
|
+
File.open(File.expand_path('../fixtures/foo_master/config.xml',
|
67
|
+
__FILE__), 'r') do |file|
|
68
|
+
@source_xml = file.readlines
|
69
|
+
end
|
70
|
+
|
71
|
+
File.open(File.expand_path('../fixtures/foo_production/config.xml',
|
72
|
+
__FILE__), 'r') do |file|
|
73
|
+
@expected_xml = file.readlines
|
74
|
+
end
|
75
|
+
|
76
|
+
FakeFS.activate!
|
77
|
+
FakeFS::FileSystem.clear
|
78
|
+
FileUtils.mkdir_p 'foo_master'
|
79
|
+
File.open('foo_master/config.xml', 'w') do |file|
|
80
|
+
file.write @source_xml
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
teardown do
|
85
|
+
FakeFS.deactivate!
|
86
|
+
end
|
87
|
+
|
88
|
+
should 'clone some projects' do
|
89
|
+
capture(:stdout) do
|
90
|
+
JenkinsJob::Cli.start(['clone', 'foo_master', '--original_branch',
|
91
|
+
'master', '--new_branch', 'production'])
|
92
|
+
end
|
93
|
+
|
94
|
+
assert File.directory? 'foo_production'
|
95
|
+
assert File.exists? 'foo_production/config.xml'
|
96
|
+
actual_xml = File.open('foo_production/config.xml', 'r') do |file|
|
97
|
+
file.readlines
|
98
|
+
end
|
99
|
+
assert_equal @expected_xml, actual_xml
|
100
|
+
end
|
101
|
+
|
102
|
+
context 'final project already exists' do
|
103
|
+
setup do
|
104
|
+
FileUtils.mkdir_p 'foo_production'
|
105
|
+
FileUtils.touch 'foo_production/config.xml'
|
106
|
+
end
|
107
|
+
|
108
|
+
teardown do
|
109
|
+
FileUtils.rm_rf 'foo_production'
|
110
|
+
end
|
111
|
+
|
112
|
+
context 'force overwrite enabled' do
|
113
|
+
should 'not prompt user when force is set to true' do
|
114
|
+
JenkinsJob::Cli.any_instance.expects(:yes?).never
|
115
|
+
|
116
|
+
capture(:stdout) do
|
117
|
+
JenkinsJob::Cli.start(['clone', 'foo_master', '--original_branch',
|
118
|
+
'master', '--new_branch', 'production',
|
119
|
+
'--force'])
|
120
|
+
end
|
121
|
+
|
122
|
+
assert File.directory? 'foo_production'
|
123
|
+
assert File.exists? 'foo_production/config.xml'
|
124
|
+
actual_xml = File.open('foo_production/config.xml', 'r') do |file|
|
125
|
+
file.readlines
|
126
|
+
end
|
127
|
+
assert_equal @expected_xml, actual_xml
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context 'force overwrite is not set' do
|
132
|
+
context 'user wants to overwrite' do
|
133
|
+
should 'overwrite the old contents and create the new ones' do
|
134
|
+
JenkinsJob::Cli.any_instance.expects(:yes?).once.returns(true)
|
135
|
+
capture(:stdout) do
|
136
|
+
JenkinsJob::Cli.start(['clone', 'foo_master', '--original_branch',
|
137
|
+
'master', '--new_branch', 'production'])
|
138
|
+
end
|
139
|
+
assert File.directory? 'foo_production'
|
140
|
+
assert File.exists? 'foo_production/config.xml'
|
141
|
+
actual_xml = File.open('foo_production/config.xml', 'r') do |file|
|
142
|
+
file.readlines
|
143
|
+
end
|
144
|
+
assert_equal @expected_xml, actual_xml
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
context "user doesn't want to overwrite" do
|
149
|
+
should 'preserve the old contents' do
|
150
|
+
JenkinsJob::Cli.any_instance.expects(:yes?).once.returns(false)
|
151
|
+
capture(:stdout) do
|
152
|
+
JenkinsJob::Cli.start(['clone', 'foo_master', '--original_branch',
|
153
|
+
'master', '--new_branch', 'production'])
|
154
|
+
end
|
155
|
+
assert File.directory? 'foo_production'
|
156
|
+
assert File.exists? 'foo_production/config.xml'
|
157
|
+
actual_xml = File.open('foo_production/config.xml', 'r') do |file|
|
158
|
+
file.readlines
|
159
|
+
end
|
160
|
+
assert actual_xml.empty?
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
context 'copy settings' do
|
169
|
+
should 'spot a missing setting' do
|
170
|
+
source = mock()
|
171
|
+
source.expects(:setting).with('irc_bot').once.returns(nil)
|
172
|
+
|
173
|
+
dest = mock()
|
174
|
+
dest.expects(:update_setting).never
|
175
|
+
|
176
|
+
JenkinsJob::Project.expects(:new).twice.returns(source).then.returns(dest)
|
177
|
+
JenkinsJob::Cli.any_instance.expects(:say).with(is_a(String), :red).once
|
178
|
+
|
179
|
+
capture(:stdout) do
|
180
|
+
JenkinsJob::Cli.start(['copy_setting', 'foo_master', 'irc_bot',
|
181
|
+
'foo_production'])
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
should 'handle an existing setting' do
|
187
|
+
source = mock()
|
188
|
+
source.expects(:setting).with('irc_bot').once.returns('foo')
|
189
|
+
|
190
|
+
dest = mock()
|
191
|
+
dest.expects(:update_setting).with('irc_bot', 'foo').once
|
192
|
+
|
193
|
+
JenkinsJob::Project.expects(:new).twice.returns(source).then.returns(dest)
|
194
|
+
JenkinsJob::Cli.any_instance.expects(:say).with(is_a(String), :green).once
|
195
|
+
capture(:stdout) do
|
196
|
+
JenkinsJob::Cli.start(['copy_setting', 'foo_master', 'irc_bot',
|
197
|
+
'foo_production'])
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|
202
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
<?xml version='1.0' encoding='UTF-8'?>
|
2
|
+
<project>
|
3
|
+
<actions/>
|
4
|
+
<description>foo [master] tests</description>
|
5
|
+
<keepDependencies>false</keepDependencies>
|
6
|
+
<properties/>
|
7
|
+
<scm class='hudson.plugins.git.GitSCM'>
|
8
|
+
<configVersion>1</configVersion>
|
9
|
+
<remoteRepositories>
|
10
|
+
<org.spearce.jgit.transport.RemoteConfig>
|
11
|
+
<string>origin</string>
|
12
|
+
<int>5</int>
|
13
|
+
<string>fetch</string>
|
14
|
+
<string>+refs/heads/*:refs/remotes/origin/*</string>
|
15
|
+
<string>receivepack</string>
|
16
|
+
<string>git-upload-pack</string>
|
17
|
+
<string>uploadpack</string>
|
18
|
+
<string>git-upload-pack</string>
|
19
|
+
<string>url</string>
|
20
|
+
<string>foo_user@git.example.com:foo.git</string>
|
21
|
+
<string>tagopt</string>
|
22
|
+
<string/>
|
23
|
+
</org.spearce.jgit.transport.RemoteConfig>
|
24
|
+
</remoteRepositories>
|
25
|
+
<branches>
|
26
|
+
<hudson.plugins.git.BranchSpec>
|
27
|
+
<name>master</name>
|
28
|
+
</hudson.plugins.git.BranchSpec>
|
29
|
+
</branches>
|
30
|
+
<mergeOptions/>
|
31
|
+
<recursiveSubmodules>false</recursiveSubmodules>
|
32
|
+
<doGenerateSubmoduleConfigurations>false</doGenerateSubmoduleConfigurations>
|
33
|
+
<authorOrCommitter>false</authorOrCommitter>
|
34
|
+
<clean>false</clean>
|
35
|
+
<pruneBranches>false</pruneBranches>
|
36
|
+
<buildChooser class='hudson.plugins.git.util.DefaultBuildChooser'/>
|
37
|
+
<gitTool>Default</gitTool>
|
38
|
+
<submoduleCfg class='list'/>
|
39
|
+
<relativeTargetDir/>
|
40
|
+
<excludedRegions/>
|
41
|
+
<excludedUsers/>
|
42
|
+
<skipTag>false</skipTag>
|
43
|
+
</scm>
|
44
|
+
<assignedNode>slave01</assignedNode>
|
45
|
+
<canRoam>false</canRoam>
|
46
|
+
<blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
|
47
|
+
<blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
|
48
|
+
<triggers class='vector'>
|
49
|
+
<hudson.triggers.SCMTrigger>
|
50
|
+
<spec>*/5 * * * *</spec>
|
51
|
+
</hudson.triggers.SCMTrigger>
|
52
|
+
</triggers>
|
53
|
+
<concurrentBuild>false</concurrentBuild>
|
54
|
+
<builders>
|
55
|
+
<hudson.plugins.rake.Rake>
|
56
|
+
<rakeInstallation>(Default)</rakeInstallation>
|
57
|
+
<rakeFile/>
|
58
|
+
<rakeLibDir/>
|
59
|
+
<rakeWorkingDir>foo</rakeWorkingDir>
|
60
|
+
<tasks>db:create db:migrate ci:setup:testunit test:test:rcov</tasks>
|
61
|
+
<silent>false</silent>
|
62
|
+
</hudson.plugins.rake.Rake>
|
63
|
+
</builders>
|
64
|
+
<publishers>
|
65
|
+
<hudson.tasks.junit.JUnitResultArchiver>
|
66
|
+
<testResults>**/foo/test/reports/*.xml</testResults>
|
67
|
+
<keepLongStdio>false</keepLongStdio>
|
68
|
+
<testDataPublishers/>
|
69
|
+
</hudson.tasks.junit.JUnitResultArchiver>
|
70
|
+
<hudson.plugins.rubyMetrics.railsStats.RailsStatsPublisher>
|
71
|
+
<rake>
|
72
|
+
<rakeInstallation>(Default)</rakeInstallation>
|
73
|
+
<rakeWorkingDir/>
|
74
|
+
<tasks>stats</tasks>
|
75
|
+
<silent>true</silent>
|
76
|
+
</rake>
|
77
|
+
<rakeInstallation>(Default)</rakeInstallation>
|
78
|
+
<rakeWorkingDir/>
|
79
|
+
<task>stats</task>
|
80
|
+
</hudson.plugins.rubyMetrics.railsStats.RailsStatsPublisher>
|
81
|
+
<hudson.plugins.rubyMetrics.railsNotes.RailsNotesPublisher>
|
82
|
+
<rake>
|
83
|
+
<rakeInstallation>(Default)</rakeInstallation>
|
84
|
+
<rakeWorkingDir/>
|
85
|
+
<tasks>notes</tasks>
|
86
|
+
<silent>true</silent>
|
87
|
+
</rake>
|
88
|
+
<rakeInstallation>(Default)</rakeInstallation>
|
89
|
+
<rakeWorkingDir/>
|
90
|
+
<task>notes</task>
|
91
|
+
</hudson.plugins.rubyMetrics.railsNotes.RailsNotesPublisher>
|
92
|
+
<hudson.plugins.rubyMetrics.rcov.RcovPublisher>
|
93
|
+
<reportDir>foo/coverage/test/</reportDir>
|
94
|
+
<targets>
|
95
|
+
<hudson.plugins.rubyMetrics.rcov.model.MetricTarget>
|
96
|
+
<metric>TOTAL_COVERAGE</metric>
|
97
|
+
<healthy>80</healthy>
|
98
|
+
<unhealthy>0</unhealthy>
|
99
|
+
<unstable>0</unstable>
|
100
|
+
</hudson.plugins.rubyMetrics.rcov.model.MetricTarget>
|
101
|
+
<hudson.plugins.rubyMetrics.rcov.model.MetricTarget>
|
102
|
+
<metric>CODE_COVERAGE</metric>
|
103
|
+
<healthy>80</healthy>
|
104
|
+
<unhealthy>0</unhealthy>
|
105
|
+
<unstable>0</unstable>
|
106
|
+
</hudson.plugins.rubyMetrics.rcov.model.MetricTarget>
|
107
|
+
</targets>
|
108
|
+
</hudson.plugins.rubyMetrics.rcov.RcovPublisher>
|
109
|
+
</publishers>
|
110
|
+
<buildWrappers/>
|
111
|
+
<disabled>false</disabled></project>
|
@@ -0,0 +1,111 @@
|
|
1
|
+
<?xml version='1.0' encoding='UTF-8'?>
|
2
|
+
<project>
|
3
|
+
<actions/>
|
4
|
+
<description>foo [production] tests</description>
|
5
|
+
<keepDependencies>false</keepDependencies>
|
6
|
+
<properties/>
|
7
|
+
<scm class='hudson.plugins.git.GitSCM'>
|
8
|
+
<configVersion>1</configVersion>
|
9
|
+
<remoteRepositories>
|
10
|
+
<org.spearce.jgit.transport.RemoteConfig>
|
11
|
+
<string>origin</string>
|
12
|
+
<int>5</int>
|
13
|
+
<string>fetch</string>
|
14
|
+
<string>+refs/heads/*:refs/remotes/origin/*</string>
|
15
|
+
<string>receivepack</string>
|
16
|
+
<string>git-upload-pack</string>
|
17
|
+
<string>uploadpack</string>
|
18
|
+
<string>git-upload-pack</string>
|
19
|
+
<string>url</string>
|
20
|
+
<string>foo_user@git.example.com:foo.git</string>
|
21
|
+
<string>tagopt</string>
|
22
|
+
<string/>
|
23
|
+
</org.spearce.jgit.transport.RemoteConfig>
|
24
|
+
</remoteRepositories>
|
25
|
+
<branches>
|
26
|
+
<hudson.plugins.git.BranchSpec>
|
27
|
+
<name>production</name>
|
28
|
+
</hudson.plugins.git.BranchSpec>
|
29
|
+
</branches>
|
30
|
+
<mergeOptions/>
|
31
|
+
<recursiveSubmodules>false</recursiveSubmodules>
|
32
|
+
<doGenerateSubmoduleConfigurations>false</doGenerateSubmoduleConfigurations>
|
33
|
+
<authorOrCommitter>false</authorOrCommitter>
|
34
|
+
<clean>false</clean>
|
35
|
+
<pruneBranches>false</pruneBranches>
|
36
|
+
<buildChooser class='hudson.plugins.git.util.DefaultBuildChooser'/>
|
37
|
+
<gitTool>Default</gitTool>
|
38
|
+
<submoduleCfg class='list'/>
|
39
|
+
<relativeTargetDir/>
|
40
|
+
<excludedRegions/>
|
41
|
+
<excludedUsers/>
|
42
|
+
<skipTag>false</skipTag>
|
43
|
+
</scm>
|
44
|
+
<assignedNode>slave01</assignedNode>
|
45
|
+
<canRoam>false</canRoam>
|
46
|
+
<blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
|
47
|
+
<blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
|
48
|
+
<triggers class='vector'>
|
49
|
+
<hudson.triggers.SCMTrigger>
|
50
|
+
<spec>*/5 * * * *</spec>
|
51
|
+
</hudson.triggers.SCMTrigger>
|
52
|
+
</triggers>
|
53
|
+
<concurrentBuild>false</concurrentBuild>
|
54
|
+
<builders>
|
55
|
+
<hudson.plugins.rake.Rake>
|
56
|
+
<rakeInstallation>(Default)</rakeInstallation>
|
57
|
+
<rakeFile/>
|
58
|
+
<rakeLibDir/>
|
59
|
+
<rakeWorkingDir>foo</rakeWorkingDir>
|
60
|
+
<tasks>db:create db:migrate ci:setup:testunit test:test:rcov</tasks>
|
61
|
+
<silent>false</silent>
|
62
|
+
</hudson.plugins.rake.Rake>
|
63
|
+
</builders>
|
64
|
+
<publishers>
|
65
|
+
<hudson.tasks.junit.JUnitResultArchiver>
|
66
|
+
<testResults>**/foo/test/reports/*.xml</testResults>
|
67
|
+
<keepLongStdio>false</keepLongStdio>
|
68
|
+
<testDataPublishers/>
|
69
|
+
</hudson.tasks.junit.JUnitResultArchiver>
|
70
|
+
<hudson.plugins.rubyMetrics.railsStats.RailsStatsPublisher>
|
71
|
+
<rake>
|
72
|
+
<rakeInstallation>(Default)</rakeInstallation>
|
73
|
+
<rakeWorkingDir/>
|
74
|
+
<tasks>stats</tasks>
|
75
|
+
<silent>true</silent>
|
76
|
+
</rake>
|
77
|
+
<rakeInstallation>(Default)</rakeInstallation>
|
78
|
+
<rakeWorkingDir/>
|
79
|
+
<task>stats</task>
|
80
|
+
</hudson.plugins.rubyMetrics.railsStats.RailsStatsPublisher>
|
81
|
+
<hudson.plugins.rubyMetrics.railsNotes.RailsNotesPublisher>
|
82
|
+
<rake>
|
83
|
+
<rakeInstallation>(Default)</rakeInstallation>
|
84
|
+
<rakeWorkingDir/>
|
85
|
+
<tasks>notes</tasks>
|
86
|
+
<silent>true</silent>
|
87
|
+
</rake>
|
88
|
+
<rakeInstallation>(Default)</rakeInstallation>
|
89
|
+
<rakeWorkingDir/>
|
90
|
+
<task>notes</task>
|
91
|
+
</hudson.plugins.rubyMetrics.railsNotes.RailsNotesPublisher>
|
92
|
+
<hudson.plugins.rubyMetrics.rcov.RcovPublisher>
|
93
|
+
<reportDir>foo/coverage/test/</reportDir>
|
94
|
+
<targets>
|
95
|
+
<hudson.plugins.rubyMetrics.rcov.model.MetricTarget>
|
96
|
+
<metric>TOTAL_COVERAGE</metric>
|
97
|
+
<healthy>80</healthy>
|
98
|
+
<unhealthy>0</unhealthy>
|
99
|
+
<unstable>0</unstable>
|
100
|
+
</hudson.plugins.rubyMetrics.rcov.model.MetricTarget>
|
101
|
+
<hudson.plugins.rubyMetrics.rcov.model.MetricTarget>
|
102
|
+
<metric>CODE_COVERAGE</metric>
|
103
|
+
<healthy>80</healthy>
|
104
|
+
<unhealthy>0</unhealthy>
|
105
|
+
<unstable>0</unstable>
|
106
|
+
</hudson.plugins.rubyMetrics.rcov.model.MetricTarget>
|
107
|
+
</targets>
|
108
|
+
</hudson.plugins.rubyMetrics.rcov.RcovPublisher>
|
109
|
+
</publishers>
|
110
|
+
<buildWrappers/>
|
111
|
+
<disabled>false</disabled></project>
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
|
3
|
+
class ProjectTest < Test::Unit::TestCase
|
4
|
+
context 'valid project' do
|
5
|
+
setup do
|
6
|
+
@dir = File.expand_path('../fixtures/foo_master', __FILE__)
|
7
|
+
end
|
8
|
+
|
9
|
+
should 'not raise exceptions' do
|
10
|
+
assert_nothing_raised do
|
11
|
+
JenkinsJob::Project.new @dir
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
should 'understand project is enabled' do
|
16
|
+
project = JenkinsJob::Project.new @dir
|
17
|
+
assert project.enabled?
|
18
|
+
assert !project.disabled?
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'invalid project' do
|
23
|
+
setup do
|
24
|
+
FakeFS.activate!
|
25
|
+
FakeFS::FileSystem.clear
|
26
|
+
end
|
27
|
+
|
28
|
+
teardown do
|
29
|
+
FakeFS.deactivate!
|
30
|
+
end
|
31
|
+
|
32
|
+
should 'find missing directory' do
|
33
|
+
assert_raises RuntimeError do
|
34
|
+
JenkinsJob::Project.new 'foo_master'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
should 'find missing config' do
|
39
|
+
FileUtils.mkdir_p 'foo_master'
|
40
|
+
assert_raises RuntimeError do
|
41
|
+
JenkinsJob::Project.new 'foo_master'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'alter project' do
|
47
|
+
setup do
|
48
|
+
File.open(File.expand_path('../fixtures/foo_master/config.xml',
|
49
|
+
__FILE__), 'r') do |file|
|
50
|
+
@source_xml = file.readlines
|
51
|
+
end
|
52
|
+
|
53
|
+
FakeFS.activate!
|
54
|
+
FakeFS::FileSystem.clear
|
55
|
+
|
56
|
+
FileUtils.mkdir_p 'foo_master'
|
57
|
+
File.open('foo_master/config.xml', 'w') do |file|
|
58
|
+
file.write @source_xml
|
59
|
+
end
|
60
|
+
@project = JenkinsJob::Project.new 'foo_master'
|
61
|
+
assert @project.enabled?
|
62
|
+
end
|
63
|
+
|
64
|
+
teardown do
|
65
|
+
FakeFS.deactivate!
|
66
|
+
end
|
67
|
+
|
68
|
+
should 'enable and disable project' do
|
69
|
+
@project.enable
|
70
|
+
assert @project.enabled?
|
71
|
+
|
72
|
+
@project.disable
|
73
|
+
assert @project.disabled?
|
74
|
+
|
75
|
+
@project.enable
|
76
|
+
assert @project.enabled?
|
77
|
+
end
|
78
|
+
|
79
|
+
context 'settings' do
|
80
|
+
should 'return a project setting' do
|
81
|
+
actual = @project.setting('disabled')
|
82
|
+
assert_equal 'false', actual.text
|
83
|
+
end
|
84
|
+
|
85
|
+
should "return a nil if setting doesn't exist" do
|
86
|
+
actual = @project.setting('foobar')
|
87
|
+
assert_nil actual
|
88
|
+
end
|
89
|
+
|
90
|
+
should 'update an existing setting by overwriting it' do
|
91
|
+
node = REXML::Element.new 'disabled'
|
92
|
+
node.text = 'true'
|
93
|
+
|
94
|
+
assert @project.enabled?
|
95
|
+
@project.update_setting 'disabled', node
|
96
|
+
assert @project.disabled?
|
97
|
+
end
|
98
|
+
|
99
|
+
should 'update an existing setting by overwriting it' do
|
100
|
+
parent = REXML::Element.new 'foobar_parent'
|
101
|
+
node = REXML::Element.new 'foobar'
|
102
|
+
node.text = 'hello world'
|
103
|
+
parent << node
|
104
|
+
|
105
|
+
|
106
|
+
@project.update_setting 'foobar_parent', parent
|
107
|
+
actual_node = @project.setting 'foobar_parent/foobar'
|
108
|
+
assert_equal 1, @project.setting('foobar_parent').size
|
109
|
+
assert_equal node.text, actual_node.text
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.expand_path('../../lib/jenkins_job',__FILE__)
|
2
|
+
require 'test/unit'
|
3
|
+
require 'mocha'
|
4
|
+
require 'shoulda'
|
5
|
+
require 'stringio'
|
6
|
+
require 'fakefs/safe'
|
7
|
+
|
8
|
+
class Object
|
9
|
+
def capture(stream)
|
10
|
+
begin
|
11
|
+
stream = stream.to_s
|
12
|
+
eval "$#{stream} = StringIO.new"
|
13
|
+
yield
|
14
|
+
result = eval("$#{stream}").string
|
15
|
+
ensure
|
16
|
+
eval("$#{stream} = #{stream.upcase}")
|
17
|
+
end
|
18
|
+
result
|
19
|
+
end
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jenkins_job
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Flavio Castelli
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-02-25 00:00:00 +01:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: thor
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 39
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
- 14
|
33
|
+
- 0
|
34
|
+
version: 0.14.0
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: bundler
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 23
|
46
|
+
segments:
|
47
|
+
- 1
|
48
|
+
- 0
|
49
|
+
- 0
|
50
|
+
version: 1.0.0
|
51
|
+
type: :development
|
52
|
+
version_requirements: *id002
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: fakefs
|
55
|
+
prerelease: false
|
56
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 17
|
62
|
+
segments:
|
63
|
+
- 0
|
64
|
+
- 3
|
65
|
+
- 1
|
66
|
+
version: 0.3.1
|
67
|
+
type: :development
|
68
|
+
version_requirements: *id003
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: mocha
|
71
|
+
prerelease: false
|
72
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
hash: 3
|
78
|
+
segments:
|
79
|
+
- 0
|
80
|
+
version: "0"
|
81
|
+
type: :development
|
82
|
+
version_requirements: *id004
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: shoulda
|
85
|
+
prerelease: false
|
86
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
hash: 3
|
92
|
+
segments:
|
93
|
+
- 0
|
94
|
+
version: "0"
|
95
|
+
type: :development
|
96
|
+
version_requirements: *id005
|
97
|
+
description: Handle your Jenkins job configuration files in an easier way.
|
98
|
+
email:
|
99
|
+
- flavio@castelli.name
|
100
|
+
executables:
|
101
|
+
- jenkins_job
|
102
|
+
extensions: []
|
103
|
+
|
104
|
+
extra_rdoc_files: []
|
105
|
+
|
106
|
+
files:
|
107
|
+
- .gitignore
|
108
|
+
- Gemfile
|
109
|
+
- Gemfile.lock
|
110
|
+
- README.rdoc
|
111
|
+
- Rakefile
|
112
|
+
- bin/jenkins_job
|
113
|
+
- jenkins_job.gemspec
|
114
|
+
- lib/jenkins_job.rb
|
115
|
+
- lib/jenkins_job/cli.rb
|
116
|
+
- lib/jenkins_job/project.rb
|
117
|
+
- lib/jenkins_job/version.rb
|
118
|
+
- test/cli_test.rb
|
119
|
+
- test/fixtures/foo_master/config.xml
|
120
|
+
- test/fixtures/foo_production/config.xml
|
121
|
+
- test/project_test.rb
|
122
|
+
- test/test_helper.rb
|
123
|
+
has_rdoc: true
|
124
|
+
homepage:
|
125
|
+
licenses: []
|
126
|
+
|
127
|
+
post_install_message:
|
128
|
+
rdoc_options: []
|
129
|
+
|
130
|
+
require_paths:
|
131
|
+
- lib
|
132
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
133
|
+
none: false
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
hash: 3
|
138
|
+
segments:
|
139
|
+
- 0
|
140
|
+
version: "0"
|
141
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
142
|
+
none: false
|
143
|
+
requirements:
|
144
|
+
- - ">="
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
hash: 23
|
147
|
+
segments:
|
148
|
+
- 1
|
149
|
+
- 3
|
150
|
+
- 6
|
151
|
+
version: 1.3.6
|
152
|
+
requirements: []
|
153
|
+
|
154
|
+
rubyforge_project: jenkins_job
|
155
|
+
rubygems_version: 1.5.2
|
156
|
+
signing_key:
|
157
|
+
specification_version: 3
|
158
|
+
summary: Simple cli tool created to manage Jenkins job files.
|
159
|
+
test_files: []
|
160
|
+
|