geordi 3.0.2 → 4.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/.ruby-version +1 -1
  4. data/CHANGELOG.md +44 -6
  5. data/Gemfile.lock +1 -1
  6. data/README.md +138 -118
  7. data/Rakefile +18 -8
  8. data/geordi.gemspec +1 -0
  9. data/lib/geordi/COMMAND_TEMPLATE +3 -1
  10. data/lib/geordi/commands/chromedriver_update.rb +2 -2
  11. data/lib/geordi/commands/commit.rb +1 -6
  12. data/lib/geordi/commands/console.rb +9 -4
  13. data/lib/geordi/commands/create_databases.rb +2 -1
  14. data/lib/geordi/commands/cucumber.rb +15 -10
  15. data/lib/geordi/commands/delete_dumps.rb +0 -1
  16. data/lib/geordi/commands/deploy.rb +11 -11
  17. data/lib/geordi/commands/drop_databases.rb +0 -6
  18. data/lib/geordi/commands/dump.rb +9 -18
  19. data/lib/geordi/commands/firefox.rb +2 -5
  20. data/lib/geordi/commands/migrate.rb +1 -1
  21. data/lib/geordi/commands/rake.rb +3 -1
  22. data/lib/geordi/commands/rspec.rb +5 -9
  23. data/lib/geordi/commands/security_update.rb +65 -9
  24. data/lib/geordi/commands/server.rb +1 -1
  25. data/lib/geordi/commands/setup.rb +2 -11
  26. data/lib/geordi/commands/shell.rb +7 -4
  27. data/lib/geordi/commands/unit.rb +1 -1
  28. data/lib/geordi/commands/update.rb +0 -11
  29. data/lib/geordi/commands/vnc.rb +1 -3
  30. data/lib/geordi/commands/with_rake.rb +1 -1
  31. data/lib/geordi/cucumber.rb +6 -4
  32. data/lib/geordi/dump_loader.rb +3 -1
  33. data/lib/geordi/gitpt.rb +9 -51
  34. data/lib/geordi/remote.rb +14 -2
  35. data/lib/geordi/settings.rb +155 -0
  36. data/lib/geordi/util.rb +12 -4
  37. data/lib/geordi/version.rb +1 -1
  38. metadata +7 -33
  39. data/exe/cap-all +0 -4
  40. data/exe/console-for +0 -4
  41. data/exe/cuc +0 -4
  42. data/exe/cuc-show +0 -4
  43. data/exe/cuc-vnc-setup +0 -4
  44. data/exe/deploy-to-production +0 -4
  45. data/exe/dump-for +0 -8
  46. data/exe/gitpt +0 -4
  47. data/exe/load-dump +0 -4
  48. data/exe/migrate-all +0 -4
  49. data/exe/rs +0 -4
  50. data/exe/run_tests +0 -4
  51. data/exe/shell-for +0 -4
  52. data/exe/tests +0 -4
  53. data/lib/geordi/commands/eurest.rb +0 -4
@@ -3,7 +3,7 @@ desc 'server [PORT]', 'Start a development server'
3
3
  option :port, aliases: '-p', default: '3000',
4
4
  desc: 'Choose a port'
5
5
  option :public, aliases: '-P', type: :boolean,
6
- desc: 'Make the server accessible in the local network'
6
+ desc: 'Make the server accessible from the local network'
7
7
 
8
8
  def server(port = nil)
9
9
  invoke_cmd 'bundle_install'
@@ -6,17 +6,8 @@ Check out a repository and cd into its directory. Then let `setup` do the tiring
6
6
  work: run `bundle install`, create `database.yml`, create databases, migrate
7
7
  (all if applicable).
8
8
 
9
- If a local bin/setup file is found, Geordi skips these steps runs bin/setup
10
- for setup instead.
11
-
12
- After setting up, loads a remote database dump into the development db when
13
- called with the `--dump` option:
14
-
15
- geordi setup -d staging
16
-
17
- After setting up, runs all tests when called with the `--test` option:
18
-
19
- geordi setup -t
9
+ If a local bin/setup file is found, Geordi skips its routine and runs bin/setup
10
+ instead.
20
11
  LONGDESC
21
12
 
22
13
  option :dump, type: :string, aliases: '-d', banner: 'TARGET',
@@ -2,18 +2,21 @@ desc 'shell TARGET', 'Open a shell on a Capistrano deploy target'
2
2
  long_desc <<-LONGDESC
3
3
  Example: `geordi shell production`
4
4
 
5
- Lets you select the server to connect to when called with `--select-server`:
6
-
7
- geordi shell production -s
5
+ Selecting the server: `geordi shell staging -s` shows a menu with all available
6
+ servers. When passed a number, directly connects to the selected server.
8
7
  LONGDESC
9
8
 
10
- option :select_server, default: false, type: :boolean, aliases: '-s'
9
+ # This option is duplicated in console.rb
10
+ option :select_server, type: :string, aliases: '-s', banner: '[SERVER_NUMBER]',
11
+ desc: 'Select a server to connect to'
11
12
 
12
13
  # This method has a triple 'l' because :shell is a Thor reserved word. However,
13
14
  # it can still be called with `geordi shell` :)
14
15
  def shelll(target, *_args)
15
16
  require 'geordi/remote'
16
17
 
18
+ invoke_cmd 'bundle_install'
19
+
17
20
  Interaction.announce 'Opening a shell on ' + target
18
21
  Geordi::Remote.new(target).shell(options)
19
22
  end
@@ -5,7 +5,7 @@ def unit
5
5
  invoke_cmd 'yarn_install'
6
6
 
7
7
  Interaction.announce 'Running Test::Unit'
8
- Util.system! 'bundle exec rake test'
8
+ Util.system! Util.binstub('rake'), 'test'
9
9
  else
10
10
  Interaction.note 'Test::Unit not employed.'
11
11
  end
@@ -3,17 +3,6 @@ long_desc <<-LONGDESC
3
3
  Example: `geordi update`
4
4
 
5
5
  Performs: `git pull`, `bundle install` (if necessary) and migrates (if applicable).
6
-
7
- After updating, loads a dump into the development db when called with the
8
- `--dump` option:
9
-
10
- geordi update -d staging
11
-
12
- After updating, runs all tests when called with the `--test` option:
13
-
14
- geordi update -t
15
-
16
- See `geordi help update` for details.
17
6
  LONGDESC
18
7
 
19
8
  option :dump, type: :string, aliases: '-d', banner: 'TARGET',
@@ -4,11 +4,9 @@ Example: `geordi vnc` or `geordi vnc --setup`
4
4
 
5
5
  Launch a VNC session to the hidden screen where `geordi cucumber` runs Selenium
6
6
  tests.
7
-
8
- When called with `--setup`, will guide through the setup of VNC.
9
7
  LONGDESC
10
8
 
11
- option :setup, type: :boolean
9
+ option :setup, type: :boolean, desc: 'Guide through the setup of VNC'
12
10
 
13
11
  def vnc
14
12
  if options.setup
@@ -5,7 +5,7 @@ def with_rake
5
5
  invoke_cmd 'yarn_install'
6
6
 
7
7
  Interaction.announce 'Running tests with `rake`'
8
- Util.system! 'rake'
8
+ Util.system! Util.binstub('rake')
9
9
  else
10
10
  Interaction.note '`rake` does not run tests.'
11
11
  :did_not_perform
@@ -5,6 +5,7 @@ require 'tempfile'
5
5
  # version of Geordi.
6
6
  require File.expand_path('interaction', __dir__)
7
7
  require File.expand_path('firefox_for_selenium', __dir__)
8
+ require File.expand_path('settings', __dir__)
8
9
 
9
10
  module Geordi
10
11
  class Cucumber
@@ -16,10 +17,11 @@ module Geordi
16
17
 
17
18
  def run(files, cucumber_options, options = {})
18
19
  self.argv = files + cucumber_options.map { |option| option.split('=') }.flatten
20
+ self.settings = Geordi::Settings.new
19
21
 
20
22
  consolidate_rerun_txt_files
21
23
  show_features_to_run
22
- setup_vnc
24
+ setup_vnc if settings.use_vnc?
23
25
 
24
26
  command = use_parallel_tests?(options) ? parallel_execution_command : serial_execution_command
25
27
  Interaction.note_cmd(command) if options[:verbose]
@@ -65,14 +67,14 @@ module Geordi
65
67
 
66
68
  private
67
69
 
68
- attr_accessor :argv
70
+ attr_accessor :argv, :settings
69
71
 
70
72
  def serial_execution_command
71
73
  format_args = []
72
74
  unless argv.include?('--format') || argv.include?('-f')
73
75
  format_args = spinner_available? ? ['--format', 'CucumberSpinner::CuriousProgressBarFormatter'] : ['--format', 'progress']
74
76
  end
75
- [use_firefox_for_selenium, 'b', 'cucumber', format_args, escape_shell_args(argv)].flatten.compact.join(' ')
77
+ [use_firefox_for_selenium, Util.binstub('cucumber'), format_args, escape_shell_args(argv)].flatten.compact.join(' ')
76
78
  end
77
79
 
78
80
  def parallel_execution_command
@@ -85,7 +87,7 @@ module Geordi
85
87
 
86
88
  [
87
89
  use_firefox_for_selenium,
88
- 'b parallel_test -t ' + type_arg,
90
+ 'bundle exec parallel_test -t ' + type_arg,
89
91
  %(-o '#{command_line_options.join(' ')} --tags "#{not_tag('@solo')}"'),
90
92
  "-- #{features.join(' ')}",
91
93
  ].compact.join(' ')
@@ -13,7 +13,9 @@ module Geordi
13
13
  def development_database_config
14
14
  require 'yaml'
15
15
 
16
- @config ||= YAML.safe_load(ERB.new(File.read('config/database.yml')).result)
16
+ evaluated_config_file = ERB.new(File.read('config/database.yml')).result
17
+ # Allow aliases and a special set of classes like symbols and time objects
18
+ @config ||= YAML.safe_load(evaluated_config_file, [Symbol, Time], [], true)
17
19
  @config['development']
18
20
  end
19
21
  alias_method :config, :development_database_config
@@ -3,12 +3,14 @@ class Gitpt
3
3
  require 'highline'
4
4
  require 'tracker_api'
5
5
 
6
- SETTINGS_FILE_NAME = '.gitpt'.freeze
7
- PROJECT_IDS_FILE_NAME = '.pt_project_id'.freeze
6
+ # This require-style is to prevent Ruby from loading files of a different
7
+ # version of Geordi.
8
+ require File.expand_path('settings', __dir__)
8
9
 
9
10
  def initialize
10
11
  self.highline = HighLine.new
11
- self.client = build_client(read_settings)
12
+ self.settings = Geordi::Settings.new
13
+ self.client = build_client
12
14
  end
13
15
 
14
16
  def run(git_args)
@@ -24,57 +26,17 @@ No staged changes. Will create an empty commit.
24
26
 
25
27
  private
26
28
 
27
- attr_accessor :highline, :client
29
+ attr_accessor :highline, :client, :settings
28
30
 
29
- def read_settings
30
- file_path = File.join(ENV['HOME'], SETTINGS_FILE_NAME)
31
-
32
- unless File.exist?(file_path)
33
- highline.say HighLine::RESET
34
- highline.say "Welcome to #{bold 'gitpt'}.\n\n"
35
-
36
- highline.say highlight('Your settings are missing or invalid.')
37
- highline.say "Please configure your Pivotal Tracker access.\n\n"
38
- token = highline.ask bold('Your API key:') + ' '
39
- highline.say "\n"
40
-
41
- settings = { token: token }
42
- File.open(file_path, 'w') do |file|
43
- file.write settings.to_yaml
44
- end
45
- end
46
-
47
- YAML.load_file(file_path)
48
- end
49
-
50
- def build_client(settings)
51
- TrackerApi::Client.new(token: settings.fetch(:token))
31
+ def build_client
32
+ TrackerApi::Client.new(token: settings.pivotal_tracker_api_key)
52
33
  end
53
34
 
54
35
  def load_projects
55
- project_ids = read_project_ids
36
+ project_ids = settings.pivotal_tracker_project_ids
56
37
  project_ids.collect { |project_id| client.project(project_id) }
57
38
  end
58
39
 
59
- def read_project_ids
60
- file_path = PROJECT_IDS_FILE_NAME
61
-
62
- if File.exist?(file_path)
63
- project_ids = File.read('.pt_project_id').split(/[\s]+/).map(&:to_i)
64
- end
65
-
66
- if project_ids && (project_ids.size > 0)
67
- project_ids
68
- else
69
- Geordi::Interaction.warn "Sorry, I could not find a project ID in #{file_path} :("
70
- puts
71
-
72
- puts "Please put at least one Pivotal Tracker project id into #{file_path} in this directory."
73
- puts 'You may add multiple IDs, separated using white space.'
74
- exit 1
75
- end
76
- end
77
-
78
40
  def applicable_stories
79
41
  projects = load_projects
80
42
  projects.collect do |project|
@@ -134,8 +96,4 @@ No staged changes. Will create an empty commit.
134
96
  HighLine::BOLD + string + HighLine::RESET
135
97
  end
136
98
 
137
- def highlight(string)
138
- bold HighLine::BLUE + string
139
- end
140
-
141
99
  end
@@ -34,7 +34,6 @@ module Geordi
34
34
  # Generate dump on the server
35
35
  shell options.merge({
36
36
  remote_command: "dumple #{@config.env} --for_download",
37
- select_server: nil, # Dump must be generated on the primary server
38
37
  })
39
38
 
40
39
  destination_directory = File.join(@config.root, 'tmp')
@@ -56,7 +55,20 @@ module Geordi
56
55
  end
57
56
 
58
57
  def shell(options = {})
59
- server = options[:select_server] ? select_server : @config.primary_server
58
+ server_option = options[:select_server]
59
+ server_number = server_option.to_i
60
+
61
+ server = if server_option == 'select_server'
62
+ select_server
63
+ elsif server_number != 0 && server_number <= @config.servers.count
64
+ server_index = server_number - 1
65
+ @config.servers[server_index]
66
+ elsif server_option.nil?
67
+ @config.primary_server
68
+ else
69
+ Interaction.warn "Invalid server number: #{server_option}"
70
+ select_server
71
+ end
60
72
 
61
73
  remote_command = "cd #{@config.remote_root} && #{@config.shell}"
62
74
  remote_command << " -c '#{options[:remote_command]}'" if options[:remote_command]
@@ -0,0 +1,155 @@
1
+ require 'yaml'
2
+
3
+ require File.expand_path('util', __dir__)
4
+ require File.expand_path('interaction', __dir__)
5
+
6
+ module Geordi
7
+ class Settings
8
+ GLOBAL_SETTINGS_FILE_NAME = Util.testing? ? './tmp/global_settings.yml'.freeze : File.join(ENV['HOME'], '.config/geordi/global.yml').freeze
9
+ LOCAL_SETTINGS_FILE_NAME = Util.testing? ? './tmp/local_settings.yml'.freeze : './.geordi.yml'.freeze
10
+
11
+ ALLOWED_GLOBAL_SETTINGS = %w[ pivotal_tracker_api_key ].freeze
12
+ ALLOWED_LOCAL_SETTINGS = %w[ use_vnc pivotal_tracker_project_ids ].freeze
13
+
14
+ def initialize
15
+ read_settings
16
+ end
17
+
18
+ # Global settings
19
+ def pivotal_tracker_api_key
20
+ @global_settings['pivotal_tracker_api_key'] || gitpt_api_key_old || inquire_pt_api_key
21
+ end
22
+
23
+ def pivotal_tracker_api_key=(value)
24
+ @global_settings['pivotal_tracker_api_key'] = value
25
+ save_global_settings
26
+ end
27
+
28
+ # Local settings
29
+ # They should not be changed by geordi to avoid unexpected diffs, therefore
30
+ # there are no setters for these settings
31
+ def use_vnc?
32
+ @local_settings.fetch('use_vnc', true)
33
+ end
34
+
35
+ def pivotal_tracker_project_ids
36
+ project_ids = @local_settings['pivotal_tracker_project_ids'] || pt_project_ids_old
37
+
38
+ case project_ids
39
+ when Array
40
+ # nothing to do
41
+ when String
42
+ project_ids = project_ids.split(/[\s]+/).map(&:to_i)
43
+ when Integer
44
+ project_ids = [project_ids]
45
+ else
46
+ project_ids = []
47
+ end
48
+
49
+ if project_ids.empty?
50
+ puts
51
+ Geordi::Interaction.warn "Sorry, I could not find a project ID in .geordi.yml :("
52
+ puts
53
+
54
+ puts "Please put at least one Pivotal Tracker project id into the .geordi.yml file in this directory, e.g."
55
+ puts
56
+ puts "pivotal_tracker_project_ids:"
57
+ puts "- 123456"
58
+ puts
59
+ puts 'You may add multiple IDs.'
60
+ exit 1
61
+ end
62
+
63
+ project_ids
64
+ end
65
+
66
+ private
67
+
68
+ def read_settings
69
+ global_path = GLOBAL_SETTINGS_FILE_NAME
70
+ local_path = LOCAL_SETTINGS_FILE_NAME
71
+
72
+ if File.exists?(global_path)
73
+ global_settings = YAML.safe_load(File.read(global_path))
74
+ check_for_invalid_keys(global_settings, ALLOWED_GLOBAL_SETTINGS, global_path)
75
+ end
76
+
77
+ if File.exists?(local_path)
78
+ local_settings = YAML.safe_load(File.read(local_path))
79
+ check_for_invalid_keys(local_settings, ALLOWED_LOCAL_SETTINGS, local_path)
80
+ end
81
+
82
+ @global_settings = global_settings || {}
83
+ @local_settings = local_settings || {}
84
+ end
85
+
86
+ def check_for_invalid_keys(settings, allowed_keys, file)
87
+ invalid_keys = settings.keys - allowed_keys
88
+ unless invalid_keys.empty?
89
+ Geordi::Interaction.warn "Geordi detected unknown keys in #{file}.\n"
90
+
91
+ invalid_keys.sort.each do |key|
92
+ puts "* #{key}"
93
+ end
94
+
95
+ puts "\nAllowed keys are:"
96
+ allowed_keys.sort.each do |key|
97
+ puts "* #{key}"
98
+ end
99
+ puts
100
+
101
+ exit 1
102
+ end
103
+ end
104
+
105
+ def save_global_settings
106
+ global_path = GLOBAL_SETTINGS_FILE_NAME
107
+ global_directory = File.dirname(global_path)
108
+ FileUtils.mkdir_p(global_directory) unless File.directory? global_directory
109
+ File.open(global_path, 'w') do |file|
110
+ file.write @global_settings.to_yaml
111
+ end
112
+ end
113
+
114
+ # deprecated
115
+ def gitpt_api_key_old
116
+ file_path = File.join(ENV['HOME'], '.gitpt')
117
+ if File.exist?(file_path) && !Util.testing?
118
+ token = YAML.load_file(file_path).fetch :token
119
+ self.pivotal_tracker_api_key = token
120
+
121
+ Geordi::Interaction.warn "The ~/.gitpt file is deprecated."
122
+ Geordi::Interaction.note "The contained setting has been moved to ~/.config/geordi/global.yml."
123
+ Geordi::Interaction.note "If you don't need to work with an older version of geordi you can delete ~/.gitpt now."
124
+
125
+ token
126
+ end
127
+ end
128
+
129
+ def inquire_pt_api_key
130
+ Geordi::Interaction.warn 'Your settings are missing or invalid.'
131
+ Geordi::Interaction.warn "Please configure your Pivotal Tracker access."
132
+ token = Geordi::Interaction.prompt 'Your API key:'
133
+ self.pivotal_tracker_api_key = token
134
+ puts
135
+
136
+ token
137
+ end
138
+
139
+ # deprecated
140
+ def pt_project_ids_old
141
+ if File.exist?('.pt_project_id')
142
+ project_ids = File.read('.pt_project_id')
143
+ puts # Make sure to start on a new line (e.g. when invoked after a #print)
144
+ Geordi::Interaction.warn "The usage of the .pt_project_id file is deprecated."
145
+ Geordi::Interaction.note Util.strip_heredoc(<<-INSTRUCTIONS)
146
+ Please remove this file from your project and add or extend the .geordi.yml file with the following content:
147
+ pivotal_tracker_project_ids: #{project_ids}
148
+ INSTRUCTIONS
149
+
150
+ project_ids
151
+ end
152
+ end
153
+
154
+ end
155
+ end
@@ -62,13 +62,21 @@ module Geordi
62
62
  end
63
63
  end
64
64
 
65
+ def binstub(executable, *arguments)
66
+ binstub_file = "bin/#{executable}"
67
+
68
+ command = File.exists?(binstub_file) ? [binstub_file] : ['bundle exec', executable]
69
+ command.push(*arguments)
70
+ command.join(' ')
71
+ end
72
+
65
73
  def console_command(environment)
66
74
  if gem_major_version('rails') == 2
67
75
  'script/console ' + environment
68
76
  elsif gem_major_version('rails') == 3
69
- 'bundle exec rails console ' + environment
77
+ binstub 'rails', 'console', environment
70
78
  else
71
- "bundle exec rails console -e #{environment}"
79
+ binstub 'rails', 'console -e', environment
72
80
  end
73
81
  end
74
82
 
@@ -76,7 +84,7 @@ module Geordi
76
84
  if gem_major_version('rails') == 2
77
85
  'script/server ""'
78
86
  else
79
- 'bundle exec rails server'
87
+ binstub 'rails', 'server'
80
88
  end
81
89
  end
82
90
 
@@ -93,7 +101,7 @@ module Geordi
93
101
  ENV['GEORDI_TESTING_STAGED_CHANGES'] == 'true'
94
102
  else
95
103
  statuses = `git status --porcelain`.split("\n")
96
- statuses.any? { |l| l.start_with? /[A-Z]/i }
104
+ statuses.any? { |l| /^[A-Z]/i =~ l }
97
105
  end
98
106
  end
99
107