jenkins-capistrano 0.0.7 → 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 +73 -16
- data/Gemfile.lock +80 -0
- data/README.md +169 -56
- data/Vagrantfile +23 -0
- data/example/Gemfile +3 -1
- data/example/README.md +13 -17
- data/example/config/deploy.rb +8 -12
- data/example/config/deploy/develop.rb +5 -3
- data/example/config/deploy/production.rb +13 -3
- data/example/config/deploy/staging.rb +13 -4
- data/example/config/jenkins/nodes/develop/dev-slave01.xml.erb +33 -0
- data/example/config/jenkins/nodes/production/prod-slave01.xml.erb +33 -0
- data/example/config/jenkins/nodes/production/prod-slave02.xml.erb +33 -0
- data/example/config/jenkins/nodes/production/prod-slave03.xml.erb +33 -0
- data/example/config/jenkins/nodes/staging/stg-slave01.xml.erb +33 -0
- data/example/config/jenkins/nodes/staging/stg-slave02.xml.erb +33 -0
- data/example/config/jenkins/nodes/staging/stg-slave03.xml.erb +33 -0
- data/example/script/bootstrap +1 -2
- data/features/config_jobs.feature +162 -0
- data/features/config_nodes.feature +121 -0
- data/features/config_views.feature +136 -0
- data/features/help.feature +83 -0
- data/features/step_definitions/jenkins_steps.rb +19 -0
- data/features/support/env.rb +5 -0
- data/features/support/jenkins_helper.rb +113 -0
- data/jenkins-capistrano.gemspec +4 -3
- data/lib/jenkins-capistrano.rb +2 -215
- data/lib/jenkins-capistrano/configurator.rb +87 -0
- data/lib/jenkins-capistrano/tasks.rb +112 -0
- data/lib/jenkins-capistrano/template.rb +20 -0
- data/lib/jenkins-capistrano/version.rb +1 -1
- metadata +87 -41
- checksums.yaml +0 -7
- data/example/config/jenkins/jobs/.gitkeep +0 -0
- data/example/config/jenkins/nodes/develop/dev-slave01.json +0 -17
- data/example/config/jenkins/nodes/production/prod-slave01.json +0 -17
- data/example/config/jenkins/nodes/production/prod-slave02.json +0 -17
- data/example/config/jenkins/nodes/production/prod-slave03.json +0 -17
- data/example/config/jenkins/nodes/staging/stg-slave01.json +0 -17
- data/example/config/jenkins/nodes/staging/stg-slave02.json +0 -17
- data/example/config/jenkins/nodes/staging/stg-slave03.json +0 -17
- data/lib/jenkins-capistrano/client.rb +0 -66
- data/lib/jenkins-capistrano/client/job.rb +0 -47
- data/lib/jenkins-capistrano/client/node.rb +0 -103
- data/lib/jenkins-capistrano/client/plugin_manager.rb +0 -51
- data/lib/jenkins-capistrano/client/update_center.rb +0 -43
- data/lib/jenkins-capistrano/client/view.rb +0 -40
data/jenkins-capistrano.gemspec
CHANGED
@@ -15,10 +15,11 @@ Gem::Specification.new do |gem|
|
|
15
15
|
gem.require_paths = ["lib"]
|
16
16
|
gem.version = Jenkins::Capistrano::VERSION
|
17
17
|
|
18
|
-
gem.add_dependency 'capistrano'
|
19
|
-
gem.add_dependency '
|
20
|
-
gem.add_dependency 'hpricot'
|
18
|
+
gem.add_dependency 'capistrano', '< 3.0.0'
|
19
|
+
gem.add_dependency 'jenkins_api_client', '>= 1.0.0'
|
21
20
|
|
22
21
|
gem.add_development_dependency 'rake'
|
23
22
|
gem.add_development_dependency 'pry'
|
23
|
+
gem.add_development_dependency 'tapp'
|
24
|
+
gem.add_development_dependency 'aruba'
|
24
25
|
end
|
data/lib/jenkins-capistrano.rb
CHANGED
@@ -1,216 +1,3 @@
|
|
1
1
|
require 'jenkins-capistrano/version'
|
2
|
-
require 'jenkins-capistrano/
|
3
|
-
|
4
|
-
def _cset(name, *args, &block)
|
5
|
-
unless exists?(name)
|
6
|
-
set(name, *args, &block)
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
# Capistrano task for Jenkins.
|
11
|
-
#
|
12
|
-
# Just add "require 'jenkins-capistrano'" in your Capistrano deploy.rb
|
13
|
-
Capistrano::Configuration.instance(:must_exist).load do
|
14
|
-
|
15
|
-
_cset(:jenkins_host) { abort "Please specify the host of your jenkins server, set :jenkins_host, 'http://localhost:8080'" }
|
16
|
-
|
17
|
-
_cset(:jenkins_username) { '' }
|
18
|
-
_cset(:jenkins_password) { '' }
|
19
|
-
|
20
|
-
_cset(:jenkins_job_config_dir) { 'config/jenkins/jobs' }
|
21
|
-
_cset(:jenkins_node_config_dir) { 'config/jenkins/nodes' }
|
22
|
-
_cset(:jenkins_view_config_dir) { 'config/jenkins/views' }
|
23
|
-
|
24
|
-
_cset(:jenkins_plugins) { [] }
|
25
|
-
_cset(:jenkins_install_timeout) { 60 * 5 }
|
26
|
-
_cset(:jenkins_plugin_enable_update) { false }
|
27
|
-
_cset(:jenkins_plugin_enable_restart) { false }
|
28
|
-
|
29
|
-
_cset(:disabled_jobs) { [] }
|
30
|
-
|
31
|
-
def client
|
32
|
-
@client ||= Jenkins::Client.new(jenkins_host, { :username => jenkins_username, :password => jenkins_password})
|
33
|
-
end
|
34
|
-
|
35
|
-
def job_configs
|
36
|
-
abort "Please create the jenkins_job_config_dir first: #{jenkins_job_config_dir}" unless Dir.exists? jenkins_job_config_dir
|
37
|
-
Dir.glob("#{jenkins_job_config_dir}/*.xml")
|
38
|
-
end
|
39
|
-
|
40
|
-
def node_configs
|
41
|
-
abort "Please create the jenkins_node_config_dir first: #{jenkins_node_config_dir}" unless Dir.exists? jenkins_node_config_dir
|
42
|
-
Dir.glob("#{jenkins_node_config_dir}/*.json")
|
43
|
-
end
|
44
|
-
|
45
|
-
def view_configs
|
46
|
-
abort "Please create the jenkins_view_config_dir first: #{jenkins_node_config_dir}" unless Dir.exists? jenkins_node_config_dir
|
47
|
-
Dir.glob("#{jenkins_view_config_dir}/*.xml")
|
48
|
-
end
|
49
|
-
|
50
|
-
def missing_plugins
|
51
|
-
installed = client.plugin_names
|
52
|
-
jenkins_plugins.select do |plugin|
|
53
|
-
missing = !installed.include?(plugin.split('@'))
|
54
|
-
logger.info "#{plugin} is already installed." unless missing
|
55
|
-
missing
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
# minimum configurations
|
60
|
-
#
|
61
|
-
# role :jenkins, 'localhost:8080'
|
62
|
-
namespace :jenkins do
|
63
|
-
|
64
|
-
desc <<-DESC
|
65
|
-
Deploy the jobs to Jenkins server -- meaning create or update --
|
66
|
-
|
67
|
-
Configuration
|
68
|
-
-------------
|
69
|
-
jenkins_job_config_dir
|
70
|
-
the directory path where the config.xml stored.
|
71
|
-
default: 'config/jenkins/jobs'
|
72
|
-
|
73
|
-
disabled_jobs
|
74
|
-
job names array which should be disabled after deployment.
|
75
|
-
default: []
|
76
|
-
|
77
|
-
DESC
|
78
|
-
task :deploy_jobs do
|
79
|
-
logger.info "deploying jenkins jobs to #{jenkins_host}"
|
80
|
-
logger.important "no job configs found." if job_configs.empty?
|
81
|
-
job_configs.each do |file|
|
82
|
-
name = File.basename(file, '.xml')
|
83
|
-
msg = StringIO.new
|
84
|
-
|
85
|
-
client.create_or_update_job(name, File.read(file))
|
86
|
-
msg << "job #{name} created"
|
87
|
-
|
88
|
-
if disabled_jobs.include? name
|
89
|
-
client.disable_job(name)
|
90
|
-
msg << ", but was set to disabled"
|
91
|
-
end
|
92
|
-
msg << "."
|
93
|
-
logger.trace msg.string
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
desc <<-DESC
|
98
|
-
Configure the nodes to Jenkins server -- meaning create or update --
|
99
|
-
|
100
|
-
Configuration
|
101
|
-
-------------
|
102
|
-
jenkins_node_config_dir
|
103
|
-
the directory path where the node's configuration stored.
|
104
|
-
default: 'config/jenkins/nodes'
|
105
|
-
DESC
|
106
|
-
task :config_nodes do
|
107
|
-
logger.info "configuring jenkins nodes to #{jenkins_host}"
|
108
|
-
logger.important "no node configs found." if node_configs.empty?
|
109
|
-
node_configs.each do |file|
|
110
|
-
name = File.basename(file, '.json')
|
111
|
-
opts = JSON.parse(File.read(file)).to_hash.
|
112
|
-
inject({}) { |mem, (key, val)| mem[key.to_sym] = val; mem }
|
113
|
-
client.config_node(name, opts)
|
114
|
-
logger.trace "node #{name} created."
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
desc <<-DESC
|
119
|
-
Configure the views to Jenkins server -- meaning create or update --
|
120
|
-
|
121
|
-
Configuration
|
122
|
-
-------------
|
123
|
-
jenkins_view_config_dir
|
124
|
-
the directory path where the view's configuration stored.
|
125
|
-
default: 'config/jenkins/views'
|
126
|
-
DESC
|
127
|
-
task :config_views do
|
128
|
-
logger.info "configuring jenkins views to #{jenkins_host}"
|
129
|
-
logger.important "no view configs found." if view_configs.empty?
|
130
|
-
view_configs.each do |file|
|
131
|
-
name = File.basename(file, '.xml')
|
132
|
-
msg = StringIO.new
|
133
|
-
client.create_or_update_view(name, File.read(file))
|
134
|
-
logger.trace "view #{name} created."
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
desc <<-DESC
|
139
|
-
Install plugins to Jenkins server
|
140
|
-
|
141
|
-
Configuration
|
142
|
-
-------------
|
143
|
-
jenkins_plugins
|
144
|
-
the hash array contains plugin's name and version.
|
145
|
-
|
146
|
-
jenkins_install_timeout
|
147
|
-
a timeout seconds to wait for plugin installation.
|
148
|
-
default: 5 min
|
149
|
-
|
150
|
-
jenkins_plugin_enable_update
|
151
|
-
whether to update or ignore when the plugin already installed.
|
152
|
-
default: false
|
153
|
-
|
154
|
-
jenkins_plugin_enable_restart
|
155
|
-
whether to restart or ignore when the plugin installation requires restarting.
|
156
|
-
default: false
|
157
|
-
DESC
|
158
|
-
task :install_plugins do
|
159
|
-
logger.important "installing plugins to #{jenkins_host}"
|
160
|
-
if jenkins_plugins.empty?
|
161
|
-
logger.info "no plugin config found."
|
162
|
-
next
|
163
|
-
end
|
164
|
-
|
165
|
-
logger.info "calcurating plugins to install..."
|
166
|
-
candidates = client.prevalidate_plugin_config(missing_plugins)
|
167
|
-
plugins_to_install = candidates.reduce([]) do |mem, candidate|
|
168
|
-
plugin = "#{candidate['name']}@#{candidate['version']}"
|
169
|
-
mode = candidate['mode']
|
170
|
-
case
|
171
|
-
when mode == 'missing'
|
172
|
-
logger.debug "#{plugin} marked to be installed."
|
173
|
-
mem << candidate
|
174
|
-
when mode == 'old'
|
175
|
-
if jenkins_plugin_enable_update
|
176
|
-
logger.debug "#{plugin} marked to be updated."
|
177
|
-
mem << candidate
|
178
|
-
end
|
179
|
-
end
|
180
|
-
mem
|
181
|
-
end
|
182
|
-
if plugins_to_install.empty?
|
183
|
-
logger.info "all plugins already installed."
|
184
|
-
next
|
185
|
-
end
|
186
|
-
|
187
|
-
logger.info "installing the plugins, this could be take a while..."
|
188
|
-
client.install_plugin(plugins_to_install)
|
189
|
-
|
190
|
-
names = plugins_to_install.map {|v| v['name'] }
|
191
|
-
client.wait_for_complete(jenkins_install_timeout) do |job|
|
192
|
-
result = job['status'] == true
|
193
|
-
# skip unknown jobs
|
194
|
-
if result and names.include?(job['name'])
|
195
|
-
names.delete job['name']
|
196
|
-
logger.debug "#{job['name']}@#{job['version']} installed."
|
197
|
-
end
|
198
|
-
result
|
199
|
-
end
|
200
|
-
logger.info "all plugins successfully installed."
|
201
|
-
|
202
|
-
if client.restart_required?
|
203
|
-
if jenkins_plugin_enable_restart
|
204
|
-
logger.important "restarting jenkins."
|
205
|
-
client.safe_restart! do
|
206
|
-
logger.debug "waiting for Jenkins to restart..."
|
207
|
-
end
|
208
|
-
logger.info "Jenkins is successfully restarted."
|
209
|
-
else
|
210
|
-
logger.important "restarting is disabled, please restart the jenkins manually for complete the installation."
|
211
|
-
end
|
212
|
-
end
|
213
|
-
end
|
214
|
-
end
|
215
|
-
|
216
|
-
end
|
2
|
+
require 'jenkins-capistrano/configurator'
|
3
|
+
require 'jenkins-capistrano/tasks'
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'jenkins-capistrano/template'
|
2
|
+
require 'jenkins_api_client'
|
3
|
+
|
4
|
+
module Jenkins
|
5
|
+
module Capistrano
|
6
|
+
class Configurator
|
7
|
+
|
8
|
+
attr_reader :logger, :client, :job_config_dir, :node_config_dir, :view_config_dir, :template_vars
|
9
|
+
|
10
|
+
def initialize(options = {})
|
11
|
+
opts = {:log_location => '/dev/null'}.merge(options)
|
12
|
+
@logger = opts.delete(:logger)
|
13
|
+
@template_vars = opts.delete(:template_vars)
|
14
|
+
@client = JenkinsApi::Client.new(opts)
|
15
|
+
end
|
16
|
+
|
17
|
+
def configure_jobs(config_files, disabled_jobs)
|
18
|
+
if config_files.empty?
|
19
|
+
logger.important "no node configs found."
|
20
|
+
return
|
21
|
+
end
|
22
|
+
|
23
|
+
config_files.each do |file|
|
24
|
+
name = name_for(file)
|
25
|
+
client.job.create_or_update(name, config_xml_for(file))
|
26
|
+
logger.trace "job #{name} created."
|
27
|
+
if disabled_jobs.include? name
|
28
|
+
client.job.disable(name)
|
29
|
+
logger.trace " -> disabled"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def configure_nodes(config_files)
|
35
|
+
if config_files.empty?
|
36
|
+
logger.important "no node configs found."
|
37
|
+
return
|
38
|
+
end
|
39
|
+
|
40
|
+
config_files.each do |file|
|
41
|
+
name = name_for(file)
|
42
|
+
unless client.node.list.include? name
|
43
|
+
params = { :name => name, :slave_host => 'dummy-by-jenkins-capistrano', :private_key_file => 'dummy' }
|
44
|
+
client.node.create_dumb_slave(params)
|
45
|
+
end
|
46
|
+
client.node.post_config(name, config_xml_for(file))
|
47
|
+
logger.trace "node #{name} created."
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def configure_views(config_files)
|
52
|
+
if config_files.empty?
|
53
|
+
logger.important "no view configs found."
|
54
|
+
return
|
55
|
+
end
|
56
|
+
|
57
|
+
config_files.each do |file|
|
58
|
+
name = name_for(file)
|
59
|
+
unless client.view.exists? name
|
60
|
+
client.view.create name
|
61
|
+
end
|
62
|
+
client.view.post_config(name, config_xml_for(file))
|
63
|
+
logger.trace "view #{name} created."
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
def name_for(file_path)
|
69
|
+
file_path.basename.to_s.split('.').first
|
70
|
+
end
|
71
|
+
|
72
|
+
def config_xml_for(file)
|
73
|
+
config_xml = if file.extname == '.erb'
|
74
|
+
Jenkins::Template.new(file, template_vars).evaluate
|
75
|
+
else
|
76
|
+
file.read
|
77
|
+
end
|
78
|
+
Nokogiri::XML(config_xml) do |config|
|
79
|
+
config.options = Nokogiri::XML::ParseOptions::STRICT
|
80
|
+
end.to_xml
|
81
|
+
rescue => e
|
82
|
+
abort "`#{file}` is not well-formed, put `.erb` as extname if it's erb template: #{e.message}"
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
Capistrano::Configuration.instance(:must_exist).load do
|
2
|
+
|
3
|
+
def _cset(name, *args, &block)
|
4
|
+
unless exists?(name)
|
5
|
+
set(name, *args, &block)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
_cset(:jenkins_host) { abort "Please specify the host of your jenkins server, set :jenkins_host, 'http://localhost:8080'" }
|
10
|
+
|
11
|
+
_cset(:jenkins_username) { '' }
|
12
|
+
_cset(:jenkins_password) { '' }
|
13
|
+
|
14
|
+
_cset(:jenkins_job_config_dir) { 'config/jenkins/jobs' }
|
15
|
+
_cset(:jenkins_node_config_dir) { 'config/jenkins/nodes' }
|
16
|
+
_cset(:jenkins_view_config_dir) { 'config/jenkins/views' }
|
17
|
+
|
18
|
+
_cset(:disabled_jobs) { [] }
|
19
|
+
|
20
|
+
_cset(:jenkins_template_vars) { {} }
|
21
|
+
|
22
|
+
def configurator
|
23
|
+
@configurator ||= Jenkins::Capistrano::Configurator.new(
|
24
|
+
:logger => logger,
|
25
|
+
:server_url => jenkins_host,
|
26
|
+
:username => jenkins_username,
|
27
|
+
:password => jenkins_password,
|
28
|
+
:template_vars => jenkins_template_vars
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
%w(job node view).each do |name|
|
33
|
+
instance_eval <<-RUBY, __FILE__, __LINE__ + 1
|
34
|
+
def #{name}_configs
|
35
|
+
config_dir = fetch('jenkins_#{name}_config_dir'.to_sym)
|
36
|
+
abort "Please create the jenkins_#{name}_config_dir first: \#{config_dir}" unless Dir.exists? config_dir
|
37
|
+
Pathname.glob(File.join(config_dir, '/*.{xml,erb}'))
|
38
|
+
end
|
39
|
+
RUBY
|
40
|
+
end
|
41
|
+
|
42
|
+
# minimum configurations
|
43
|
+
#
|
44
|
+
# role :jenkins, 'localhost:8080'
|
45
|
+
namespace :jenkins do
|
46
|
+
|
47
|
+
desc <<-DESC
|
48
|
+
Configure the jobs to Jenkins server.
|
49
|
+
|
50
|
+
Configuration
|
51
|
+
-------------
|
52
|
+
jenkins_job_config_dir
|
53
|
+
the directory path where the config.xml stored.
|
54
|
+
default: 'config/jenkins/jobs'
|
55
|
+
|
56
|
+
disabled_jobs
|
57
|
+
job names array which should be disabled after deployment.
|
58
|
+
default: []
|
59
|
+
DESC
|
60
|
+
task :config_jobs do
|
61
|
+
logger.info "deploying jenkins jobs to #{jenkins_host}"
|
62
|
+
configurator.configure_jobs(job_configs, disabled_jobs)
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
desc <<-DESC
|
67
|
+
[DEPRECATED] Use jenkins:config_jobs instead.
|
68
|
+
DESC
|
69
|
+
task :deploy_jobs do
|
70
|
+
logger.important '[DEPRECATED] Use jenkins:config_jobs instead.'
|
71
|
+
config_jobs
|
72
|
+
end
|
73
|
+
|
74
|
+
desc <<-DESC
|
75
|
+
|
76
|
+
DESC
|
77
|
+
task :reverse_jobs do
|
78
|
+
logger.important '[DEPRECATED] Use jenkins:config_jobs instead.'
|
79
|
+
config_jobs
|
80
|
+
end
|
81
|
+
|
82
|
+
desc <<-DESC
|
83
|
+
Configure the nodes to Jenkins server.
|
84
|
+
|
85
|
+
Configuration
|
86
|
+
-------------
|
87
|
+
jenkins_node_config_dir
|
88
|
+
the directory path where the node's configuration stored.
|
89
|
+
default: 'config/jenkins/nodes'
|
90
|
+
DESC
|
91
|
+
task :config_nodes do
|
92
|
+
logger.info "configuring jenkins nodes to #{jenkins_host}"
|
93
|
+
configurator.configure_nodes(node_configs)
|
94
|
+
end
|
95
|
+
|
96
|
+
desc <<-DESC
|
97
|
+
Configure the views to Jenkins server.
|
98
|
+
|
99
|
+
Configuration
|
100
|
+
-------------
|
101
|
+
jenkins_view_config_dir
|
102
|
+
the directory path where the view's configuration stored.
|
103
|
+
default: 'config/jenkins/views'
|
104
|
+
DESC
|
105
|
+
task :config_views do
|
106
|
+
logger.info "configuring jenkins views to #{jenkins_host}"
|
107
|
+
configurator.configure_views(view_configs)
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
module Jenkins
|
3
|
+
class Template
|
4
|
+
|
5
|
+
def initialize(template, variables)
|
6
|
+
raise ArgumentError, "Template #{template} does not exist." unless File.exists? template
|
7
|
+
raise ArgumentError, "variables must be a Hash, but was #{variables.class}" unless variables.is_a? Hash
|
8
|
+
@template = template
|
9
|
+
@variables = variables
|
10
|
+
end
|
11
|
+
|
12
|
+
def evaluate
|
13
|
+
@variables.each do |param, value|
|
14
|
+
var = "@#{param.to_s}"
|
15
|
+
instance_variable_set(var, value)
|
16
|
+
end
|
17
|
+
ERB.new(File.read(@template), 0, '-').result(binding)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|