engineyard-serverside 2.7.8pre2 → 2.8.0.pre

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 (96) hide show
  1. data/bin/engineyard-serverside +1 -1
  2. data/bin/engineyard-serverside-execute-hook +1 -1
  3. data/bin/engineyard-serverside-execute-service-hook +35 -0
  4. data/lib/engineyard-serverside.rb +0 -1
  5. data/lib/engineyard-serverside/about.rb +11 -8
  6. data/lib/engineyard-serverside/callbacks.rb +11 -0
  7. data/lib/engineyard-serverside/callbacks/collection.rb +17 -0
  8. data/lib/engineyard-serverside/callbacks/collection/base.rb +79 -0
  9. data/lib/engineyard-serverside/callbacks/collection/combined.rb +45 -0
  10. data/lib/engineyard-serverside/callbacks/collection/deploy_hooks.rb +21 -0
  11. data/lib/engineyard-serverside/callbacks/collection/service_hooks.rb +17 -0
  12. data/lib/engineyard-serverside/callbacks/collection/service_hooks/collection.rb +24 -0
  13. data/lib/engineyard-serverside/callbacks/collection/service_hooks/combined.rb +40 -0
  14. data/lib/engineyard-serverside/callbacks/distributor.rb +21 -0
  15. data/lib/engineyard-serverside/callbacks/distributor/remote.rb +76 -0
  16. data/lib/engineyard-serverside/callbacks/distributor/viability_filter.rb +66 -0
  17. data/lib/engineyard-serverside/callbacks/executor.rb +23 -0
  18. data/lib/engineyard-serverside/callbacks/executor/base.rb +44 -0
  19. data/lib/engineyard-serverside/callbacks/executor/executable.rb +123 -0
  20. data/lib/engineyard-serverside/callbacks/executor/ruby.rb +20 -0
  21. data/lib/engineyard-serverside/callbacks/executor/ruby/context.rb +81 -0
  22. data/lib/engineyard-serverside/callbacks/executor/ruby/executor.rb +118 -0
  23. data/{spec/fixtures/gitrepo/bar → lib/engineyard-serverside/callbacks/hooks.rb} +0 -0
  24. data/lib/engineyard-serverside/callbacks/hooks/app.rb +21 -0
  25. data/lib/engineyard-serverside/callbacks/hooks/base.rb +43 -0
  26. data/lib/engineyard-serverside/callbacks/hooks/service.rb +28 -0
  27. data/lib/engineyard-serverside/callbacks/service_hook.rb +20 -0
  28. data/lib/engineyard-serverside/cli.rb +4 -225
  29. data/lib/engineyard-serverside/cli/app.rb +136 -0
  30. data/lib/engineyard-serverside/cli/helpers.rb +58 -0
  31. data/lib/engineyard-serverside/cli/server_hash_extractor.rb +49 -0
  32. data/lib/engineyard-serverside/cli/workflows.rb +45 -0
  33. data/lib/engineyard-serverside/cli/workflows/base.rb +78 -0
  34. data/lib/engineyard-serverside/cli/workflows/calling_deploy_hooks.rb +31 -0
  35. data/lib/engineyard-serverside/cli/workflows/deploying_applications.rb +28 -0
  36. data/lib/engineyard-serverside/cli/workflows/disabling_maintenance.rb +29 -0
  37. data/lib/engineyard-serverside/cli/workflows/enabling_maintenance.rb +29 -0
  38. data/lib/engineyard-serverside/cli/workflows/errors.rb +13 -0
  39. data/lib/engineyard-serverside/cli/workflows/helpers.rb +21 -0
  40. data/lib/engineyard-serverside/cli/workflows/integrating_servers.rb +71 -0
  41. data/lib/engineyard-serverside/cli/workflows/restarting_applications.rb +36 -0
  42. data/lib/engineyard-serverside/cli/workflows/rolling_back_applications.rb +28 -0
  43. data/lib/engineyard-serverside/cli/workflows/showing_maintenance_status.rb +28 -0
  44. data/lib/engineyard-serverside/configuration.rb +1 -0
  45. data/lib/engineyard-serverside/dependency_manager/bundler.rb +46 -18
  46. data/lib/engineyard-serverside/dependency_manager/npm.rb +12 -1
  47. data/lib/engineyard-serverside/deploy.rb +7 -45
  48. data/lib/engineyard-serverside/maintenance.rb +1 -9
  49. data/lib/engineyard-serverside/paths.rb +11 -0
  50. data/lib/engineyard-serverside/propagator.rb +59 -0
  51. data/lib/engineyard-serverside/rails_assets.rb +2 -1
  52. data/lib/engineyard-serverside/slug.rb +7 -0
  53. data/lib/engineyard-serverside/slug/distributor.rb +58 -0
  54. data/lib/engineyard-serverside/slug/enabler.rb +100 -0
  55. data/lib/engineyard-serverside/slug/failure_handler.rb +24 -0
  56. data/lib/engineyard-serverside/slug/finalizer.rb +86 -0
  57. data/lib/engineyard-serverside/slug/generator.rb +29 -0
  58. data/lib/engineyard-serverside/slug/migrator.rb +41 -0
  59. data/lib/engineyard-serverside/slug/restarter.rb +103 -0
  60. data/lib/engineyard-serverside/slug/source.rb +16 -0
  61. data/lib/engineyard-serverside/slug/source/updater.rb +194 -0
  62. data/lib/engineyard-serverside/version.rb +1 -1
  63. data/lib/railway.rb +43 -0
  64. data/lib/result.rb +7 -0
  65. data/lib/result/base.rb +41 -0
  66. data/lib/result/dsl.rb +16 -0
  67. data/lib/result/failure.rb +29 -0
  68. data/lib/result/success.rb +24 -0
  69. data/lib/runner.rb +34 -0
  70. data/spec/archive_deploy_spec.rb +1 -1
  71. data/spec/bundler_deploy_spec.rb +22 -1
  72. data/spec/configuration_spec.rb +1 -0
  73. data/spec/deploy_hook_spec.rb +148 -132
  74. data/spec/fixtures/lockfiles/1.15.1-no-bundler +51 -0
  75. data/spec/fixtures/repos/assets_error/Gemfile +5 -0
  76. data/spec/fixtures/repos/assets_error/Gemfile.lock +88 -0
  77. data/spec/fixtures/repos/assets_error/README +1 -0
  78. data/spec/fixtures/repos/assets_error/Rakefile +4 -0
  79. data/spec/fixtures/{gitrepo/foo → repos/assets_error/app/assets/empty} +0 -0
  80. data/spec/fixtures/repos/assets_error/config/application.rb +5 -0
  81. data/spec/fixtures/repos/assets_error/config/ey.yml +4 -0
  82. data/spec/fixtures/repos/bundler_old/Gemfile +5 -0
  83. data/spec/fixtures/repos/bundler_old/Gemfile.lock +15 -0
  84. data/spec/fixtures/repos/bundler_old/README +1 -0
  85. data/spec/fixtures/repos/no_ey_config_no_warning/Gemfile +3 -0
  86. data/spec/fixtures/repos/no_ey_config_no_warning/Gemfile.lock +10 -0
  87. data/spec/fixtures/repos/no_ey_config_no_warning/README +1 -0
  88. data/spec/fixtures/repos/no_ey_config_no_warning/ey.yml +5 -0
  89. data/spec/lockfile_parser_spec.rb +5 -1
  90. data/spec/rails31_deploy_spec.rb +8 -0
  91. data/spec/rollback_spec.rb +1 -1
  92. data/spec/services_deploy_spec.rb +12 -0
  93. data/spec/spec_helper.rb +14 -8
  94. metadata +488 -429
  95. data/lib/engineyard-serverside/cli_helpers.rb +0 -53
  96. data/lib/engineyard-serverside/deploy_hook.rb +0 -142
@@ -0,0 +1,36 @@
1
+ require 'engineyard-serverside/cli/workflows/base'
2
+
3
+ module EY
4
+ module Serverside
5
+ module CLI
6
+ module Workflows
7
+
8
+ # RestartingApplications is a Workflow that restarts the application
9
+ # specified in the options
10
+ class RestartingApplications < Base
11
+ private
12
+ def procedure
13
+ restart_options[:release_path] = Pathname.new(
14
+ "/data/#{options[:app]}/current"
15
+ )
16
+
17
+ propagate_serverside
18
+
19
+ EY::Serverside::Deploy.
20
+ new(servers, config, shell).
21
+ restart_with_maintenance_page
22
+
23
+ end
24
+
25
+ def restart_options
26
+ @restart_options ||= options.dup
27
+ end
28
+
29
+ def task_name
30
+ 'restart'
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,28 @@
1
+ require 'engineyard-serverside/cli/workflows/base'
2
+ require 'engineyard-serverside/cli/workflows/helpers'
3
+
4
+ module EY
5
+ module Serverside
6
+ module CLI
7
+ module Workflows
8
+
9
+ # RollingBackApplications is a Workflow that attempts to roll the
10
+ # application specified in the options back to its previous deployment
11
+ class RollingBackApplications < Base
12
+ include Helpers
13
+
14
+ private
15
+ def procedure
16
+ propagate_serverside
17
+
18
+ deployer.rollback
19
+ end
20
+
21
+ def task_name
22
+ 'rollback'
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ require 'engineyard-serverside/cli/workflows/base'
2
+ require 'engineyard-serverside/cli/workflows/helpers'
3
+ require 'engineyard-serverside/maintenance'
4
+
5
+ module EY
6
+ module Serverside
7
+ module CLI
8
+ module Workflows
9
+
10
+ # ShowingMaintenanceStatus is a Workflow that reports the current
11
+ # enabled/disabled status of the maintenance page for the application
12
+ # specified in the options
13
+ class ShowingMaintenanceStatus < Base
14
+ include Helpers
15
+
16
+ private
17
+ def procedure
18
+ maintenance.status
19
+ end
20
+
21
+ def task_name
22
+ 'maintenance-status'
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -101,6 +101,7 @@ module EY
101
101
  def_option(:group) { user }
102
102
  def_option :services_check_command, "which /usr/local/ey_resin/ruby/bin/ey-services-setup >/dev/null 2>&1"
103
103
  def_option(:services_setup_command) { "/usr/local/ey_resin/ruby/bin/ey-services-setup #{app}" }
104
+ def_option :ignore_ey_config_warning, false
104
105
 
105
106
  DEFAULT_KEEP_RELEASES = 3
106
107
 
@@ -4,12 +4,19 @@ module EY
4
4
  module Serverside
5
5
  class DependencyManager
6
6
  class Bundler < Base
7
- DEFAULT_VERSION = "1.7.9"
7
+ DEFAULT_VERSION = "1.14.6"
8
+
9
+ # Number of Bundler workers (--jobs)
10
+ JOBS_NUMBER = 4
8
11
 
9
12
  def self.default_version
10
13
  DEFAULT_VERSION
11
14
  end
12
15
 
16
+ def self.jobs_number
17
+ JOBS_NUMBER
18
+ end
19
+
13
20
  def detected?
14
21
  gemfile?
15
22
  end
@@ -59,7 +66,7 @@ To fix this problem, commit your Gemfile.lock to your repository and redeploy.
59
66
  end
60
67
 
61
68
  def show_ey_config_instructions
62
- if lockfile && !lockfile.has_ey_config?
69
+ if lockfile && !lockfile.has_ey_config? && !config.ignore_ey_config_warning
63
70
  shell.warning "Gemfile.lock does not contain ey_config. Add gem 'ey_config' to your Gemfile to access service configurations through EY::Config."
64
71
  end
65
72
  end
@@ -87,7 +94,7 @@ To fix this problem, commit your Gemfile.lock to your repository and redeploy.
87
94
  # the [,)] is to stop us from looking for e.g. 0.9.2, seeing
88
95
  # 0.9.22, and mistakenly thinking 0.9.2 is there
89
96
  has_gem_cmd = %{gem list bundler | grep "bundler " | egrep -q "#{egrep_escaped_version}[,)]"}
90
- install_cmd = %{gem install bundler -q --no-rdoc --no-ri -v "#{bundler_version}"}
97
+ install_cmd = %{gem install bundler -q -v "#{bundler_version}"}
91
98
  sudo "#{clean_environment} && #{has_gem_cmd} || #{install_cmd}"
92
99
  end
93
100
 
@@ -103,6 +110,11 @@ To fix this problem, commit your Gemfile.lock to your repository and redeploy.
103
110
  "--path", "#{paths.bundled_gems}",
104
111
  "--binstubs", "#{paths.binstubs}",
105
112
  ]
113
+
114
+ # Install gems in parallel (Bundler.1.5+ only)
115
+ # @see EYPP-58
116
+ options += ["--jobs", self.class.jobs_number] if check_version(bundler_version, ">= 1.5")
117
+
106
118
  options += ["--deployment"] if lockfile
107
119
  options += config.extra_bundle_install_options
108
120
  options
@@ -120,10 +132,9 @@ To fix this problem, commit your Gemfile.lock to your repository and redeploy.
120
132
  shell.substatus "Clean bundle forced (--clean)"
121
133
  run clean_bundle
122
134
  else
123
- check_ruby = "#{config.ruby_version_command} | diff - #{paths.ruby_version} >/dev/null 2>&1"
124
135
  check_system = "#{config.system_version_command} | diff - #{paths.system_version} >/dev/null 2>&1"
125
136
  shell.substatus "Checking for system version changes"
126
- run "#{check_ruby} && #{check_system} || #{clean_bundle}"
137
+ run "#{check_system} || #{clean_bundle}"
127
138
  end
128
139
  end
129
140
 
@@ -140,9 +151,24 @@ To fix this problem, commit your Gemfile.lock to your repository and redeploy.
140
151
  end
141
152
  end
142
153
 
154
+ ##
155
+ # Checks that given version is satisfies give requirement.
156
+ #
157
+ # @param [String] version
158
+ # @param [String] requirement
159
+ # @return [true, false]
160
+ #
161
+ def check_version(version, requirement)
162
+ Gem::Requirement.new( requirement ).satisfied_by? Gem::Version.new( version )
163
+ end
164
+
143
165
  class Lockfile
144
166
  attr_reader :bundler_version
145
167
 
168
+ METADATA = 'METADATA'.freeze
169
+ BUNDLED = 'BUNDLED WITH'.freeze
170
+ DEPENDENCIES = 'DEPENDENCIES'.freeze
171
+
146
172
  def initialize(lockfile_contents, default = EY::Serverside::DependencyManager::Bundler.default_version)
147
173
  @contents = lockfile_contents
148
174
  @default = default
@@ -180,9 +206,8 @@ To fix this problem, commit your Gemfile.lock to your repository and redeploy.
180
206
  end
181
207
 
182
208
  def parse
183
- parse_from_metadata ||
184
- parse_from_dependencies ||
185
- raise("Malformed or pre bundler-1.0.0 Gemfile.lock: #{@contents[0,50]}...")
209
+ @bundler_version = parse_from_metadata || parse_from_bundled_with || parse_from_dependencies
210
+ raise("Malformed or pre bundler-1.0.0 Gemfile.lock: #{@contents[0,50]}...") unless @bundler_version
186
211
  end
187
212
 
188
213
  def slice_section(header)
@@ -195,25 +220,28 @@ To fix this problem, commit your Gemfile.lock to your repository and redeploy.
195
220
  end
196
221
 
197
222
  def parse_from_metadata
198
- section = slice_section('METADATA')
199
-
200
- if section.empty?
201
- return nil
202
- end
223
+ section = slice_section(METADATA)
224
+ return if section.empty?
203
225
 
204
226
  result = section.scan(/^\s*version:\s*(.*)$/).first
205
- @bundler_version = result ? result.first : @default
227
+ result ? result.first : @default
228
+ end
229
+
230
+ def parse_from_bundled_with
231
+ section = slice_section(BUNDLED)
232
+ return if section.empty?
233
+
234
+ result = section.scan(/^#{BUNDLED}\s*(.*)$/).first
235
+ result ? result.first : @default
206
236
  end
207
237
 
208
238
  def dependencies_section
209
- @dependencies_section ||= slice_section('DEPENDENCIES')
239
+ @dependencies_section ||= slice_section(DEPENDENCIES)
210
240
  end
211
241
 
212
242
  def parse_from_dependencies
213
243
  section = dependencies_section
214
- if section.empty?
215
- return nil
216
- end
244
+ return if section.empty?
217
245
 
218
246
  result = scan_gem('bundler', section)
219
247
  bundler_version = result ? result.last : nil
@@ -10,7 +10,18 @@ module EY
10
10
 
11
11
  def install
12
12
  shell.status "Installing npm packages (package.json detected)"
13
- run %{cd #{paths.active_release} && export GIT_SSH="#{ENV['GIT_SSH']}" && npm install}
13
+ run "mkdir -p #{paths.shared_node_modules} && ln -nfs #{paths.shared_node_modules} #{paths.active_node_modules}"
14
+ run %{cd #{paths.active_release} && export GIT_SSH="#{ENV['GIT_SSH']}" && npm install #{npm_install_options.join(" ")}}
15
+ end
16
+
17
+ def npm_install_options
18
+ options = []
19
+ options += ['--production'] if npm_production?
20
+ options
21
+ end
22
+
23
+ def npm_production?
24
+ ENV['NODE_ENV'] == 'production'
14
25
  end
15
26
  end
16
27
  end
@@ -5,6 +5,7 @@ require 'engineyard-serverside/rails_assets'
5
5
  require 'engineyard-serverside/maintenance'
6
6
  require 'engineyard-serverside/dependency_manager'
7
7
  require 'engineyard-serverside/dependency_manager/legacy_helpers'
8
+ require 'engineyard-serverside/callbacks'
8
9
 
9
10
  module EY
10
11
  module Serverside
@@ -450,27 +451,9 @@ defaults:
450
451
  end
451
452
 
452
453
  def callback(what)
454
+ load_callbacks
453
455
  @callbacks_reached ||= true
454
-
455
- if paths.deploy_hook(what).exist?
456
- shell.status "Running deploy hook: deploy/#{what}.rb"
457
- run Escape.shell_command(base_callback_command_for(what)) do |server, cmd|
458
- per_instance_args = []
459
- per_instance_args << '--current-roles' << server.roles.to_a.join(' ')
460
- per_instance_args << '--current-name' << server.name.to_s if server.name
461
- per_instance_args << '--config' << config.to_json
462
- cmd << " " << Escape.shell_command(per_instance_args)
463
- end
464
- elsif paths.executable_deploy_hook(what).executable?
465
- shell.status "Running deploy hook: deploy/#{what}"
466
- run [About.hook_executor, what.to_s].join(' ') do |server, cmd|
467
- cmd = hook_env_vars(server).reject{|k,v| v.nil?}.map{|k,v|
468
- "#{k}=#{Escape.shell_command([v])}"
469
- }.join(' ') + ' ' + config.framework_envs + ' ' + cmd
470
- end
471
- elsif paths.executable_deploy_hook(what).exist?
472
- shell.warning "Skipping possible deploy hook deploy/#{what} because it is not executable."
473
- end
456
+ @callbacks_collection.distribute(self, what)
474
457
  end
475
458
 
476
459
  def source
@@ -480,31 +463,6 @@ defaults:
480
463
 
481
464
  protected
482
465
 
483
- def base_callback_command_for(what)
484
- cmd = [About.binary, 'hook', what.to_s]
485
- cmd << '--app' << config.app
486
- cmd << '--environment-name' << config.environment_name
487
- cmd << '--account-name' << config.account_name
488
- cmd << '--release-path' << paths.active_release.to_s
489
- cmd << '--framework-env' << config.framework_env.to_s
490
- cmd << '--verbose' if config.verbose
491
- cmd
492
- end
493
-
494
- def hook_env_vars(server)
495
- {
496
- 'EY_DEPLOY_ACCOUNT_NAME' => config.account_name,
497
- 'EY_DEPLOY_APP' => config.app,
498
- 'EY_DEPLOY_CONFIG' => config.to_json,
499
- 'EY_DEPLOY_CURRENT_ROLES' => server.roles.to_a.join(' '),
500
- 'EY_DEPLOY_CURRENT_NAME' => server.name ? server.name.to_s : nil,
501
- 'EY_DEPLOY_ENVIRONMENT_NAME' => config.environment_name,
502
- 'EY_DEPLOY_FRAMEWORK_ENV' => config.framework_env.to_s,
503
- 'EY_DEPLOY_RELEASE_PATH' => paths.active_release.to_s,
504
- 'EY_DEPLOY_VERBOSE' => (config.verbose ? '1' : '0'),
505
- }
506
- end
507
-
508
466
  # FIXME: Legacy method, warn and remove.
509
467
  def serverside_bin
510
468
  About.binary
@@ -554,6 +512,10 @@ defaults:
554
512
  def compile_assets
555
513
  RailsAssets.detect_and_compile(config, shell, self)
556
514
  end
515
+
516
+ def load_callbacks
517
+ @callbacks_collection ||= Callbacks.load(paths)
518
+ end
557
519
  end # DeployBase
558
520
 
559
521
  class Deploy < DeployBase
@@ -70,15 +70,7 @@ module EY
70
70
  run "mkdir -p #{maintenance_page_dirname}"
71
71
  public_system_symlink_warning
72
72
  @up = true
73
- maintenance_page_html = File.read(source_path)
74
- if source_path == EY::Serverside::Paths::DEFAULT_MAINTENANCE_PAGE
75
- #run fans out to all app servers but the serverside version is only isntalled on the server being used to deploy
76
- #so if the serveside version being used isn't installed on a given app server the cp command would fail, but this echo will still work
77
- run "echo '#{maintenance_page_html}' > #{enabled_maintenance_page_pathname}"
78
- else
79
- #since other possible paths are in your app, cp should work
80
- run "cp #{source_path} #{enabled_maintenance_page_pathname}"
81
- end
73
+ run "cp #{source_path} #{enabled_maintenance_page_pathname}"
82
74
  end
83
75
 
84
76
  def disable
@@ -68,6 +68,8 @@ module EY
68
68
  def_path :shared_log, [:shared, 'log']
69
69
  def_path :shared_tmp, [:shared, 'tmp']
70
70
  def_path :shared_config, [:shared, 'config']
71
+ def_path :shared_hooks, [:shared, 'hooks']
72
+ def_path :shared_node_modules, [:shared, 'node_modules']
71
73
  def_path :shared_system, [:shared, 'system']
72
74
  def_path :default_repository_cache, [:shared, 'cached-copy']
73
75
  def_path :enabled_maintenance_page, [:shared_system, 'maintenance.html']
@@ -90,6 +92,7 @@ module EY
90
92
  def_path :composer_lock, [:active_release, 'composer.lock']
91
93
  def_path :active_release_config, [:active_release, 'config']
92
94
  def_path :active_log, [:active_release, 'log']
95
+ def_path :active_node_modules, [:active_release, 'node_modules']
93
96
  def_path :active_tmp, [:active_release, 'tmp']
94
97
 
95
98
  def initialize(opts)
@@ -128,10 +131,18 @@ module EY
128
131
  path(:deploy_hooks, "#{hook_name}.rb")
129
132
  end
130
133
 
134
+ def service_hook(service_name, hook_name)
135
+ path(:shared_hooks, service_name, "#{hook_name}.rb")
136
+ end
137
+
131
138
  def executable_deploy_hook(hook_name)
132
139
  path(:deploy_hooks, "#{hook_name}")
133
140
  end
134
141
 
142
+ def executable_service_hook(service_name, hook_name)
143
+ path(:shared_hooks, service_name, "#{hook_name}")
144
+ end
145
+
135
146
  def repository_cache
136
147
  @repository_cache ||= default_repository_cache
137
148
  end
@@ -0,0 +1,59 @@
1
+ require 'forwardable'
2
+ require 'engineyard-serverside/about'
3
+
4
+ module EY
5
+ module Serverside
6
+
7
+ # Propagator ensures that a proper version of the engineyard-serverside
8
+ # gem is installed on a given set of servers
9
+ class Propagator
10
+ def self.propagate(servers, shell)
11
+ new(servers, shell).propagate
12
+ end
13
+
14
+ extend Forwardable
15
+
16
+ def_delegators About, :gem_binary, :gem_file, :gem_filename, :gem_name,
17
+ :name_with_version, :version
18
+
19
+ attr_reader :servers, :shell
20
+
21
+ def initialize(servers, shell)
22
+ @servers = servers
23
+ @shell = shell
24
+ end
25
+
26
+ def propagate
27
+ shell.status "Verifying and propagating #{name_with_version} to all servers."
28
+
29
+ servers.remote.run_for_each! do |server|
30
+ propagation_command_for(server)
31
+ end
32
+ end
33
+
34
+ private
35
+ def propagation_command_for(server)
36
+ check = server.command_on_server('sh -l -c', check_command)
37
+ scp = server.scp_command(About.gem_file, remote_gem_file)
38
+ install = server.command_on_server('sudo sh -l -c', install_command)
39
+
40
+ "(#{check}) || ((#{scp}) && (#{install}))"
41
+ end
42
+
43
+ def remote_gem_file
44
+ @remote_gem_file ||= File.join(Dir.tmpdir, gem_filename)
45
+ end
46
+
47
+ def check_command
48
+ # the [,)] is to stop us from looking for e.g. 0.5.1, seeing
49
+ # 0.5.11, and mistakenly thinking 0.5.1 is there
50
+
51
+ %{#{gem_binary} list #{gem_name} | grep "#{gem_name}" | egrep -q "#{version.gsub(/\./, '\.')}[,)]"}
52
+ end
53
+
54
+ def install_command
55
+ "#{gem_binary} install --no-rdoc --no-ri '#{remote_gem_file}'"
56
+ end
57
+ end
58
+ end
59
+ end