etsy-deployinator 1.0.1 → 1.1.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.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.travis.yml +8 -0
- data/Gemfile +0 -1
- data/README.md +47 -10
- data/Rakefile +0 -1
- data/bin/deployinator-tailer.rb +2 -2
- data/deployinator.gemspec +17 -10
- data/lib/deployinator/app.rb +53 -12
- data/lib/deployinator/controller.rb +8 -3
- data/lib/deployinator/helpers/concurrency.rb +70 -0
- data/lib/deployinator/helpers/dsh.rb +15 -5
- data/lib/deployinator/helpers/git.rb +16 -9
- data/lib/deployinator/helpers/version.rb +52 -52
- data/lib/deployinator/helpers.rb +109 -13
- data/lib/deployinator/static/css/maintenance.css +9 -0
- data/lib/deployinator/static/css/style.css +25 -5
- data/lib/deployinator/static/images/maintenance.gif +0 -0
- data/lib/deployinator/static/js/stats_load.js +10 -0
- data/lib/deployinator/templates/exception.mustache +11 -0
- data/lib/deployinator/templates/generic_single_push.mustache +2 -1
- data/lib/deployinator/templates/layout.mustache +5 -4
- data/lib/deployinator/templates/log_table.mustache +18 -5
- data/lib/deployinator/templates/maintenance.mustache +5 -0
- data/lib/deployinator/templates/stats.mustache +180 -0
- data/lib/deployinator/version.rb +1 -1
- data/lib/deployinator/views/log_table.rb +36 -0
- data/lib/deployinator/views/maintenance.rb +15 -0
- data/lib/deployinator/views/stats.rb +96 -0
- data/lib/deployinator.rb +20 -1
- data/test/unit/concurrency_test.rb +72 -0
- data/test/unit/git_test.rb +70 -0
- data/test/unit/helpers_test.rb +61 -2
- data/test/unit/version_test.rb +12 -12
- metadata +129 -27
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 974ad4c20edcc72fab530b9d6292a1d81598b3ac
|
|
4
|
+
data.tar.gz: 622e28bd20fd84731ce311441a2d292491e210cd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2a70027a504454016d46d1ee4ff76ebdcea9eaa5950b25700308ab622bf0a7b5c309bf922404f9018569c6a4d309bd9f549c02c57dc13ea987618c644071c930
|
|
7
|
+
data.tar.gz: 0708eda8da10d0b12085767410969bb9a138871755a768537190c1503806f282f7977df3afe3d9b4e7d815c4c02be5e0b38105b81aeb4038856b0af70978c021
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
_________ ______ _____ _____
|
|
3
3
|
______ /_____ ________ ___ /______ _____ _____(_)_______ ______ ___ /_______ ________
|
|
4
4
|
_ __ / _ _ \___ __ \__ / _ __ \__ / / /__ / __ __ \_ __ `/_ __/_ __ \__ ___/
|
|
5
|
-
/ /_/ / / __/__ /_/ /_ / / /_/ /_ /_/ / _ / _ / / // /_/ / / /_ / /_/ /_ /
|
|
6
|
-
\__,_/ \___/ _ .___/ /_/ \____/ _\__, / /_/ /_/ /_/ \__,_/ \__/ \____/ /_/
|
|
5
|
+
/ /_/ / / __/__ /_/ /_ / / /_/ /_ /_/ / _ / _ / / // /_/ / / /_ / /_/ /_ /
|
|
6
|
+
\__,_/ \___/ _ .___/ /_/ \____/ _\__, / /_/ /_/ /_/ \__,_/ \__/ \____/ /_/
|
|
7
7
|
/_/ /____/ Deploy with style!
|
|
8
8
|
</pre>
|
|
9
9
|
|
|
@@ -28,9 +28,9 @@ Deployinator is a deployment framework extracted from Etsy. We've been using it
|
|
|
28
28
|
## Stacks
|
|
29
29
|
Deployments are grouped by "stacks". You might have a "web" and "search" stack.
|
|
30
30
|
|
|
31
|
-
Each of those stacks might have different deployment environments, such as "staging" or "production".
|
|
31
|
+
Each of those stacks might have different deployment environments, such as "staging" or "production".
|
|
32
32
|
|
|
33
|
-
You can map a button to each of these environments, to create multi-stage pushes within each stack.
|
|
33
|
+
You can map a button to each of these environments, to create multi-stage pushes within each stack.
|
|
34
34
|
|
|
35
35
|
## Installation
|
|
36
36
|
|
|
@@ -55,13 +55,14 @@ you can skip the bundler steps.
|
|
|
55
55
|
- Run the following command to make deployinator gem's rake tasks available to you:
|
|
56
56
|
|
|
57
57
|
```sh
|
|
58
|
+
$ shopt -s xpg_echo
|
|
58
59
|
$ echo "require 'deployinator'\nload 'deployinator/tasks/initialize.rake' " > Rakefile
|
|
59
60
|
```
|
|
60
61
|
|
|
61
62
|
- Create a binstub for the deploy log tailing backend:
|
|
62
63
|
|
|
63
64
|
```sh
|
|
64
|
-
bundle
|
|
65
|
+
bundle binstub etsy-deployinator
|
|
65
66
|
```
|
|
66
67
|
|
|
67
68
|
- Initialize the project directory by running the following command replacing ___Company___ with the name of your company/organization. This must start with a capital letter.
|
|
@@ -116,14 +117,14 @@ underscores but if you forget the rake task will convert from camelcase for you.
|
|
|
116
117
|
$ bundle exec shotgun --host localhost -p 7777 config.ru
|
|
117
118
|
```
|
|
118
119
|
|
|
119
|
-
- You will probably want a robust server like apache to handle production traffic.
|
|
120
|
+
- You will probably want a robust server like apache to handle production traffic.
|
|
120
121
|
|
|
121
122
|
- The `config/base.rb` file is the base config for the application. Replace all
|
|
122
123
|
occurences of ___test_stack___ with the name you chose above. Also the example
|
|
123
124
|
below uses a git repository of http://github.com/etsy/deployinator, feel free to
|
|
124
125
|
replace this with your specific repository
|
|
125
126
|
|
|
126
|
-
```ruby
|
|
127
|
+
```ruby
|
|
127
128
|
Deployinator.app_context['test_stack_config'] = {
|
|
128
129
|
:prod_host => "localhost",
|
|
129
130
|
:checkout_path => "/tmp/deployinator_dev/"
|
|
@@ -232,7 +233,7 @@ For example you could wrap your capistrano deploy:
|
|
|
232
233
|
run_cmd %Q{cap deploy}
|
|
233
234
|
|
|
234
235
|
|
|
235
|
-
#### log_and_stream
|
|
236
|
+
#### log_and_stream
|
|
236
237
|
|
|
237
238
|
Output information to the log file, and the streaming output handler.
|
|
238
239
|
The real time output console renders HTML so you should use markup here.
|
|
@@ -241,7 +242,7 @@ The real time output console renders HTML so you should use markup here.
|
|
|
241
242
|
|
|
242
243
|
#### log_and_shout
|
|
243
244
|
|
|
244
|
-
Output an announcement message with build related information.
|
|
245
|
+
Output an announcement message with build related information.
|
|
245
246
|
Also includes hooks for Email.
|
|
246
247
|
|
|
247
248
|
log_and_shout({
|
|
@@ -348,8 +349,20 @@ To set these simple override the methods in your view class. For example:
|
|
|
348
349
|
This can be done on a global layout that extends the gem's default layout or on
|
|
349
350
|
a stack by stack basis in their own view.
|
|
350
351
|
|
|
352
|
+
### Maintenance mode
|
|
353
|
+
Deployinator has a setting for maintenance mode, which is mostly useful if you
|
|
354
|
+
have major changes that affect all stacks and you want to make sure no deploys
|
|
355
|
+
are going on while you make the change. In order to enable it, you have to set
|
|
356
|
+
`Deployinator.maintenance_mode = true`. This will make all pages for anyone
|
|
357
|
+
not in `Deployinator.admin_groups` go to `/maintenance`. As a Deployinator
|
|
358
|
+
admin you can then still deploy stacks and use the app.
|
|
359
|
+
|
|
360
|
+
On the maintenance page Deployinator will show the value of
|
|
361
|
+
`Deployinator.maintenance_contact` as the place to get help in case you need
|
|
362
|
+
any or are confused about maintenance mode.
|
|
363
|
+
|
|
351
364
|
## Hacking on the gem
|
|
352
|
-
If you find issues with the gem, or would like to play around with it, you can check it out from git and start hacking on it.
|
|
365
|
+
If you find issues with the gem, or would like to play around with it, you can check it out from git and start hacking on it.
|
|
353
366
|
First tell bundler to use your local copy instead by running:
|
|
354
367
|
```sh
|
|
355
368
|
$ bundle config local.deployinator /path/to/DeployinatorGem
|
|
@@ -359,6 +372,30 @@ Next, on every code change, you can install from the checked out gem by running
|
|
|
359
372
|
$ bundle install --no-deployment && bundle install --deployment
|
|
360
373
|
```
|
|
361
374
|
|
|
375
|
+
### Stats dashboard
|
|
376
|
+
The `/stats` page pulls from `log/deployinator.log` to show a graph of deployments per day for each stack over time. By default, it shows all stacks. To blacklist or whitelist certain stacks, update `config/base.rb` with:
|
|
377
|
+
```rb
|
|
378
|
+
Deployinator.stats_included_stacks = ['my_whitelisted_stack', 'another_whitelisted_stack']
|
|
379
|
+
Deployinator.stats_ignored_stacks = ['my_stack_to_ignore', 'another_stack_to_ignore']
|
|
380
|
+
Deployinator.stats_extra_grep = 'Production deploy' # filter out log entries matching this string
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
Whitelisting stacks or applying a custom extra grep can help speed up graph rendering when you have a large log file.
|
|
384
|
+
|
|
385
|
+
If at some point you change the name of a stack, you can group the old log entries with the new by adding the following to `config/base.rb`:
|
|
386
|
+
|
|
387
|
+
```rb
|
|
388
|
+
Deployinator.stats_renamed_stacks = [
|
|
389
|
+
{
|
|
390
|
+
:previous_stack => {
|
|
391
|
+
:stack => [ "old_stack_name" ]
|
|
392
|
+
},
|
|
393
|
+
:new_name => "new_stack_name"
|
|
394
|
+
}
|
|
395
|
+
]
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
|
|
362
399
|
### Contributing
|
|
363
400
|
|
|
364
401
|
1. Fork it
|
data/Rakefile
CHANGED
data/bin/deployinator-tailer.rb
CHANGED
|
@@ -54,8 +54,8 @@ EM.run do
|
|
|
54
54
|
if stack && @@channels.key?(stack)
|
|
55
55
|
@@channels[stack].unsubscribe(session_id)
|
|
56
56
|
@@channels_count[stack] -= 1
|
|
57
|
-
if @@channels_count[stack] == 0
|
|
58
|
-
@@tailers[stack].close
|
|
57
|
+
if @@channels_count[stack] == 0 && @@tailers.has_key?(stack)
|
|
58
|
+
@@tailers[stack].close
|
|
59
59
|
@@tailers.delete(stack)
|
|
60
60
|
end
|
|
61
61
|
end
|
data/deployinator.gemspec
CHANGED
|
@@ -10,22 +10,29 @@ Gem::Specification.new do |gem|
|
|
|
10
10
|
gem.email = ["jpaul@etsy.com"]
|
|
11
11
|
gem.description = %q{Deployinator as a Gem}
|
|
12
12
|
gem.summary = %q{Rewrite of deployinator to be a gem}
|
|
13
|
-
gem.homepage = ""
|
|
13
|
+
gem.homepage = "http://github.com/etsy/Deployinator"
|
|
14
|
+
gem.licenses = ["MIT"]
|
|
14
15
|
|
|
15
16
|
gem.files = `git ls-files`.split($/)
|
|
16
17
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
|
17
18
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
|
18
19
|
gem.require_paths = ["lib"]
|
|
20
|
+
gem.required_ruby_version = '>= 1.9.3'
|
|
19
21
|
|
|
20
|
-
gem.add_development_dependency "mocha"
|
|
22
|
+
gem.add_development_dependency "mocha", "~> 0.14"
|
|
23
|
+
gem.add_development_dependency "test-unit", ">= 3"
|
|
24
|
+
gem.add_development_dependency "rdoc", "~> 4.2"
|
|
21
25
|
|
|
22
|
-
gem.add_runtime_dependency "rake"
|
|
23
|
-
gem.add_runtime_dependency "json"
|
|
26
|
+
gem.add_runtime_dependency "rake", "~> 10", ">= 10.3.2"
|
|
27
|
+
gem.add_runtime_dependency "json", "~> 1.8"
|
|
24
28
|
gem.add_runtime_dependency "mustache", "~> 0.99"
|
|
25
|
-
|
|
26
|
-
gem.add_runtime_dependency "
|
|
27
|
-
gem.add_runtime_dependency "
|
|
28
|
-
gem.add_runtime_dependency "
|
|
29
|
-
gem.add_runtime_dependency "
|
|
30
|
-
gem.add_runtime_dependency "
|
|
29
|
+
# pony pulls in mail >2.0. mail 3.0 needs ruby 2 or greater. to maintain ruby 1.9 compat we're pinning mail here
|
|
30
|
+
gem.add_runtime_dependency "mail", "2.6.3"
|
|
31
|
+
gem.add_runtime_dependency "pony", "~> 1.5"
|
|
32
|
+
gem.add_runtime_dependency "tlsmail", "~> 0.0"
|
|
33
|
+
gem.add_runtime_dependency "eventmachine", "~> 1.0", ">= 1.0.4"
|
|
34
|
+
gem.add_runtime_dependency "eventmachine-tail", "~> 0.6", ">= 0.6.5"
|
|
35
|
+
gem.add_runtime_dependency "em-websocket", "~> 0.5", ">= 0.5.1"
|
|
36
|
+
gem.add_runtime_dependency "sinatra", "~> 1.4", ">=1.4.3"
|
|
37
|
+
gem.add_runtime_dependency "celluloid", "~> 0.16"
|
|
31
38
|
end
|
data/lib/deployinator/app.rb
CHANGED
|
@@ -12,6 +12,8 @@ require 'deployinator/views/log'
|
|
|
12
12
|
require 'deployinator/views/run_logs'
|
|
13
13
|
require 'deployinator/views/log_table'
|
|
14
14
|
require 'deployinator/views/deploys_status'
|
|
15
|
+
require 'deployinator/views/stats'
|
|
16
|
+
require 'deployinator/views/maintenance'
|
|
15
17
|
|
|
16
18
|
module Deployinator
|
|
17
19
|
class DeployinatorApp < Sinatra::Base
|
|
@@ -41,12 +43,32 @@ module Deployinator
|
|
|
41
43
|
register_plugins(nil)
|
|
42
44
|
init(env)
|
|
43
45
|
@disabled_override = params[:override].nil? ? false : true
|
|
46
|
+
pass if [
|
|
47
|
+
"/maintenance",
|
|
48
|
+
"/css/maintenance.css",
|
|
49
|
+
"/images/maintenance.gif",
|
|
50
|
+
"/static/css/style.css"
|
|
51
|
+
].include? request.path_info
|
|
52
|
+
if Deployinator.maintenance_mode == true && !is_admin?
|
|
53
|
+
redirect "/maintenance"
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
before do
|
|
44
58
|
end
|
|
45
59
|
|
|
46
60
|
get '/' do
|
|
47
61
|
mustache Deployinator::Views::Index
|
|
48
62
|
end
|
|
49
63
|
|
|
64
|
+
get '/stats' do
|
|
65
|
+
mustache Deployinator::Views::Stats
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
get '/maintenance' do
|
|
69
|
+
mustache Deployinator::Views::Maintenance
|
|
70
|
+
end
|
|
71
|
+
|
|
50
72
|
get '/run_logs/view/:log' do
|
|
51
73
|
template = open("#{File.dirname(__FILE__)}/templates/stream.mustache").read
|
|
52
74
|
template.gsub!("{{ yield }}", "{{{yield}}}")
|
|
@@ -95,17 +117,6 @@ module Deployinator
|
|
|
95
117
|
git_head_rev(params[:stack]).chomp
|
|
96
118
|
end
|
|
97
119
|
|
|
98
|
-
get '/:thing' do
|
|
99
|
-
@stack = params[:thing]
|
|
100
|
-
@params = params
|
|
101
|
-
register_plugins(@stack)
|
|
102
|
-
begin
|
|
103
|
-
mustache @stack
|
|
104
|
-
rescue Errno::ENOENT
|
|
105
|
-
pass
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
|
-
|
|
109
120
|
get '/:stack/remove-lock' do
|
|
110
121
|
stack = params[:stack]
|
|
111
122
|
unlock_pushes(stack) if can_remove_stack_lock?
|
|
@@ -129,6 +140,10 @@ module Deployinator
|
|
|
129
140
|
send_file "#{File.dirname(__FILE__)}/static/css/highlight.css"
|
|
130
141
|
end
|
|
131
142
|
|
|
143
|
+
get '/css/maintenance.css' do
|
|
144
|
+
send_file "#{File.dirname(__FILE__)}/static/css/maintenance.css"
|
|
145
|
+
end
|
|
146
|
+
|
|
132
147
|
get '/js/flot/jquery.flot.min.js' do
|
|
133
148
|
send_file "#{File.dirname(__FILE__)}/static/js/flot/jquery.flot.min.js"
|
|
134
149
|
end
|
|
@@ -149,6 +164,14 @@ module Deployinator
|
|
|
149
164
|
send_file "#{File.dirname(__FILE__)}/static/js/jquery.timed_bar.js"
|
|
150
165
|
end
|
|
151
166
|
|
|
167
|
+
get '/js/stats_load.js' do
|
|
168
|
+
send_file "#{File.dirname(__FILE__)}/static/js/stats_load.js"
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
get '/images/maintenance.gif' do
|
|
172
|
+
send_file "#{File.dirname(__FILE__)}/static/images/maintenance.gif"
|
|
173
|
+
end
|
|
174
|
+
|
|
152
175
|
get '/deploys_status' do
|
|
153
176
|
mustache Deployinator::Views::DeploysStatus
|
|
154
177
|
end
|
|
@@ -172,6 +195,7 @@ module Deployinator
|
|
|
172
195
|
# the background.
|
|
173
196
|
post '/deploys/?' do
|
|
174
197
|
params[:username] = @username
|
|
198
|
+
params[:groups] = @groups
|
|
175
199
|
params[:block] = Proc.new { |line| foo = line }
|
|
176
200
|
deploy_running = is_deploy_active?(params[:stack], params[:stage])
|
|
177
201
|
|
|
@@ -180,7 +204,7 @@ module Deployinator
|
|
|
180
204
|
return 403
|
|
181
205
|
end
|
|
182
206
|
|
|
183
|
-
fork {
|
|
207
|
+
pid = fork {
|
|
184
208
|
Signal.trap("HUP") { exit }
|
|
185
209
|
Deployinator.setup_logging
|
|
186
210
|
$0 = get_deploy_process_title(params[:stack], params[:stage])
|
|
@@ -188,6 +212,7 @@ module Deployinator
|
|
|
188
212
|
d = controller.new
|
|
189
213
|
d.run(params)
|
|
190
214
|
}
|
|
215
|
+
Process.detach(pid)
|
|
191
216
|
200
|
|
192
217
|
end
|
|
193
218
|
|
|
@@ -200,5 +225,21 @@ module Deployinator
|
|
|
200
225
|
|
|
201
226
|
200
|
|
202
227
|
end
|
|
228
|
+
|
|
229
|
+
get '/:thing' do
|
|
230
|
+
@stack = params[:thing]
|
|
231
|
+
|
|
232
|
+
unless Deployinator.get_stacks.include?(@stack)
|
|
233
|
+
raise "No such stack #{@stack}"
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
@params = params
|
|
237
|
+
register_plugins(@stack)
|
|
238
|
+
begin
|
|
239
|
+
mustache @stack
|
|
240
|
+
rescue Errno::ENOENT
|
|
241
|
+
pass
|
|
242
|
+
end
|
|
243
|
+
end
|
|
203
244
|
end
|
|
204
245
|
end
|
|
@@ -29,6 +29,7 @@ module Deployinator
|
|
|
29
29
|
@deploy_start_time = Time.now.to_i
|
|
30
30
|
@start_time = Time.now.to_i
|
|
31
31
|
@username = args[:username]
|
|
32
|
+
@groups = args[:groups]
|
|
32
33
|
@host = `hostname -s`
|
|
33
34
|
@stack = args[:stack]
|
|
34
35
|
@method = args[:method]
|
|
@@ -37,7 +38,7 @@ module Deployinator
|
|
|
37
38
|
|
|
38
39
|
# This gets the runlog output on the console; is used by log_and_stream
|
|
39
40
|
@block = args[:block] || Proc.new do |output|
|
|
40
|
-
$stdout.write output.gsub!(/(<[^>]*>)|\n|\t/
|
|
41
|
+
$stdout.write output.gsub!(/(<[^>]*>)|\n|\t/m) {" "}
|
|
41
42
|
$stdout.write "\n"
|
|
42
43
|
end
|
|
43
44
|
end
|
|
@@ -102,7 +103,11 @@ module Deployinator
|
|
|
102
103
|
deploy_instance = deploy_class.new(options)
|
|
103
104
|
deploy_instance.register_plugins(options[:stack])
|
|
104
105
|
|
|
105
|
-
deploy_instance.lock_pushes(options[:stack], options[:username], options[:method])
|
|
106
|
+
locked = deploy_instance.lock_pushes(options[:stack], options[:username], options[:method])
|
|
107
|
+
|
|
108
|
+
unless locked
|
|
109
|
+
return deploy_instance
|
|
110
|
+
end
|
|
106
111
|
|
|
107
112
|
@start_time = Time.now
|
|
108
113
|
deploy_instance.log_and_stream "Push started at #{@start_time.to_i}\n"
|
|
@@ -115,7 +120,7 @@ module Deployinator
|
|
|
115
120
|
state = deploy_instance.send(options[:method], options)
|
|
116
121
|
rescue Exception => e
|
|
117
122
|
deploy_instance.log_error("There was an exception during this deploy. Aborted!", e)
|
|
118
|
-
deploy_instance.raise_event(:deploy_error)
|
|
123
|
+
deploy_instance.raise_event(:deploy_error, {:exception => e})
|
|
119
124
|
end
|
|
120
125
|
|
|
121
126
|
if state.nil? || !state.is_a?(Hash)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
require 'celluloid'
|
|
2
|
+
require 'celluloid/autostart'
|
|
3
|
+
|
|
4
|
+
module Deployinator
|
|
5
|
+
module Helpers
|
|
6
|
+
module ConcurrencyHelpers
|
|
7
|
+
extend Celluloid
|
|
8
|
+
# Hash of future objects that have been instantiated so far
|
|
9
|
+
@@futures = {}
|
|
10
|
+
# Public: run block of code in parallel
|
|
11
|
+
#
|
|
12
|
+
# Returns Handle to future object created
|
|
13
|
+
def run_parallel(name, &block)
|
|
14
|
+
name = name.to_sym
|
|
15
|
+
if reference_taken? name
|
|
16
|
+
raise DuplicateReferenceError, "Name #{name} already taken for future."
|
|
17
|
+
end
|
|
18
|
+
log_and_stream '</br>Queueing execution of future: ' + name.to_s + '</br>'
|
|
19
|
+
@@futures[name] = Celluloid::Future.new do
|
|
20
|
+
# Set filename for thread
|
|
21
|
+
runlog_filename(name)
|
|
22
|
+
# setting up separate logger
|
|
23
|
+
log_and_stream '</br>Starting execution of future: ' + name.to_s + '</br>'
|
|
24
|
+
block.call
|
|
25
|
+
end
|
|
26
|
+
@@futures[name]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# Public: check if the reference name for future is taken
|
|
33
|
+
#
|
|
34
|
+
# Returns boolean
|
|
35
|
+
def reference_taken?(name)
|
|
36
|
+
return @@futures.has_key?(name)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Public: returns the value of the code block execution
|
|
40
|
+
# This also sends all the logged data in stream back to main
|
|
41
|
+
# log file and removes the temporary log file created for the thread
|
|
42
|
+
# Returns the return value of the last line executed in block
|
|
43
|
+
def get_value(future, timeout=nil)
|
|
44
|
+
if @filename
|
|
45
|
+
file_path = "#{RUN_LOG_PATH}" + runlog_thread_filename(future)
|
|
46
|
+
return_value = @@futures[future.to_sym].value(timeout)
|
|
47
|
+
log_and_stream File.read(file_path)
|
|
48
|
+
File.delete(file_path) if File.exists?(file_path)
|
|
49
|
+
return_value
|
|
50
|
+
else
|
|
51
|
+
@@futures[future.to_sym].value
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Public: returns the values of the specified futures
|
|
56
|
+
#
|
|
57
|
+
# Returns hash of values
|
|
58
|
+
def get_values(*futures)
|
|
59
|
+
value_hash = {}
|
|
60
|
+
futures.each do |future|
|
|
61
|
+
value_hash[future] = get_value(future)
|
|
62
|
+
end
|
|
63
|
+
value_hash
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
class DuplicateReferenceError < StandardError
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -5,14 +5,19 @@ module Deployinator
|
|
|
5
5
|
@dsh_fanout || 30
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
-
def run_dsh(groups, cmd, &block)
|
|
8
|
+
def run_dsh(groups, cmd, only_stdout=true, &block)
|
|
9
9
|
groups = [groups] unless groups.is_a?(Array)
|
|
10
10
|
dsh_groups = groups.map {|group| "-g #{group} "}.join("")
|
|
11
|
-
run_cmd(%Q{ssh #{Deployinator.default_user}@#{Deployinator.deploy_host} dsh #{dsh_groups} -r ssh -F #{dsh_fanout} "#{cmd}"}, &block)
|
|
11
|
+
cmd_return = run_cmd(%Q{ssh #{Deployinator.default_user}@#{Deployinator.deploy_host} dsh #{dsh_groups} -r ssh -F #{dsh_fanout} "#{cmd}"}, &block)
|
|
12
|
+
if only_stdout
|
|
13
|
+
cmd_return[:stdout]
|
|
14
|
+
else
|
|
15
|
+
cmd_return
|
|
16
|
+
end
|
|
12
17
|
end
|
|
13
18
|
|
|
14
19
|
# run dsh against a given host or array of hosts
|
|
15
|
-
def run_dsh_hosts(hosts, cmd, extra_opts='', &block)
|
|
20
|
+
def run_dsh_hosts(hosts, cmd, extra_opts='', only_stdout=true, &block)
|
|
16
21
|
hosts = [hosts] unless hosts.is_a?(Array)
|
|
17
22
|
if extra_opts.length > 0
|
|
18
23
|
run_cmd %Q{ssh #{Deployinator.default_user}@#{Deployinator.deploy_host} 'dsh -m #{hosts.join(',')} -r ssh -F #{dsh_fanout} #{extra_opts} -- "#{cmd}"'}, &block
|
|
@@ -21,9 +26,14 @@ module Deployinator
|
|
|
21
26
|
end
|
|
22
27
|
end
|
|
23
28
|
|
|
24
|
-
def run_dsh_extra(dsh_group, cmd, extra_opts, &block)
|
|
29
|
+
def run_dsh_extra(dsh_group, cmd, extra_opts, only_stdout=true, &block)
|
|
25
30
|
# runs dsh to a single group with extra args to dsh
|
|
26
|
-
run_cmd(%Q{ssh #{Deployinator.default_user}@#{Deployinator.deploy_host} dsh -g #{dsh_group} -r ssh #{extra_opts} -F #{dsh_fanout} "#{cmd}"}, &block)
|
|
31
|
+
cmd_return = run_cmd(%Q{ssh #{Deployinator.default_user}@#{Deployinator.deploy_host} dsh -g #{dsh_group} -r ssh #{extra_opts} -F #{dsh_fanout} "#{cmd}"}, &block)
|
|
32
|
+
if only_stdout
|
|
33
|
+
cmd_return[:stdout]
|
|
34
|
+
else
|
|
35
|
+
cmd_return
|
|
36
|
+
end
|
|
27
37
|
end
|
|
28
38
|
|
|
29
39
|
def hosts_for(group)
|
|
@@ -93,10 +93,16 @@ module Deployinator
|
|
|
93
93
|
# branch - the branch to checkout after the fetch
|
|
94
94
|
#
|
|
95
95
|
# Returns nothing
|
|
96
|
-
def git_freshen_clone(stack, extra_cmd="", path=nil, branch="master")
|
|
96
|
+
def git_freshen_clone(stack, extra_cmd="", path=nil, branch="master", force_checkout=false)
|
|
97
97
|
path ||= git_checkout_path(checkout_root, stack)
|
|
98
|
-
cmd =
|
|
99
|
-
|
|
98
|
+
cmd = [
|
|
99
|
+
"cd #{path}",
|
|
100
|
+
"git fetch --quiet origin +refs/heads/#{branch}:refs/remotes/origin/#{branch}",
|
|
101
|
+
"git reset --hard origin/#{branch} 2>&1",
|
|
102
|
+
"git checkout #{'--force' if force_checkout} #{branch} 2>&1",
|
|
103
|
+
]
|
|
104
|
+
cmd << "git reset --hard origin/#{branch} 2>&1"
|
|
105
|
+
cmd = build_git_cmd(cmd.join(" && "), extra_cmd)
|
|
100
106
|
run_cmd cmd
|
|
101
107
|
yield "#{path}" if block_given?
|
|
102
108
|
end
|
|
@@ -117,15 +123,15 @@ module Deployinator
|
|
|
117
123
|
# read_write - boolean; True means clone the repo read/write
|
|
118
124
|
#
|
|
119
125
|
# Returns stdout of the respective git command.
|
|
120
|
-
def git_freshen_or_clone(stack, extra_cmd, checkout_root, branch="master", read_write=false)
|
|
126
|
+
def git_freshen_or_clone(stack, extra_cmd, checkout_root, branch="master", read_write=false, protocol="git", force_checkout=false)
|
|
121
127
|
path = git_checkout_path(checkout_root, stack)
|
|
122
128
|
is_git = is_git_repo(path, extra_cmd)
|
|
123
129
|
if is_git == :true
|
|
124
130
|
log_and_stream "</br>Refreshing repo #{stack} at #{path}</br>"
|
|
125
|
-
git_freshen_clone(stack, extra_cmd, path, branch)
|
|
131
|
+
git_freshen_clone(stack, extra_cmd, path, branch, force_checkout)
|
|
126
132
|
elsif is_git == :missing
|
|
127
133
|
log_and_stream "</br>Cloning branch #{branch} of #{stack} repo into #{path}</br>"
|
|
128
|
-
git_clone(stack, git_url(stack,
|
|
134
|
+
git_clone(stack, git_url(stack, protocol, read_write), extra_cmd, checkout_root, branch)
|
|
129
135
|
else
|
|
130
136
|
log_and_stream "</br><span class=\"stderr\">The path for #{stack} at #{path} exists but is not a git repo.</span></br>"
|
|
131
137
|
end
|
|
@@ -188,7 +194,7 @@ module Deployinator
|
|
|
188
194
|
|
|
189
195
|
unless head_rev
|
|
190
196
|
head_rev = get_git_head_rev(stack, branch)
|
|
191
|
-
|
|
197
|
+
write_to_cache(filename, head_rev)
|
|
192
198
|
end
|
|
193
199
|
|
|
194
200
|
return head_rev
|
|
@@ -329,11 +335,12 @@ module Deployinator
|
|
|
329
335
|
# ssh_cmd: string ssh cmd to get to a host where you've got this repo checked out
|
|
330
336
|
# extra: string any extra cmds like cd that you need to do on the remote host to get to your checkout
|
|
331
337
|
# quiet: boolean - if true we make no additional output and just return the files
|
|
338
|
+
# diff_filter: string to pass to git to make it only show certain types of changes (added/removed)
|
|
332
339
|
#
|
|
333
340
|
# Returns:
|
|
334
341
|
# Array of files names changed between these revs
|
|
335
|
-
def git_show_changed_files(rev1, rev2, ssh_cmd, extra=nil, quiet=false)
|
|
336
|
-
cmd = %Q{git log --name-only --pretty=oneline --full-index #{rev1}..#{rev2} | grep -vE '^[0-9a-f]{40} ' | sort | uniq}
|
|
342
|
+
def git_show_changed_files(rev1, rev2, ssh_cmd, extra=nil, quiet=false, diff_filter="")
|
|
343
|
+
cmd = %Q{git log --name-only --pretty=oneline --full-index #{rev1}..#{rev2} --diff-filter=#{diff_filter} | grep -vE '^[0-9a-f]{40} ' | sort | uniq}
|
|
337
344
|
extra = "#{extra} &&" unless extra.nil?
|
|
338
345
|
if quiet
|
|
339
346
|
list_of_touched_files = %x{#{ssh_cmd} "#{extra} #{cmd}"}
|