geordi 3.0.0 → 3.2.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. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/CHANGELOG.md +36 -2
  4. data/Gemfile.lock +1 -1
  5. data/README.md +46 -3
  6. data/lib/geordi/COMMAND_TEMPLATE +2 -3
  7. data/lib/geordi/chromedriver_updater.rb +7 -8
  8. data/lib/geordi/cli.rb +0 -1
  9. data/lib/geordi/commands/_setup_vnc.rb +22 -22
  10. data/lib/geordi/commands/bundle_install.rb +1 -1
  11. data/lib/geordi/commands/capistrano.rb +4 -4
  12. data/lib/geordi/commands/clean.rb +6 -5
  13. data/lib/geordi/commands/commit.rb +1 -1
  14. data/lib/geordi/commands/console.rb +11 -3
  15. data/lib/geordi/commands/create_database_yml.rb +2 -2
  16. data/lib/geordi/commands/create_databases.rb +1 -1
  17. data/lib/geordi/commands/cucumber.rb +16 -9
  18. data/lib/geordi/commands/delete_dumps.rb +5 -5
  19. data/lib/geordi/commands/deploy.rb +19 -19
  20. data/lib/geordi/commands/drop_databases.rb +3 -3
  21. data/lib/geordi/commands/dump.rb +8 -9
  22. data/lib/geordi/commands/firefox.rb +2 -2
  23. data/lib/geordi/commands/migrate.rb +3 -3
  24. data/lib/geordi/commands/png_optimize.rb +5 -5
  25. data/lib/geordi/commands/rake.rb +1 -1
  26. data/lib/geordi/commands/remove_executable_flags.rb +2 -2
  27. data/lib/geordi/commands/rspec.rb +5 -5
  28. data/lib/geordi/commands/security_update.rb +76 -19
  29. data/lib/geordi/commands/server.rb +2 -2
  30. data/lib/geordi/commands/setup.rb +3 -3
  31. data/lib/geordi/commands/shell.rb +7 -3
  32. data/lib/geordi/commands/tests.rb +1 -1
  33. data/lib/geordi/commands/unit.rb +2 -2
  34. data/lib/geordi/commands/update.rb +2 -2
  35. data/lib/geordi/commands/with_rake.rb +2 -2
  36. data/lib/geordi/commands/yarn_install.rb +1 -1
  37. data/lib/geordi/cucumber.rb +18 -17
  38. data/lib/geordi/db_cleaner.rb +21 -22
  39. data/lib/geordi/dump_loader.rb +5 -4
  40. data/lib/geordi/firefox_for_selenium.rb +17 -19
  41. data/lib/geordi/gitpt.rb +10 -53
  42. data/lib/geordi/interaction.rb +2 -6
  43. data/lib/geordi/remote.rb +17 -6
  44. data/lib/geordi/settings.rb +155 -0
  45. data/lib/geordi/util.rb +14 -9
  46. data/lib/geordi/version.rb +1 -1
  47. metadata +4 -3
@@ -4,7 +4,7 @@ def create_database_yml
4
4
  sample_yml = 'config/database.sample.yml'
5
5
 
6
6
  if File.exist?(sample_yml) && !File.exist?(real_yml)
7
- announce 'Creating ' + real_yml
7
+ Interaction.announce 'Creating ' + real_yml
8
8
 
9
9
  sample = File.read(sample_yml)
10
10
  adapter = sample.match(/adapter: (\w+)\n/).captures.first
@@ -15,6 +15,6 @@ def create_database_yml
15
15
  real = sample.gsub(/password:.*$/, "password: #{db_password}")
16
16
  File.open(real_yml, 'w') { |f| f.write(real) }
17
17
 
18
- note "Created #{real_yml}."
18
+ Interaction.note "Created #{real_yml}."
19
19
  end
20
20
  end
@@ -3,7 +3,7 @@ def create_databases
3
3
  invoke_cmd 'create_database_yml'
4
4
  invoke_cmd 'bundle_install'
5
5
 
6
- announce 'Creating databases'
6
+ Interaction.announce 'Creating databases'
7
7
 
8
8
  if File.exist?('config/database.yml')
9
9
  command = 'bundle exec rake db:create:all'
@@ -15,6 +15,13 @@ or `-d`.
15
15
 
16
16
  - *Options:* Any unknown option will be passed through to Cucumber,
17
17
  e.g. `--format pretty`.
18
+
19
+ - *VNC:* By default, test browsers will run in a VNC session. When using a
20
+ headless test browser anyway, you can disable VNC by putting the following
21
+ config into `.geordi.yml` in the project root:
22
+
23
+ use_vnc: false
24
+
18
25
  LONGDESC
19
26
 
20
27
  option :modified, aliases: '-m', type: :boolean,
@@ -34,7 +41,7 @@ def cucumber(*args)
34
41
  if options.modified?
35
42
  modified_features = `git status --short`.split($INPUT_RECORD_SEPARATOR).map do |line|
36
43
  indicators = line.slice!(0..2) # Remove leading indicators
37
- line if line.include?('.feature') && indicators.exclude?('D')
44
+ line if line.include?('.feature') && !indicators.include?('D')
38
45
  end.compact
39
46
  args = modified_features
40
47
  end
@@ -56,7 +63,7 @@ def cucumber(*args)
56
63
 
57
64
  # Serial run of @solo scenarios ############################################
58
65
  if files.any? { |f| f.include? ':' }
59
- note '@solo run skipped when called with line numbers' if options.verbose
66
+ Interaction.note '@solo run skipped when called with line numbers' if options.verbose
60
67
  else
61
68
  solo_files = if files.empty?
62
69
  'features' # Proper grepping
@@ -70,14 +77,14 @@ def cucumber(*args)
70
77
  solo_cmd_opts = cmd_opts.dup
71
78
  solo_cmd_opts << '--tags' << '@solo'
72
79
 
73
- announce 'Running @solo features'
80
+ Interaction.announce 'Running @solo features'
74
81
  solo_success = Geordi::Cucumber.new.run files, solo_cmd_opts, verbose: options.verbose, parallel: false
75
- solo_success || raise('Features failed.')
82
+ solo_success || Interaction.fail('Features failed.')
76
83
  end
77
84
  end
78
85
 
79
86
  # Parallel run of all given features + reruns ##############################
80
- announce 'Running features'
87
+ Interaction.announce 'Running features'
81
88
  normal_run_successful = Geordi::Cucumber.new.run(files, cmd_opts, verbose: options.verbose)
82
89
 
83
90
  unless normal_run_successful
@@ -85,16 +92,16 @@ def cucumber(*args)
85
92
 
86
93
  # Reruns
87
94
  (options.rerun + 1).times do |i|
88
- raise 'Features failed.' if i == options.rerun # All reruns done?
95
+ Interaction.fail 'Features failed.' if i == options.rerun # All reruns done?
89
96
 
90
- announce "Rerun ##{i + 1} of #{options.rerun}"
97
+ Interaction.announce "Rerun ##{i + 1} of #{options.rerun}"
91
98
  break if Geordi::Cucumber.new.run([], cmd_opts, verbose: options.verbose, parallel: false)
92
99
  end
93
100
  end
94
101
 
95
- success 'Features green.'
102
+ Interaction.success 'Features green.'
96
103
 
97
104
  else
98
- note 'Cucumber not employed.'
105
+ Interaction.note 'Cucumber not employed.'
99
106
  end
100
107
  end
@@ -20,24 +20,24 @@ def delete_dumps(dump_directory = nil)
20
20
  else
21
21
  [dump_directory]
22
22
  end
23
- announce 'Looking for *.dump in ' << dump_directories.join(',')
23
+ Interaction.announce 'Looking for *.dump in ' << dump_directories.join(',')
24
24
  dump_directories.each do |d|
25
25
  d_2 = File.expand_path(d)
26
26
  unless File.directory? File.realdirpath(d_2)
27
- warn "Directory #{d_2} does not exist"
27
+ Interaction.warn "Directory #{d_2} does not exist"
28
28
  next
29
29
  end
30
30
  deletable_dumps.concat(Dir.glob("#{d_2}/**/*.dump"))
31
31
  end
32
32
  if deletable_dumps.empty?
33
- success 'No dumps to delete' if deletable_dumps.empty?
33
+ Interaction.success 'No dumps to delete' if deletable_dumps.empty?
34
34
  exit 0
35
35
  end
36
36
  deletable_dumps.uniq!.sort!
37
- note 'The following dumps can be deleted:'
37
+ Interaction.note 'The following dumps can be deleted:'
38
38
  puts
39
39
  puts deletable_dumps
40
- prompt('Delete those dumps', 'n', /y|yes/) || raise('Cancelled.')
40
+ Interaction.prompt('Delete those dumps', 'n', /y|yes/) || raise('Cancelled.')
41
41
  deletable_dumps.each do |dump|
42
42
  File.delete dump unless File.directory? dump
43
43
  end
@@ -38,17 +38,17 @@ option :current_branch, aliases: '-c', type: :boolean,
38
38
  def deploy(target_stage = nil)
39
39
  # Set/Infer default values
40
40
  branch_stage_map = { 'master' => 'staging', 'production' => 'production' }
41
- if target_stage && (Util.deploy_targets.exclude? target_stage)
41
+ if target_stage && !Util.deploy_targets.include?(target_stage)
42
42
  # Target stage autocompletion from available stages
43
43
  target_stage = Util.deploy_targets.find { |t| t.start_with? target_stage }
44
- target_stage || warn('Given deployment stage not found')
44
+ target_stage || Interaction.warn('Given deployment stage not found')
45
45
  end
46
46
 
47
47
  # Ask for required information
48
- target_stage ||= prompt 'Deployment stage:', branch_stage_map.fetch(Util.current_branch, 'staging')
48
+ target_stage ||= Interaction.prompt 'Deployment stage:', branch_stage_map.fetch(Util.current_branch, 'staging')
49
49
  if options.current_branch
50
50
  stage_file = "config/deploy/#{target_stage}.rb"
51
- Util.file_containing?(stage_file, 'DEPLOY_BRANCH') || raise(<<-ERROR)
51
+ Util.file_containing?(stage_file, 'DEPLOY_BRANCH') || Interaction.fail(<<-ERROR)
52
52
  To deploy from the current branch, configure #{stage_file} to respect the
53
53
  environment variable DEPLOY_BRANCH. Example:
54
54
 
@@ -57,38 +57,38 @@ set :branch, ENV['DEPLOY_BRANCH'] || 'master'
57
57
 
58
58
  source_branch = target_branch = Util.current_branch
59
59
  else
60
- source_branch = prompt 'Source branch:', Util.current_branch
61
- target_branch = prompt 'Deploy branch:', branch_stage_map.invert.fetch(target_stage, 'master')
60
+ source_branch = Interaction.prompt 'Source branch:', Util.current_branch
61
+ target_branch = Interaction.prompt 'Deploy branch:', branch_stage_map.invert.fetch(target_stage, 'master')
62
62
  end
63
63
 
64
64
  merge_needed = (source_branch != target_branch)
65
65
  push_needed = merge_needed || `git cherry -v | wc -l`.strip.to_i > 0
66
66
  push_needed = false if Util.testing? # Hard to test
67
67
 
68
- announce "Checking whether your #{source_branch} branch is ready" ############
68
+ Interaction.announce "Checking whether your #{source_branch} branch is ready" ############
69
69
  Util.system! "git checkout #{source_branch}"
70
70
  if (`git status -s | wc -l`.strip != '0') && !Util.testing?
71
- warn "Your #{source_branch} branch holds uncommitted changes."
72
- prompt('Continue anyway?', 'n', /y|yes/) || raise('Cancelled.')
71
+ Interaction.warn "Your #{source_branch} branch holds uncommitted changes."
72
+ Interaction.prompt('Continue anyway?', 'n', /y|yes/) || raise('Cancelled.')
73
73
  else
74
- note 'All good.'
74
+ Interaction.note 'All good.'
75
75
  end
76
76
 
77
77
  if merge_needed
78
- announce "Checking what's in your #{target_branch} branch right now" #######
78
+ Interaction.announce "Checking what's in your #{target_branch} branch right now" #######
79
79
  Util.system! "git checkout #{target_branch} && git pull"
80
80
  end
81
81
 
82
- announce 'You are about to:' #################################################
83
- note "Merge branch #{source_branch} into #{target_branch}" if merge_needed
82
+ Interaction.announce 'You are about to:' #################################################
83
+ Interaction.note "Merge branch #{source_branch} into #{target_branch}" if merge_needed
84
84
  if push_needed
85
- note 'Push these commits:' if push_needed
85
+ Interaction.note 'Push these commits:' if push_needed
86
86
  Util.system! "git --no-pager log origin/#{target_branch}..#{source_branch} --oneline"
87
87
  end
88
- note "Deploy to #{target_stage}"
89
- note "From current branch #{source_branch}" if options.current_branch
88
+ Interaction.note "Deploy to #{target_stage}"
89
+ Interaction.note "From current branch #{source_branch}" if options.current_branch
90
90
 
91
- if prompt('Go ahead with the deployment?', 'n', /y|yes/)
91
+ if Interaction.prompt('Go ahead with the deployment?', 'n', /y|yes/)
92
92
  puts
93
93
  git_call = []
94
94
  git_call << "git merge #{source_branch}" if merge_needed
@@ -107,9 +107,9 @@ set :branch, ENV['DEPLOY_BRANCH'] || 'master'
107
107
 
108
108
  Util.system! capistrano_call, show_cmd: true
109
109
 
110
- success 'Deployment complete.'
110
+ Interaction.success 'Deployment complete.'
111
111
  else
112
112
  Util.system! "git checkout #{source_branch}"
113
- raise 'Deployment cancelled.'
113
+ Interaction.fail 'Deployment cancelled.'
114
114
  end
115
115
  end
@@ -28,7 +28,7 @@ option :mysql, banner: 'PORT_OR_SOCKET',
28
28
 
29
29
  def drop_databases
30
30
  require 'geordi/db_cleaner'
31
- raise '-P and -M are mutually exclusive' if options.postgres_only && options.mysql_only
31
+ Interaction.fail '-P and -M are mutually exclusive' if options.postgres_only && options.mysql_only
32
32
  mysql_flags = nil
33
33
  postgres_flags = nil
34
34
 
@@ -38,7 +38,7 @@ def drop_databases
38
38
  mysql_flags = "--port=#{mysql_port} --protocol=TCP"
39
39
  rescue AttributeError
40
40
  unless File.exist? options.mysql
41
- raise "Path #{options.mysql} is not a valid MySQL socket"
41
+ Interaction.fail "Path #{options.mysql} is not a valid MySQL socket"
42
42
  end
43
43
  mysql_flags = "--socket=#{options.mysql}"
44
44
  end
@@ -54,5 +54,5 @@ def drop_databases
54
54
  cleaner.clean_mysql unless options.postgres_only
55
55
  cleaner.clean_postgres unless options.mysql_only
56
56
 
57
- success 'Done.'
57
+ Interaction.success 'Done.'
58
58
  end
@@ -21,7 +21,6 @@ dump into the development database after downloading it.
21
21
  DESC
22
22
 
23
23
  option :load, aliases: ['-l'], type: :string, desc: 'Load a dump'
24
- option :select_server, default: false, type: :boolean, aliases: '-s'
25
24
 
26
25
  def dump(target = nil, *_args)
27
26
  require 'geordi/dump_loader'
@@ -30,33 +29,33 @@ def dump(target = nil, *_args)
30
29
  if target.nil?
31
30
  if options.load
32
31
  # validate load option
33
- raise 'Missing a dump file.' if options.load == 'load'
32
+ Interaction.fail 'Missing a dump file.' if options.load == 'load'
34
33
  File.exist?(options.load) || raise('Could not find the given dump file: ' + options.load)
35
34
 
36
35
  loader = DumpLoader.new(options.load)
37
36
 
38
- announce "Sourcing dump into the #{loader.config['database']} db"
37
+ Interaction.announce "Sourcing dump into the #{loader.config['database']} db"
39
38
  loader.load
40
39
 
41
- success "Your #{loader.config['database']} database has now the data of #{options.load}."
40
+ Interaction.success "Your #{loader.config['database']} database has now the data of #{options.load}."
42
41
 
43
42
  else
44
- announce 'Dumping the development database'
43
+ Interaction.announce 'Dumping the development database'
45
44
  Util.system! 'dumple development'
46
- success 'Successfully dumped the development database.'
45
+ Interaction.success 'Successfully dumped the development database.'
47
46
  end
48
47
 
49
48
  else
50
- announce 'Dumping the database of ' + target
49
+ Interaction.announce 'Dumping the database of ' + target
51
50
  dump_path = Geordi::Remote.new(target).dump(options)
52
51
 
53
52
  if options.load
54
53
  loader = DumpLoader.new(dump_path)
55
54
 
56
- announce "Sourcing dump into the #{loader.config['database']} db"
55
+ Interaction.announce "Sourcing dump into the #{loader.config['database']} db"
57
56
  loader.load
58
57
 
59
- success "Your #{loader.config['database']} database has now the data of #{target}."
58
+ Interaction.success "Your #{loader.config['database']} database has now the data of #{target}."
60
59
  end
61
60
  end
62
61
  end
@@ -15,7 +15,7 @@ option :setup, banner: 'FIREFOX_VERSION',
15
15
 
16
16
  def firefox(*command)
17
17
  if options.setup
18
- raise 'Firefox version required (e.g. --setup 24.0)' if options.setup == 'setup'
18
+ Interaction.fail 'Firefox version required (e.g. --setup 24.0)' if options.setup == 'setup'
19
19
 
20
20
  require 'geordi/firefox_for_selenium'
21
21
  Geordi::FirefoxForSelenium.install(options.setup)
@@ -27,7 +27,7 @@ def firefox(*command)
27
27
  FirefoxForSelenium.setup_firefox
28
28
 
29
29
  puts
30
- note_cmd command.join(' ')
30
+ Interaction.note_cmd command.join(' ')
31
31
  system *command # Util.system! would reset the Firefox PATH
32
32
  end
33
33
  end
@@ -10,11 +10,11 @@ LONGDESC
10
10
  def migrate
11
11
  invoke_cmd 'bundle_install'
12
12
  invoke_cmd 'yarn_install'
13
- announce 'Migrating'
13
+ Interaction.announce 'Migrating'
14
14
 
15
15
  if File.directory?('db/migrate')
16
16
  if Util.file_containing?('Gemfile', /parallel_tests/)
17
- note 'Development and parallel test databases'
17
+ Interaction.note 'Development and parallel test databases'
18
18
  puts
19
19
 
20
20
  Util.system! 'bundle exec rake db:migrate parallel:prepare'
@@ -22,6 +22,6 @@ def migrate
22
22
  invoke_cmd 'rake', 'db:migrate'
23
23
  end
24
24
  else
25
- note 'No migrations directory found.'
25
+ Interaction.note 'No migrations directory found.'
26
26
  end
27
27
  end
@@ -10,10 +10,10 @@ LONGDESC
10
10
  def png_optimize(path)
11
11
  require 'fileutils'
12
12
 
13
- announce 'Optimizing .png files'
13
+ Interaction.announce 'Optimizing .png files'
14
14
 
15
15
  if `which pngcrush`.strip.empty?
16
- raise 'Please install pngcrush first (sudo apt-get install pngcrush)'
16
+ Interaction.fail 'Please install pngcrush first (sudo apt-get install pngcrush)'
17
17
  end
18
18
 
19
19
  po = PngOptimizer.new
@@ -22,10 +22,10 @@ def png_optimize(path)
22
22
  elsif File.file?(path)
23
23
  po.optimize_inplace(path)
24
24
  else
25
- raise 'Neither directory nor file: ' + path
25
+ Interaction.fail 'Neither directory nor file: ' + path
26
26
  end
27
27
 
28
- success 'PNG optimization completed.'
28
+ Interaction.success 'PNG optimization completed.'
29
29
  end
30
30
 
31
31
  class PngOptimizer
@@ -64,7 +64,7 @@ class PngOptimizer
64
64
  FileUtils.rm(input_file)
65
65
  FileUtils.mv(temp_file.to_s, input_file.to_s)
66
66
  else
67
- raise 'Error:' + $CHILD_STATUS
67
+ Interaction.fail 'Error:' + $?
68
68
  end
69
69
  end
70
70
 
@@ -13,7 +13,7 @@ def rake(*args)
13
13
  %w[development test cucumber].each do |env| # update long_desc when changing this
14
14
  if File.exist? "config/environments/#{env}.rb"
15
15
  call = %w[bundle exec rake] + args + ["RAILS_ENV=#{env}"]
16
- note_cmd call.join(' ')
16
+ Interaction.note_cmd call.join(' ')
17
17
 
18
18
  Util.system! *call
19
19
  end
@@ -1,13 +1,13 @@
1
1
  desc 'remove-executable-flags', 'Remove executable-flags from files that should not be executable'
2
2
  def remove_executable_flags
3
- announce 'Removing executable-flags'
3
+ Interaction.announce 'Removing executable-flags'
4
4
 
5
5
  patterns = %w[
6
6
  *.rb *.html *.erb *.haml *.yml *.css *.sass *.rake *.png *.jpg
7
7
  *.gif *.pdf *.txt *.rdoc *.feature Rakefile VERSION README Capfile
8
8
  ]
9
9
  patterns.each do |pattern|
10
- note pattern
10
+ Interaction.note pattern
11
11
  `find . -name "#{pattern}" -exec chmod -x {} ';'`
12
12
  end
13
13
  puts 'Done.'
@@ -11,19 +11,19 @@ def rspec(*files)
11
11
  invoke_cmd 'bundle_install'
12
12
  invoke_cmd 'yarn_install'
13
13
 
14
- announce 'Running specs'
14
+ Interaction.announce 'Running specs'
15
15
 
16
16
  if Util.file_containing?('Gemfile', /parallel_tests/) && files.empty?
17
- note 'All specs at once (using parallel_tests)'
17
+ Interaction.note 'All specs at once (using parallel_tests)'
18
18
  Util.system! 'bundle exec rake parallel:spec', fail_message: 'Specs failed.'
19
19
 
20
20
  else
21
21
  # tell which specs will be run
22
22
  if files.empty?
23
23
  files << 'spec/'
24
- note 'All specs in spec/'
24
+ Interaction.note 'All specs in spec/'
25
25
  else
26
- note 'Only: ' + files.join(', ')
26
+ Interaction.note 'Only: ' + files.join(', ')
27
27
  end
28
28
 
29
29
  command = ['bundle exec']
@@ -41,6 +41,6 @@ def rspec(*files)
41
41
  Util.system! command.join(' '), fail_message: 'Specs failed.'
42
42
  end
43
43
  else
44
- note 'RSpec not employed.'
44
+ Interaction.note 'RSpec not employed.'
45
45
  end
46
46
  end
@@ -2,39 +2,63 @@ desc 'security-update [STEP]', 'Support for performing security updates'
2
2
  long_desc <<-LONGDESC
3
3
  Preparation for security update: `geordi security-update`
4
4
 
5
+ Checks out production and pulls.
6
+
5
7
  After performing the update: `geordi security-update finish`
6
8
 
7
9
  Switches branches, pulls, pushes and deploys as required by our workflow. Tells
8
- what it will do before it does it.
10
+ what it will do before it does it. In detail:
11
+
12
+ 1. Asks user, if tests are green
13
+
14
+ 2. Pushes production
15
+
16
+ 3. Checks out master and pulls
17
+
18
+ 4. Merges production and pushes in master
19
+
20
+ 5. Deploys staging first, if there is a staging environment
21
+
22
+ 6. Asks user, if deployment log is okay and application is still running on staging
23
+
24
+ 7. Deploys other stages
25
+
26
+ 8. Asks user, if deployment log is okay and application is still running on all other stages
27
+
28
+ 9. Informs user about the next steps
9
29
  LONGDESC
10
30
 
11
31
  def security_update(step = 'prepare')
12
32
  case step
13
33
  when 'prepare'
14
- announce 'Preparing for security update'
15
- warn 'Please read https://makandracards.com/makandra/1587 before applying security updates!'
16
- note 'About to checkout production and pull'
17
- prompt('Continue?', 'y', /y|yes/) || raise('Cancelled.')
34
+ Interaction.announce 'Preparing for security update'
35
+ Interaction.warn 'Please read https://makandracards.com/makandra/1587 before applying security updates!'
36
+ Interaction.note 'About to checkout production and pull.'
37
+ Interaction.prompt('Continue?', 'y', /y|yes/) || Interaction.fail('Cancelled.')
18
38
 
19
39
  Util.system! 'git checkout production', show_cmd: true
20
40
  Util.system! 'git pull', show_cmd: true
21
41
 
22
- success 'Successfully prepared for security update'
42
+ Interaction.success 'Successfully prepared for security update'
23
43
  puts
24
- note 'Please apply the security update now and commit your changes.'
25
- note 'When you are done, run `geordi security-update finish`.'
44
+ Interaction.note 'Please apply the security update now and commit your changes.'
45
+ Interaction.note 'When you are done, run `geordi security-update finish`.'
26
46
 
27
47
 
28
48
  when 'f', 'finish'
29
49
  # ensure everything is committed
30
- `git status --porcelain`.empty? || raise('Please commit your changes before finishing the update.')
50
+ if Util.testing?
51
+ puts 'Util.system! git status --porcelain'
52
+ else
53
+ `git status --porcelain`.empty? || Interaction.fail('Please commit your changes before finishing the update.')
54
+ end
31
55
 
32
- announce 'Finishing security update'
33
- note 'Working directory clean.'
34
- prompt('Have you successfully run all tests?', 'n', /y|yes/) || raise('Please run tests first.')
56
+ Interaction.announce 'Finishing security update'
57
+ Interaction.note 'Working directory clean.'
58
+ Interaction.prompt('Have you successfully run all tests?', 'n', /y|yes/) || Interaction.fail('Please run tests first.')
35
59
 
36
- note 'About to: push production, checkout & pull master, merge production, push master'
37
- prompt('Continue?', 'n', /y|yes/) || raise('Cancelled.')
60
+ Interaction.note 'About to: push production, checkout & pull master, merge production, push master.'
61
+ Interaction.prompt('Continue?', 'n', /y|yes/) || Interaction.fail('Cancelled.')
38
62
 
39
63
  Util.system! 'git push', show_cmd: true
40
64
  Util.system! 'git checkout master', show_cmd: true
@@ -42,13 +66,46 @@ def security_update(step = 'prepare')
42
66
  Util.system! 'git merge production', show_cmd: true
43
67
  Util.system! 'git push', show_cmd: true
44
68
 
45
- announce 'Deploying all targets'
69
+ Interaction.announce 'Deployment'
46
70
  deploy = (Util.gem_major_version('capistrano') == 3) ? 'deploy' : 'deploy:migrations'
47
- invoke_cmd 'capistrano', deploy
48
71
 
49
- success 'Successfully pushed and deployed security update'
72
+ all_deploy_targets = Util.deploy_targets
73
+ Interaction.fail 'There are no deploy targets!' if all_deploy_targets.empty?
74
+
75
+ if all_deploy_targets.include?('staging')
76
+ Interaction.note 'There is a staging environment.'
77
+ Interaction.prompt('Deploy staging now?', 'y', /y|yes/) || Interaction.fail('Cancelled.')
78
+
79
+ Interaction.announce 'Deploy staging'
80
+ Util.system! "bundle exec cap staging #{deploy}", show_cmd: true
81
+
82
+ Interaction.prompt('Is the deployment log okay and the application is still running on staging?', 'y', /y|yes/) || Interaction.fail('Please fix the deployment issues on staging before you continue.')
83
+ else
84
+ Interaction.note 'There is no staging environment.'
85
+ end
86
+
87
+ deploy_targets_without_staging = all_deploy_targets.select { |target| target != 'staging' }
88
+
89
+ if deploy_targets_without_staging.empty?
90
+ Interaction.note 'There are no other stages.'
91
+ else
92
+ puts
93
+ Interaction.note 'Found the following other stages:'
94
+ puts deploy_targets_without_staging
95
+ puts
96
+ Interaction.prompt('Deploy other stages now?', 'y', /y|yes/) || Interaction.fail('Cancelled.')
97
+
98
+ deploy_targets_without_staging.each do |target|
99
+ Interaction.announce "Deploy #{target}"
100
+ Util.system! "bundle exec cap #{target} #{deploy}", show_cmd: true
101
+ end
102
+
103
+ Interaction.prompt('Is the application still running on all other stages and the logs are okay?', 'y', /y|yes/) || Interaction.fail('Please fix the application immediately!')
104
+ end
105
+
106
+ Interaction.success 'Successfully pushed and deployed security update'
50
107
  puts
51
- note 'Now send an email to customer and project lead, informing them about the update.'
52
- note 'Do not forget to make a joblog on a security budget, if available.'
108
+ Interaction.note 'Now send an email to customer and project lead, informing them about the update.'
109
+ Interaction.note 'Do not forget to make a joblog on a security budget, if available.'
53
110
  end
54
111
  end