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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cd2670b5a14fac9a1b92c16876c4d361f5d6f49a
4
- data.tar.gz: a23addf61909a7feb8b9f56bdfbaff2faf30fcef
3
+ metadata.gz: 974ad4c20edcc72fab530b9d6292a1d81598b3ac
4
+ data.tar.gz: 622e28bd20fd84731ce311441a2d292491e210cd
5
5
  SHA512:
6
- metadata.gz: be256bfe8ee18d7c03d4f92424f37ed98eb6ff2e92d776feafafd351f382bfdb981de5c5766a4a25d8819b4e035b447ba25b19a5c91bf6e17c6e4961eb604b29
7
- data.tar.gz: 6f638ef633763f9ac4a4f4a2b097cb508031eae0fb2482e14d654991c42375c2942ef50d85b56f6ca9a01d6c6057042e39b1b26f6c4c9f622807343fead188b2
6
+ metadata.gz: 2a70027a504454016d46d1ee4ff76ebdcea9eaa5950b25700308ab622bf0a7b5c309bf922404f9018569c6a4d309bd9f549c02c57dc13ea987618c644071c930
7
+ data.tar.gz: 0708eda8da10d0b12085767410969bb9a138871755a768537190c1503806f282f7977df3afe3d9b4e7d815c4c02be5e0b38105b81aeb4038856b0af70978c021
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ .bundle/
2
+ vendor/
3
+ Gemfile.lock
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.0
6
+ - 2.2.0
7
+ before_install:
8
+ - gem update bundler
data/Gemfile CHANGED
@@ -1,4 +1,3 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in deployinator.gemspec
4
3
  gemspec
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 install --binstubs deployinator
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
@@ -1,4 +1,3 @@
1
-
2
1
  require 'rake/testtask'
3
2
  require 'rdoc/task'
4
3
  require 'bundler/gem_tasks'
@@ -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
- gem.add_runtime_dependency "pony"
26
- gem.add_runtime_dependency "tlsmail"
27
- gem.add_runtime_dependency "eventmachine"
28
- gem.add_runtime_dependency "eventmachine-tail"
29
- gem.add_runtime_dependency "em-websocket"
30
- gem.add_runtime_dependency "sinatra", ">=1.4.3"
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
@@ -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/s) {" "}
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)[:stdout]
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)[:stdout]
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 = "cd #{path} && git fetch --quiet origin +refs/heads/#{branch}:refs/remotes/origin/#{branch} && git reset --hard origin/#{branch} 2>&1"
99
- cmd = build_git_cmd(cmd, extra_cmd)
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, "git", read_write), extra_cmd, checkout_root, branch)
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
- File.open(filename, 'w') {|f| f.write(head_rev) }
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}"}