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.
Files changed (47) hide show
  1. data/.gitignore +73 -16
  2. data/Gemfile.lock +80 -0
  3. data/README.md +169 -56
  4. data/Vagrantfile +23 -0
  5. data/example/Gemfile +3 -1
  6. data/example/README.md +13 -17
  7. data/example/config/deploy.rb +8 -12
  8. data/example/config/deploy/develop.rb +5 -3
  9. data/example/config/deploy/production.rb +13 -3
  10. data/example/config/deploy/staging.rb +13 -4
  11. data/example/config/jenkins/nodes/develop/dev-slave01.xml.erb +33 -0
  12. data/example/config/jenkins/nodes/production/prod-slave01.xml.erb +33 -0
  13. data/example/config/jenkins/nodes/production/prod-slave02.xml.erb +33 -0
  14. data/example/config/jenkins/nodes/production/prod-slave03.xml.erb +33 -0
  15. data/example/config/jenkins/nodes/staging/stg-slave01.xml.erb +33 -0
  16. data/example/config/jenkins/nodes/staging/stg-slave02.xml.erb +33 -0
  17. data/example/config/jenkins/nodes/staging/stg-slave03.xml.erb +33 -0
  18. data/example/script/bootstrap +1 -2
  19. data/features/config_jobs.feature +162 -0
  20. data/features/config_nodes.feature +121 -0
  21. data/features/config_views.feature +136 -0
  22. data/features/help.feature +83 -0
  23. data/features/step_definitions/jenkins_steps.rb +19 -0
  24. data/features/support/env.rb +5 -0
  25. data/features/support/jenkins_helper.rb +113 -0
  26. data/jenkins-capistrano.gemspec +4 -3
  27. data/lib/jenkins-capistrano.rb +2 -215
  28. data/lib/jenkins-capistrano/configurator.rb +87 -0
  29. data/lib/jenkins-capistrano/tasks.rb +112 -0
  30. data/lib/jenkins-capistrano/template.rb +20 -0
  31. data/lib/jenkins-capistrano/version.rb +1 -1
  32. metadata +87 -41
  33. checksums.yaml +0 -7
  34. data/example/config/jenkins/jobs/.gitkeep +0 -0
  35. data/example/config/jenkins/nodes/develop/dev-slave01.json +0 -17
  36. data/example/config/jenkins/nodes/production/prod-slave01.json +0 -17
  37. data/example/config/jenkins/nodes/production/prod-slave02.json +0 -17
  38. data/example/config/jenkins/nodes/production/prod-slave03.json +0 -17
  39. data/example/config/jenkins/nodes/staging/stg-slave01.json +0 -17
  40. data/example/config/jenkins/nodes/staging/stg-slave02.json +0 -17
  41. data/example/config/jenkins/nodes/staging/stg-slave03.json +0 -17
  42. data/lib/jenkins-capistrano/client.rb +0 -66
  43. data/lib/jenkins-capistrano/client/job.rb +0 -47
  44. data/lib/jenkins-capistrano/client/node.rb +0 -103
  45. data/lib/jenkins-capistrano/client/plugin_manager.rb +0 -51
  46. data/lib/jenkins-capistrano/client/update_center.rb +0 -43
  47. data/lib/jenkins-capistrano/client/view.rb +0 -40
@@ -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 'httparty', '~> 0.8.3'
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
@@ -1,216 +1,3 @@
1
1
  require 'jenkins-capistrano/version'
2
- require 'jenkins-capistrano/client'
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