geordi 9.6.1 → 10.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8a4dc434853c2c8af63e6ac18e69cca013d3a15316401f710c90deab74cd9bbb
4
- data.tar.gz: c158a1f4c095120bfbd3ce9aacbd6bf13424994cdfd1ea5c2d5d8b613db5179e
3
+ metadata.gz: b7ba836f9a4c3651c05372c9a6f0f09cc86a4ed192020c7a0e9def2b5742b76b
4
+ data.tar.gz: 2668192b49eb8c16bcac3cdc1563f77b7d09c0759fa51ef7ea80288732fcb4dc
5
5
  SHA512:
6
- metadata.gz: 2260516cf299c4202fbf276c36abbee42190fb9e48b0f583b1af05a457c7676f6d23ccaee7d2bd8ddc2bcdfa923a255cae706dc5c1414c97eab1be58635ad205
7
- data.tar.gz: e556b2806dea5a5badbcdad1c22faad5fb56daf95923f1c5ac88f64f1a764d18aacf4d0d683ad0f2b050cbdb131482895da3d58157992af2e11d67e75d0e75bf
6
+ metadata.gz: 68a50772d7eca079e66034805f87362dca94fa969ee7bd3095fa5c64236af99c1e28658742fa9ff151f5930e23fed1035f57f8c432d3528545498e6f7fb58a76
7
+ data.tar.gz: e24dc966d01cef499829b00337bb221cc662bfe080421010c72791bb0954388fb4d2fd13a42ce689405133870b976349b42b995443b354d2c04d6239f9c4934b
@@ -31,6 +31,7 @@ jobs:
31
31
  run: |
32
32
  gem install bundler:2.2.26
33
33
  bundle install --no-deployment
34
- - name: Run tests
35
- run: bundle exec rake
36
-
34
+ - name: Run specs
35
+ run: bundle exec rspec
36
+ - name: Run feature tests
37
+ run: bundle exec cucumber
data/CHANGELOG.md CHANGED
@@ -10,6 +10,20 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html
10
10
  ### Breaking changes
11
11
 
12
12
 
13
+ # 10.0.0 2024-03-07
14
+
15
+ ### Compatible changes
16
+ * `console` command: IRB flags stored in the global config file are automatically passed on to IRB
17
+ * `console` command: Improve interrupt handling in local Rails console
18
+ * `rspec` and `cucumber` commands: Do not fail or exit if chromedriver update fails
19
+ * Fix detection of IRB version
20
+ * Add new hints to 'Did you know'
21
+
22
+ ### Breaking changes
23
+ * `dump`-command: Drop and recreate the database before restoring a postgres dump
24
+ * `drop_databases`-command: Use local user per default to connect to database
25
+
26
+
13
27
  # 9.6.1 2023-09-22
14
28
 
15
29
  ### Compatible changes
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- geordi (9.6.1)
4
+ geordi (10.0.0)
5
5
  thor (~> 1)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -19,6 +19,8 @@ You may abbreviate commands by typing only their first letters, e.g. `geordi
19
19
  con` will boot a development console, `geordi set -t` will setup a project and
20
20
  run tests afterwards.
21
21
 
22
+ Commands will occasionally print "did you know" hints of other Geordi features.
23
+
22
24
  You can always run `geordi help <command>` to quickly look up command help.
23
25
 
24
26
  ### `geordi branch`
@@ -51,9 +53,6 @@ Setting `auto_update_chromedriver` to `true` in your global Geordi config file
51
53
  (`~/.config/geordi/global.yml`), will automatically update chromedriver before
52
54
  cucumber tests if a newer chromedriver version is available.
53
55
 
54
- **Options**
55
- - `[--quiet-if-matching], [--no-quiet-if-matching]`: Suppress notification if chromedriver is already on the latest version
56
-
57
56
 
58
57
  ### `geordi clean`
59
58
  Remove unneeded files from the current directory.
@@ -80,6 +79,8 @@ Remote: `geordi console staging`
80
79
  Selecting the server: `geordi console staging -s` shows a menu with all available
81
80
  servers. When passed a number, directly connects to the selected server.
82
81
 
82
+ IRB flags can be given as `irb_flags: '...'` in the global Geordi config file (`~/.config/geordi/global.yml`).
83
+
83
84
  **Options**
84
85
  - `-s, [--select-server=[SERVER_NUMBER]]`: Select a server to connect to
85
86
 
@@ -108,7 +109,7 @@ variable like this: `PARALLEL_TEST_PROCESSORS=6 geordi cucumber`
108
109
 
109
110
 
110
111
  ### `geordi delete-dumps [DIRECTORY]`
111
- Delete database dump files (*.dump).
112
+ Help deleting database dump files (*.dump).
112
113
 
113
114
  Example: `geordi delete-dumps` or `geordi delete-dumps ~/tmp/dumps`
114
115
 
@@ -166,14 +167,18 @@ and offer to delete them. Excluded are databases that are whitelisted. This come
166
167
  in handy when you're keeping your currently active projects in the whitelist files
167
168
  and perform regular housekeeping with Geordi.
168
169
 
170
+ Per default, Geordi will try to connect to the databases as a local user without
171
+ password authorization.
172
+
169
173
  Geordi will ask for confirmation before actually dropping databases and will
170
174
  offer to edit the whitelist instead.
171
175
 
172
176
  **Options**
173
- - `-P, [--postgres-only], [--no-postgres-only]`: Only clean Postgres
177
+ - `-P, [--postgres-only], [--no-postgres-only]`: Only clean PostgreSQL
174
178
  - `-M, [--mysql-only], [--no-mysql-only]`: Only clean MySQL/MariaDB
175
- - `[--postgres=PORT_OR_SOCKET]`: Use Postgres port or socket
179
+ - `[--postgres=PORT_OR_SOCKET]`: Use PostgreSQL port or socket
176
180
  - `[--mysql=PORT_OR_SOCKET]`: Use MySQL/MariaDB port or socket
181
+ - `-S, [--sudo], [--no-sudo]`: Access databases as root
177
182
 
178
183
 
179
184
  ### `geordi dump [TARGET]`
@@ -396,19 +401,25 @@ Adding a new command
396
401
  ---------------
397
402
 
398
403
  Copy `lib/geordi/COMMAND_TEMPLATE` to `lib/geordi/commands/your_command` and
399
- edit it to do what you need it to do. Please add a feature test for the new
400
- command; see features/ for inspiration.
404
+ edit it to do what you need it to do. Please add appropriate tests for the new
405
+ command; see existing tests for inspiration.
401
406
 
402
- To try Geordi locally, call it like this:
403
407
 
404
- # -I means "add the following directory to load path"
405
- ruby -Ilib exe/geordi
408
+ Running Geordi locally
409
+ ----------------------
410
+
411
+ To run Geordi without installation, call it like this:
412
+
413
+ ruby -I lib exe/geordi
414
+
415
+ # With debugger
416
+ ruby -r byebug -I lib exe/geordi
406
417
 
407
418
  # From another directory
408
419
  ruby -I ../geordi/lib ../geordi/exe/geordi
409
420
 
410
- # With debugger
411
- ruby -r byebug -I ../geordi/lib ../geordi/exe/geordi
421
+ # Run Geordi with the Ruby version of that other directory
422
+ RBENV_VERSION=$(<.ruby-version) ruby -I ../geordi/lib ../geordi/exe/geordi
412
423
 
413
424
  You can also *install* Geordi locally from its project directory with
414
425
  `rake install`. Make sure to switch to the expected Ruby version before.
data/Rakefile CHANGED
@@ -2,10 +2,14 @@ require 'bundler'
2
2
  Bundler::GemHelper.install_tasks
3
3
 
4
4
  desc 'Default: Run all tests'
5
- task default: :features
5
+ task default: [:rspec, :features]
6
6
 
7
7
  task :features do
8
- exec 'bundle exec cucumber'
8
+ system 'bundle exec cucumber'
9
+ end
10
+
11
+ task :rspec do
12
+ system 'bundle exec rspec'
9
13
  end
10
14
 
11
15
  task :readme do
@@ -32,6 +36,8 @@ You may abbreviate commands by typing only their first letters, e.g. `geordi
32
36
  con` will boot a development console, `geordi set -t` will setup a project and
33
37
  run tests afterwards.
34
38
 
39
+ Commands will occasionally print "did you know" hints of other Geordi features.
40
+
35
41
  You can always run `geordi help <command>` to quickly look up command help.
36
42
  TEXT
37
43
 
@@ -42,12 +48,11 @@ You can always run `geordi help <command>` to quickly look up command help.
42
48
  geordi_section << "#{command.description.sub /(\.)?$/, '.'}\n\n"
43
49
  geordi_section << "#{command.long_description.strip}\n\n" if command.long_description
44
50
 
45
- if command.options.any?
46
- geordi_section << "**Options**\n"
47
- # Taken from thor-1.0.1/lib/thor/base.rb:557
48
- command.options.values.each do |option|
49
- next if option.hide
51
+ visible_options = command.options.reject {|_option_name, option_object| option_object.hide }
50
52
 
53
+ if visible_options.any?
54
+ geordi_section << "**Options**\n"
55
+ visible_options.values.each do |option|
51
56
  geordi_section << "- `#{option.usage}`"
52
57
  geordi_section << ": #{option.description}" if option.description
53
58
  geordi_section << "\n"
@@ -6,6 +6,8 @@ require 'fileutils'
6
6
 
7
7
  module Geordi
8
8
  class ChromedriverUpdater
9
+ class ProcessingError < StandardError; end
10
+
9
11
  VERSIONS_PER_MILESTONES_URL = "https://googlechromelabs.github.io/chrome-for-testing/latest-versions-per-milestone-with-downloads.json"
10
12
 
11
13
  def run(options)
@@ -18,6 +20,10 @@ module Geordi
18
20
  else
19
21
  update_chromedriver(latest_chromedriver_version)
20
22
  end
23
+
24
+ rescue ProcessingError => e
25
+ interaction_method = (options[:exit_on_failure] == false) ? :warn : :fail
26
+ Interaction.public_send(interaction_method, e.message)
21
27
  end
22
28
 
23
29
  private
@@ -29,7 +35,7 @@ module Geordi
29
35
  end
30
36
 
31
37
  if !status.success? || chrome_version.nil?
32
- Interaction.fail('Could not determine the version of Google Chrome.')
38
+ raise ProcessingError, 'Could not determine the version of Google Chrome.'
33
39
  else
34
40
  chrome_version
35
41
  end
@@ -44,7 +50,7 @@ module Geordi
44
50
  end
45
51
 
46
52
  if !status.success? || chromedriver_version.nil?
47
- Interaction.fail('Could not determine the version of chromedriver.')
53
+ raise ProcessingError, 'Could not determine the version of chromedriver.'
48
54
  else
49
55
  chromedriver_version
50
56
  end
@@ -62,11 +68,11 @@ module Geordi
62
68
  unzip(chromedriver_zip, File.expand_path('~/bin'))
63
69
 
64
70
  # We need to determine the version again, as it could be nil in case no chromedriver was installed before
65
- Interaction.success "Chromedriver updated to v#{determine_chromedriver_version}."
71
+ Interaction.success "Chromedriver updated to #{determine_chromedriver_version}."
66
72
  end
67
73
 
68
74
  def download_chromedriver(version)
69
- fetch_response(chromedriver_url(version), "Could not download chromedriver v#{version}.") do |response|
75
+ fetch_response(chromedriver_url(version), "Could not download chromedriver #{version}.") do |response|
70
76
  file = Tempfile.new(%w[chromedriver .zip])
71
77
  file.write(response.body)
72
78
 
@@ -81,8 +87,13 @@ module Geordi
81
87
  if response.is_a?(Net::HTTPSuccess)
82
88
  yield(response)
83
89
  else
84
- Interaction.fail(error_message)
90
+ raise ProcessingError, error_message
85
91
  end
92
+
93
+ # Rescue Errno::NOERROR, Errno::ENOENT, Errno::EACCES, Errno::EFAULT, Errno::ECONNREFUSED, Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EHOSTDOWN, Errno::EHOSTUNREACH, ...,
94
+ # all of which are a subclass of SystemCallError
95
+ rescue SystemCallError => e
96
+ raise ProcessingError, "Request failed: #{e.message}"
86
97
  end
87
98
 
88
99
  def chromedriver_url(chrome_version)
@@ -94,7 +105,7 @@ module Geordi
94
105
  if chromedriver && chromedriver["url"]
95
106
  chromedriver["url"]
96
107
  else
97
- Interaction.fail("Could not find chromedriver download url for Chrome version v#{chrome_version}.")
108
+ raise ProcessingError, "Could not find chromedriver download url for Chrome #{chrome_version}."
98
109
  end
99
110
  end
100
111
 
@@ -105,7 +116,7 @@ module Geordi
105
116
  begin
106
117
  chromedriver_download_data = JSON.parse(response.body)
107
118
  rescue JSON::ParserError
108
- Interaction.fail("Could not parse chromedriver download data.")
119
+ raise ProcessingError, "Could not parse chromedriver download data."
109
120
  end
110
121
  @chromedriver_download_data = chromedriver_download_data
111
122
  end
@@ -113,14 +124,14 @@ module Geordi
113
124
 
114
125
  def latest_version(chrome_version)
115
126
  latest_version = chromedriver_download_data.dig("milestones", milestone_version(chrome_version), "version")
116
- latest_version || Interaction.fail("Could not find matching chromedriver for Chrome v#{chrome_version}.")
127
+ latest_version || raise(ProcessingError, "Could not find matching chromedriver for Chrome #{chrome_version}.")
117
128
  end
118
129
 
119
130
  def unzip(zip, output_dir)
120
131
  _stdout_str, _error_str, status = Open3.capture3('unzip', '-d', output_dir, '-o', zip.path)
121
132
 
122
133
  unless status.success?
123
- Interaction.fail("Could not unzip #{zip.path}.")
134
+ raise ProcessingError, "Could not unzip #{zip.path}."
124
135
  end
125
136
 
126
137
  # the archive contains a folder in which the relevant files are located. These files must be moved to ~/bin.
@@ -19,4 +19,9 @@ def capistrano(*args)
19
19
 
20
20
  Util.run!(command, fail_message: 'Capistrano failed. Have a look!')
21
21
  end
22
+
23
+ Hint.did_you_know [
24
+ :deploy,
25
+ :rake,
26
+ ]
22
27
  end
@@ -10,11 +10,17 @@ Setting `auto_update_chromedriver` to `true` in your global Geordi config file
10
10
  cucumber tests if a newer chromedriver version is available.
11
11
  LONGDESC
12
12
 
13
- option :quiet_if_matching, type: :boolean, default: false,
13
+ option :quiet_if_matching, type: :boolean, default: false, hide: true,
14
14
  desc: 'Suppress notification if chromedriver is already on the latest version'
15
+ option :exit_on_failure, type: :boolean, default: true, hide: true,
16
+ desc: "Exit with status code 1, if an error occurs."
15
17
 
16
18
  def chromedriver_update
17
19
  require 'geordi/chromedriver_updater'
18
20
 
19
21
  ChromedriverUpdater.new.run(options)
22
+
23
+ Hint.did_you_know [
24
+ 'Geordi can automatically keep chromedriver up-to-date. See `geordi help chromedriver-update`.',
25
+ ] unless options.quiet_if_matching
20
26
  end
@@ -14,6 +14,6 @@ def clean
14
14
  end
15
15
 
16
16
  Hint.did_you_know [
17
- :remove_executable_flags
17
+ :remove_executable_flags,
18
18
  ]
19
19
  end
@@ -13,6 +13,7 @@ def commit(*git_args)
13
13
  Gitpt.new.run_commit(git_args)
14
14
 
15
15
  Hint.did_you_know [
16
- :branch
16
+ :branch,
17
+ :deploy,
17
18
  ]
18
19
  end
@@ -6,6 +6,8 @@ Remote: `geordi console staging`
6
6
 
7
7
  Selecting the server: `geordi console staging -s` shows a menu with all available
8
8
  servers. When passed a number, directly connects to the selected server.
9
+
10
+ IRB flags can be given as `irb_flags: '...'` in the global Geordi config file (`~/.config/geordi/global.yml`).
9
11
  LONGDESC
10
12
 
11
13
  # This option is duplicated in shelll.rb
@@ -18,6 +20,7 @@ def console(target = 'development', *_args)
18
20
  Hint.did_you_know [
19
21
  :shelll,
20
22
  [:console, :select_server],
23
+ 'You only need to type the unique prefix of a command to run it. `geordi con` will work as well.',
21
24
  ]
22
25
 
23
26
  if target == 'development'
@@ -27,7 +30,8 @@ def console(target = 'development', *_args)
27
30
  Interaction.announce 'Opening a local Rails console'
28
31
 
29
32
  command = Util.console_command(target)
30
- Util.run!(command)
33
+ # Exec has better behavior on Ctrl + C
34
+ Util.run!(command, exec: true)
31
35
  else
32
36
  Interaction.announce 'Opening a Rails console on ' + target
33
37
 
@@ -49,7 +49,7 @@ def cucumber(*args)
49
49
  invoke_geordi 'bundle_install'
50
50
  invoke_geordi 'yarn_install'
51
51
  if settings.auto_update_chromedriver
52
- invoke_geordi 'chromedriver_update', quiet_if_matching: true
52
+ invoke_geordi 'chromedriver_update', quiet_if_matching: true, exit_on_failure: false
53
53
  end
54
54
 
55
55
  arguments = args
@@ -77,7 +77,9 @@ def cucumber(*args)
77
77
  [:cucumber, :modified],
78
78
  [:cucumber, :containing],
79
79
  [:cucumber, :debug],
80
- 'Geordi can automatically update chromedriver before Cucumber tests. See `geordi help chromedriver-update`.'
80
+ [:cucumber, :rerun],
81
+ 'Geordi can automatically keep chromedriver up-to-date. See `geordi help chromedriver-update`.',
82
+ 'You only need to type the unique prefix of a command to run it. `geordi cuc` will work as well.',
81
83
  ]
82
84
  else
83
85
  Interaction.note 'Cucumber not employed.'
@@ -1,4 +1,4 @@
1
- desc 'delete-dumps [DIRECTORY]', 'Delete database dump files (*.dump)'
1
+ desc 'delete-dumps [DIRECTORY]', 'Help deleting database dump files (*.dump)'
2
2
  long_desc <<-LONGDESC
3
3
  Example: `geordi delete-dumps` or `geordi delete-dumps ~/tmp/dumps`
4
4
 
@@ -41,6 +41,7 @@ def delete_dumps(*locations)
41
41
 
42
42
  Hint.did_you_know [
43
43
  :clean,
44
- :drop_databases
44
+ :drop_databases,
45
+ :dump,
45
46
  ]
46
47
  end
@@ -111,7 +111,7 @@ def deploy(target_stage = nil)
111
111
 
112
112
  Hint.did_you_know [
113
113
  :capistrano,
114
- :security_update
114
+ :security_update,
115
115
  ]
116
116
  else
117
117
  Util.run!("git checkout #{source_branch}")
@@ -7,18 +7,23 @@ and offer to delete them. Excluded are databases that are whitelisted. This come
7
7
  in handy when you're keeping your currently active projects in the whitelist files
8
8
  and perform regular housekeeping with Geordi.
9
9
 
10
+ Per default, Geordi will try to connect to the databases as a local user without
11
+ password authorization.
12
+
10
13
  Geordi will ask for confirmation before actually dropping databases and will
11
14
  offer to edit the whitelist instead.
12
15
  LONGDESC
13
16
 
14
17
  option :postgres_only, aliases: '-P', type: :boolean,
15
- desc: 'Only clean Postgres', default: false
18
+ desc: 'Only clean PostgreSQL', default: false
16
19
  option :mysql_only, aliases: '-M', type: :boolean,
17
20
  desc: 'Only clean MySQL/MariaDB', default: false
18
21
  option :postgres, banner: 'PORT_OR_SOCKET',
19
- desc: 'Use Postgres port or socket'
22
+ desc: 'Use PostgreSQL port or socket'
20
23
  option :mysql, banner: 'PORT_OR_SOCKET',
21
24
  desc: 'Use MySQL/MariaDB port or socket'
25
+ option :sudo, aliases: '-S', type: :boolean, default: false,
26
+ desc: 'Access databases as root'
22
27
 
23
28
  def drop_databases
24
29
  require 'geordi/db_cleaner'
@@ -42,13 +47,19 @@ def drop_databases
42
47
  postgres_flags = "--port=#{options.postgres}"
43
48
  end
44
49
 
45
- extra_flags = { 'mysql' => mysql_flags,
46
- 'postgres' => postgres_flags }
47
- cleaner = DBCleaner.new(extra_flags)
50
+ unless options.sudo
51
+ Interaction.note 'Assuming your local user has permission to drop databases. Run with `--sudo` to use sudo.'
52
+ end
53
+
54
+ extra_flags = {
55
+ 'mysql' => mysql_flags,
56
+ 'postgres' => postgres_flags,
57
+ }
58
+ cleaner = DBCleaner.new(extra_flags, sudo: options.sudo)
48
59
  cleaner.clean_mysql unless options.postgres_only
49
60
  cleaner.clean_postgres unless options.mysql_only
50
61
 
51
62
  Hint.did_you_know [
52
- :delete_dumps
63
+ :delete_dumps,
53
64
  ]
54
65
  end
@@ -73,6 +73,6 @@ def dump(target = nil, *_args)
73
73
  :delete_dumps,
74
74
  :drop_databases,
75
75
  :migrate,
76
- 'Geordi can load a dump directly into the local database if passed a Capistrano stage and the option -l. See `geordi help dump`.'
76
+ 'Geordi can load a dump directly into the local database if passed a Capistrano stage and the option -l. See `geordi help dump`.',
77
77
  ]
78
78
  end
@@ -24,6 +24,6 @@ def rake(*args)
24
24
  end
25
25
 
26
26
  Hint.did_you_know [
27
- :capistrano
27
+ :capistrano,
28
28
  ]
29
29
  end
@@ -14,6 +14,6 @@ def remove_executable_flags
14
14
  Interaction.success 'Done.'
15
15
 
16
16
  Hint.did_you_know [
17
- :clean
17
+ :clean,
18
18
  ]
19
19
  end
@@ -17,7 +17,7 @@ def rspec(*files)
17
17
  invoke_geordi 'bundle_install'
18
18
  invoke_geordi 'yarn_install'
19
19
  if settings.auto_update_chromedriver && Util.gem_available?('selenium-webdriver')
20
- invoke_geordi 'chromedriver_update', quiet_if_matching: true
20
+ invoke_geordi 'chromedriver_update', quiet_if_matching: true, exit_on_failure: false
21
21
  end
22
22
 
23
23
  Interaction.announce 'Running specs'
@@ -46,10 +46,13 @@ def rspec(*files)
46
46
  puts
47
47
  Util.run!(command.join(' '), fail_message: 'Specs failed.')
48
48
 
49
- Hint.did_you_know [
50
- :cucumber
51
- ]
52
49
  end
50
+
51
+ Hint.did_you_know [
52
+ :cucumber,
53
+ 'Geordi can automatically keep chromedriver up-to-date. See `geordi help chromedriver-update`.',
54
+ 'You only need to type the unique prefix of a command to run it. `geordi rs` will work as well.',
55
+ ]
53
56
  else
54
57
  Interaction.note 'RSpec not employed.'
55
58
  end
@@ -7,7 +7,7 @@ option :public, aliases: '-P', type: :boolean,
7
7
 
8
8
  def server(port = nil)
9
9
  Hint.did_you_know [
10
- [:server, :public]
10
+ [:server, :public],
11
11
  ]
12
12
 
13
13
  invoke_geordi 'bundle_install'
@@ -28,7 +28,10 @@ def setup
28
28
  Interaction.success 'Successfully set up the project.'
29
29
 
30
30
  Hint.did_you_know [
31
- :update
31
+ :update,
32
+ :security_update,
33
+ [:setup, :dump],
34
+ [:setup, :test],
32
35
  ] unless options.dump || options.test
33
36
 
34
37
  invoke_geordi 'dump', options.dump, load: true if options.dump
@@ -16,7 +16,8 @@ def shelll(target, *_args)
16
16
  require 'geordi/remote'
17
17
 
18
18
  Hint.did_you_know [
19
- :console
19
+ :console,
20
+ 'You only need to type the unique prefix of a command to run it. `geordi sh` will work as well.',
20
21
  ]
21
22
 
22
23
  Interaction.announce 'Opening a shell on ' + target
@@ -37,6 +37,6 @@ def tests(*args)
37
37
  end
38
38
 
39
39
  Hint.did_you_know [
40
- :deploy
40
+ :deploy,
41
41
  ]
42
42
  end
@@ -28,7 +28,9 @@ def update
28
28
  Interaction.success 'Successfully updated the project.'
29
29
 
30
30
  Hint.did_you_know [
31
- :setup
31
+ :setup,
32
+ [:update, :dump],
33
+ [:update, :test],
32
34
  ] unless options.dump || options.test
33
35
 
34
36
  invoke_geordi 'dump', options.dump, load: true if options.dump
@@ -3,37 +3,48 @@ require 'open3'
3
3
  require 'tempfile'
4
4
 
5
5
  module Geordi
6
+
7
+ DatabaseError = Class.new(StandardError)
8
+
6
9
  class DBCleaner
7
10
 
8
- def initialize(extra_flags)
9
- puts 'Please enter your sudo password if asked, for db operations as system users'
10
- puts "We're going to run `sudo -u postgres psql` for PostgreSQL"
11
- puts ' and `sudo mysql` for MariaDB (which uses PAM auth)'
12
- `sudo true`
13
- Interaction.fail 'sudo access is required for database operations as database users' if $? != 0
11
+ def initialize(extra_flags, sudo: false)
12
+ @sudo = sudo
13
+
14
+ if @sudo
15
+ Interaction.note 'Please enter your sudo password when asked.'
16
+ puts "We're going to run `sudo -u postgres psql` for PostgreSQL"
17
+ puts ' and `sudo mysql` for MariaDB (which uses PAM auth)'
18
+ `sudo true`
19
+ Interaction.fail 'sudo access is required for database operations as database users' if $? != 0
20
+ end
21
+
14
22
  @derivative_dbname = /_(test\d*|development|cucumber)$/
15
- base_directory = ENV['XDG_CONFIG_HOME']
16
- base_directory = Dir.home.to_s if base_directory.nil?
17
- @whitelist_directory = File.join(base_directory, '.config', 'geordi', 'whitelists')
18
- FileUtils.mkdir_p(@whitelist_directory) unless File.directory? @whitelist_directory
23
+ @base_directory = ENV['XDG_CONFIG_HOME']
24
+ @base_directory ||= Dir.home.to_s
25
+ @allowlist_directory = File.join(@base_directory, '.config', 'geordi', 'allowlists')
26
+ FileUtils.mkdir_p(@allowlist_directory) unless File.directory? @allowlist_directory
27
+ if File.directory?(legacy_allowlist_directory)
28
+ move_allowlist_files
29
+ end
19
30
  @mysql_command = decide_mysql_command(extra_flags['mysql'])
20
31
  @postgres_command = decide_postgres_command(extra_flags['postgres'])
21
32
  end
22
33
 
23
- def edit_whitelist(dbtype)
24
- whitelist = whitelist_fname(dbtype)
25
- whitelisted_dbs = if File.exist? whitelist
26
- Geordi::Util.stripped_lines(File.read(whitelist))\
34
+ def edit_allowlist(dbtype)
35
+ allowlist = allowlist_fname(dbtype)
36
+ allowlisted_dbs = if File.exist? allowlist
37
+ Geordi::Util.stripped_lines(File.read(allowlist))\
27
38
  .delete_if { |l| l.start_with? '#' }
28
39
  else
29
40
  []
30
41
  end
31
42
  all_dbs = list_all_dbs(dbtype)
32
- tmp = Tempfile.open("geordi_whitelist_#{dbtype}")
43
+ tmp = Tempfile.open("geordi_allowlist_#{dbtype}")
33
44
  tmp.write <<~HEREDOC
34
- # Put each whitelisted database on a new line.
45
+ # Put each allowlisted database on a new line.
35
46
  # System databases will never be deleted.
36
- # When you whitelist foo, foo_development and foo_test\\d* are whitelisted, too.
47
+ # When you allowlist foo, foo_development and foo_test\\d* are allowlisted, too.
37
48
  # This works even if foo does not exist. Also, you will only see foo in this list.
38
49
  #
39
50
  # Syntax: keep foo
@@ -41,32 +52,32 @@ module Geordi
41
52
  HEREDOC
42
53
  tmpfile_content = Array.new
43
54
  all_dbs.each do |db|
44
- next if is_whitelisted?(dbtype, db)
55
+ next if is_allowlisted?(dbtype, db)
45
56
  next if is_protected?(dbtype, db)
46
57
  db.sub!(@derivative_dbname, '')
47
58
  tmpfile_content.push(['drop', db])
48
59
  end
49
- warn_manual_whitelist = false
50
- whitelisted_dbs.each do |db_name|
51
- # Remove 'keep' word from whitelist entries. This is not normally required since geordi
52
- # does not save 'keep' or 'drop' to the whitelist file on disk but rather saves a list
53
- # of all whitelisted db names and just presents the keep/drop information while editing
54
- # the whitelist to supply users a list of databases they can whitelist by changing the
55
- # prefix to 'keep'. Everything prefixed 'drop' is not considered whitelisted and thus
56
- # not written to the whitelist file on disk.
60
+ warn_manual_allowlist = false
61
+ allowlisted_dbs.each do |db_name|
62
+ # Remove 'keep' word from allowlist entries. This is not normally required since geordi
63
+ # does not save 'keep' or 'drop' to the allowlist file on disk but rather saves a list
64
+ # of all allowlisted db names and just presents the keep/drop information while editing
65
+ # the allowlist to supply users a list of databases they can allowlist by changing the
66
+ # prefix to 'keep'. Everything prefixed 'drop' is not considered allowlisted and thus
67
+ # not written to the allowlist file on disk.
57
68
  #
58
- # However, if users manually edit their whitelist files they might use the keep/drop
69
+ # However, if users manually edit their allowlist files they might use the keep/drop
59
70
  # syntax they're familiar with.
60
71
  if db_name.start_with? 'keep '
61
72
  db_name.gsub!(/keep /, '')
62
73
  db_name = db_name.split[1..-1].join(' ')
63
- warn_manual_whitelist = true
74
+ warn_manual_allowlist = true
64
75
  end
65
76
  tmpfile_content.push(['keep', db_name]) unless db_name.empty?
66
77
  end
67
- if warn_manual_whitelist
78
+ if warn_manual_allowlist
68
79
  Interaction.warn <<~ERROR_MSG
69
- Your whitelist #{whitelist} seems to have been generated manually.
80
+ Your allowlist #{allowlist} seems to have been generated manually.
70
81
  In that case, make sure to use only one database name per line and omit the 'keep' prefix."
71
82
 
72
83
  Launching the editor.
@@ -81,29 +92,29 @@ module Geordi
81
92
  texteditor = Geordi::Util.decide_texteditor
82
93
  system("#{texteditor} #{tmp.path}")
83
94
  File.open(tmp.path, 'r') do |wl_edited|
84
- whitelisted_dbs = []
85
- whitelist_storage = File.open(whitelist, 'w')
95
+ allowlisted_dbs = []
96
+ allowlist_storage = File.open(allowlist, 'w')
86
97
  lines = Geordi::Util.stripped_lines(wl_edited.read)
87
98
  lines.each do |line|
88
99
  next if line.start_with?('#')
89
100
  unless line.split.length == 2
90
- Interaction.fail "Invalid edit to whitelist file: \`#{line}\` - Syntax is: ^[keep|drop] dbname$"
101
+ Interaction.fail "Invalid edit to allowlist file: \`#{line}\` - Syntax is: ^[keep|drop] dbname$"
91
102
  end
92
103
  unless %w[keep drop k d].include? line.split.first
93
- Interaction.fail "Invalid edit to whitelist file: \`#{line}\` - must start with either drop or keep."
104
+ Interaction.fail "Invalid edit to allowlist file: \`#{line}\` - must start with either drop or keep."
94
105
  end
95
106
  db_status, db_name = line.split
96
107
  if db_status == 'keep'
97
- whitelisted_dbs.push db_name
98
- whitelist_storage.write(db_name << "\n")
108
+ allowlisted_dbs.push db_name
109
+ allowlist_storage.write(db_name << "\n")
99
110
  end
100
111
  end
101
- whitelist_storage.close
112
+ allowlist_storage.close
102
113
  end
103
114
  end
104
115
 
105
116
  def decide_mysql_command(extra_flags)
106
- cmd = 'sudo mysql'
117
+ cmd = @sudo ? 'sudo mysql' : 'mysql'
107
118
  unless extra_flags.nil?
108
119
  if extra_flags.include? 'port'
109
120
  port = Integer(extra_flags.split('=')[1].split[0])
@@ -111,24 +122,27 @@ module Geordi
111
122
  end
112
123
  cmd << " #{extra_flags}"
113
124
  end
114
- Open3.popen3("#{cmd} -e 'QUIT'") do |_stdin, _stdout, stderr, thread|
115
- break if thread.value.exitstatus == 0
116
- # sudo mysql was not successful, switching to mysql-internal user management
117
- mysql_error = stderr.read.lines[0].chomp.strip.split[1]
118
- if %w[1045 1698].include? mysql_error # authentication failed
119
- cmd = 'mysql -uroot'
120
- cmd << " #{extra_flags}" unless extra_flags.nil?
121
- unless File.exist? File.join(Dir.home, '.my.cnf')
122
- puts "Please enter your MySQL/MariaDB password for account 'root'."
123
- Interaction.warn "You should create a ~/.my.cnf file instead, or you'll need to enter your MySQL root password for each db."
124
- Interaction.warn 'See https://makandracards.com/makandra/50813-store-mysql-passwords-for-development for more information.'
125
- cmd << ' -p' # need to ask for password now
126
- end
127
- Open3.popen3("#{cmd} -e 'QUIT'") do |_stdin_2, _stdout_2, _stderr_2, thread_2|
128
- Interaction.fail 'Could not connect to MySQL/MariaDB' unless thread_2.value.exitstatus == 0
125
+
126
+ if @sudo
127
+ Open3.popen3("#{cmd} -e 'QUIT'") do |_stdin, _stdout, stderr, thread|
128
+ break if thread.value.exitstatus == 0
129
+ # sudo mysql was not successful, switching to mysql-internal user management
130
+ mysql_error = stderr.read.lines[0].chomp.strip.split[1]
131
+ if %w[1045 1698].include? mysql_error # authentication failed
132
+ cmd = 'mysql -uroot'
133
+ cmd << " #{extra_flags}" unless extra_flags.nil?
134
+ unless File.exist? File.join(Dir.home, '.my.cnf')
135
+ Interaction.note "Please enter your MySQL/MariaDB password for account 'root'."
136
+ Interaction.warn "You should create a ~/.my.cnf file instead, or you'll need to enter your MySQL root password for each db."
137
+ Interaction.note 'See https://makandracards.com/makandra/50813-store-mysql-passwords-for-development for more information.'
138
+ cmd << ' -p' # need to ask for password now
139
+ end
140
+ Open3.popen3("#{cmd} -e 'QUIT'") do |_stdin_2, _stdout_2, _stderr_2, thread_2|
141
+ Interaction.fail 'Could not connect to MySQL/MariaDB' unless thread_2.value.exitstatus == 0
142
+ end
143
+ elsif mysql_error == '2013' # connection to port or socket failed
144
+ Interaction.fail 'MySQL/MariaDB connection failed, is this the correct port?'
129
145
  end
130
- elsif mysql_error == '2013' # connection to port or socket failed
131
- Interaction.fail 'MySQL/MariaDB connection failed, is this the correct port?'
132
146
  end
133
147
  end
134
148
  cmd
@@ -136,7 +150,7 @@ module Geordi
136
150
  private :decide_mysql_command
137
151
 
138
152
  def decide_postgres_command(extra_flags)
139
- cmd = 'sudo -u postgres psql'
153
+ cmd = @sudo ? 'sudo -u postgres psql' : 'psql'
140
154
  unless extra_flags.nil?
141
155
  begin
142
156
  port = Integer(extra_flags.split('=')[1])
@@ -157,28 +171,38 @@ module Geordi
157
171
  else
158
172
  list_all_mysql_dbs
159
173
  end
174
+ rescue DatabaseError
175
+ Interaction.fail 'Connection to database could not be established. Try running again with --sudo.'
160
176
  end
161
177
 
162
178
  def list_all_postgres_dbs
163
- `#{@postgres_command} -t -A -c 'SELECT DATNAME FROM pg_database WHERE datistemplate = false'`.split
179
+ output, _error, status = Open3.capture3("#{@postgres_command} -t -A -c 'SELECT DATNAME FROM pg_database WHERE datistemplate = false'")
180
+
181
+ raise DatabaseError unless status.success?
182
+
183
+ output.split
164
184
  end
165
185
 
166
186
  def list_all_mysql_dbs
167
187
  if @mysql_command.include? '-p'
168
- puts "Please enter your MySQL/MariaDB account 'root' for: list all databases"
188
+ Interaction.note "Please enter your MySQL/MariaDB account 'root' for: list all databases"
169
189
  end
170
- `#{@mysql_command} -B -N -e 'show databases'`.split
190
+ output, _error, status = Open3.capture3("#{@mysql_command} -B -N -e 'show databases'")
191
+
192
+ raise DatabaseError unless status.success?
193
+
194
+ output.split
171
195
  end
172
196
 
173
197
  def clean_mysql
174
198
  Interaction.announce 'Checking for MySQL databases'
175
199
  database_list = list_all_dbs('mysql')
176
- # confirm_deletion includes option for whitelist editing
200
+ # confirm_deletion includes option for allowlist editing
177
201
  deletable_dbs = confirm_deletion('mysql', database_list)
178
202
  return if deletable_dbs.nil?
179
203
  deletable_dbs.each do |db|
180
204
  if @mysql_command.include? '-p'
181
- puts "Please enter your MySQL/MariaDB account 'root' for: DROP DATABASE #{db}"
205
+ Interaction.note "Please enter your MySQL/MariaDB account 'root' for: DROP DATABASE #{db}"
182
206
  else
183
207
  puts "Dropping MySQL/MariaDB database #{db}"
184
208
  end
@@ -187,7 +211,7 @@ module Geordi
187
211
  end
188
212
 
189
213
  def clean_postgres
190
- Interaction.announce 'Checking for Postgres databases'
214
+ Interaction.announce 'Checking for PostgreSQL databases'
191
215
  database_list = list_all_dbs('postgres')
192
216
  deletable_dbs = confirm_deletion('postgres', database_list)
193
217
  return if deletable_dbs.nil?
@@ -197,36 +221,36 @@ module Geordi
197
221
  end
198
222
  end
199
223
 
200
- def whitelist_fname(dbtype)
201
- File.join(@whitelist_directory, dbtype) << '.txt'
224
+ def allowlist_fname(dbtype)
225
+ File.join(@allowlist_directory, dbtype) << '.txt'
202
226
  end
203
227
 
204
228
  def confirm_deletion(dbtype, database_list)
205
229
  proceed = ''
206
230
  until %w[y n].include? proceed
207
- deletable_dbs = filter_whitelisted(dbtype, database_list)
231
+ deletable_dbs = filter_allowlisted(dbtype, database_list)
208
232
  if deletable_dbs.empty?
209
- Interaction.note "No #{dbtype} databases found that were not whitelisted."
210
- if Interaction.prompt('Edit the whitelist? [y]es or [n]o') == 'y'
233
+ Interaction.note "No #{dbtype} databases found that were not allowlisted."
234
+ if Interaction.prompt('Edit the allowlist? [y]es or [n]o') == 'y'
211
235
  proceed = 'e'
212
236
  else
213
237
  return []
214
238
  end
215
239
  end
216
240
  if proceed.empty?
217
- Interaction.note "The following #{dbtype} databases are not whitelisted and can be deleted:"
241
+ Interaction.note "The following #{dbtype} databases are not allowlisted and can be deleted:"
218
242
  deletable_dbs.sort.each do |db|
219
243
  puts db
220
244
  end
221
- Interaction.note "These #{dbtype} databases are not whitelisted and can be deleted."
222
- proceed = Interaction.prompt('Proceed? [y]es, [n]o or [e]dit whitelist')
245
+ Interaction.note "These #{dbtype} databases are not allowlisted and can be deleted."
246
+ proceed = Interaction.prompt('Proceed? [y]es, [n]o or [e]dit allowlist')
223
247
  end
224
248
  case proceed
225
249
  when 'e'
226
250
  proceed = '' # reset user selection
227
- edit_whitelist dbtype
251
+ edit_allowlist dbtype
228
252
  when 'n'
229
- Interaction.success 'Nothing deleted.'
253
+ Interaction.note 'Nothing deleted.'
230
254
  return []
231
255
  when 'y'
232
256
  return deletable_dbs
@@ -243,31 +267,49 @@ module Geordi
243
267
  protected[dbtype].include? database_name
244
268
  end
245
269
 
246
- def is_whitelisted?(dbtype, database_name)
247
- whitelist_content = if File.exist? whitelist_fname(dbtype)
248
- Geordi::Util.stripped_lines(File.open(whitelist_fname(dbtype), 'r').read)
270
+ def is_allowlisted?(dbtype, database_name)
271
+ allowlist_content = if File.exist? allowlist_fname(dbtype)
272
+ Geordi::Util.stripped_lines(File.open(allowlist_fname(dbtype), 'r').read)
249
273
  else
250
274
  []
251
275
  end
252
- # Allow explicit whitelisting of derivative databases like projectname_test2
253
- if whitelist_content.include? database_name
276
+ # Allow explicit allowlisting of derivative databases like projectname_test2
277
+ if allowlist_content.include? database_name
254
278
  true
255
- # whitelisting `projectname` also whitelists `projectname_test\d*`, `projectname_development`
256
- elsif whitelist_content.include? database_name.sub(@derivative_dbname, '')
279
+ # allowlisting `projectname` also allowlists `projectname_test\d*`, `projectname_development`
280
+ elsif allowlist_content.include? database_name.sub(@derivative_dbname, '')
257
281
  true
258
282
  else
259
283
  false
260
284
  end
261
285
  end
262
286
 
263
- def filter_whitelisted(dbtype, database_list)
287
+ def filter_allowlisted(dbtype, database_list)
264
288
  # n.b. `delete` means 'delete from list of dbs that should be deleted in this context
265
289
  # i.e. `delete` means 'keep this database'
266
290
  deletable_dbs = database_list.dup
267
- deletable_dbs.delete_if { |db| is_whitelisted?(dbtype, db) if File.exist? whitelist_fname(dbtype) }
291
+ deletable_dbs.delete_if { |db| is_allowlisted?(dbtype, db) if File.exist? allowlist_fname(dbtype) }
268
292
  deletable_dbs.delete_if { |db| is_protected?(dbtype, db) }
269
293
  deletable_dbs.delete_if { |db| db.start_with? '#' }
270
294
  end
271
- private :filter_whitelisted
295
+ private :filter_allowlisted
296
+
297
+ def legacy_allowlist_directory
298
+ @legacy_allowlist_directory ||= File.join(@base_directory, '.config', 'geordi', 'whitelists')
299
+ end
300
+
301
+ def move_allowlist_files
302
+ %w[postgres mysql].each do |dbtype|
303
+ new_path = allowlist_fname(dbtype)
304
+ next if File.exist?(new_path)
305
+
306
+ legacy_path = File.join(legacy_allowlist_directory, dbtype) << '.txt'
307
+ FileUtils.mv(legacy_path, new_path)
308
+
309
+ if Dir.exist?(legacy_allowlist_directory) && Dir.empty?(legacy_allowlist_directory)
310
+ Dir.delete(legacy_allowlist_directory)
311
+ end
312
+ end
313
+ end
272
314
  end
273
315
  end
@@ -62,12 +62,16 @@ module Geordi
62
62
 
63
63
  def postgresql_command
64
64
  ENV['PGPASSWORD'] = config['password']
65
- command = 'pg_restore --no-owner --clean --no-acl'
66
- command << ' --username=' << config['username'].to_s if config['username']
67
- command << ' --port=' << config['port'].to_s if config['port']
68
- command << ' --host=' << config['host'].to_s if config['host']
69
- command << ' --dbname=' << config['database'].to_s
70
- command << ' ' << dump_file
65
+ drop_command = 'dropdb --if-exists ' << config['database'].to_s
66
+ create_command = 'createdb ' << config['database'].to_s
67
+ restore_command = 'pg_restore --no-owner --no-acl'
68
+ restore_command << ' --username=' << config['username'].to_s if config['username']
69
+ restore_command << ' --port=' << config['port'].to_s if config['port']
70
+ restore_command << ' --host=' << config['host'].to_s if config['host']
71
+ restore_command << ' --dbname=' << config['database'].to_s
72
+ restore_command << ' ' << dump_file
73
+
74
+ drop_command + ' && ' + create_command + ' && ' + restore_command
71
75
  end
72
76
 
73
77
  def dump_file
data/lib/geordi/hint.rb CHANGED
@@ -14,7 +14,7 @@ module Geordi
14
14
  if generated_hints.any? && should_print_hint
15
15
  puts
16
16
  puts generated_hints.sample
17
- puts 'You can configure the probability for these hints by setting hint_probability to a unitless percent number in ~/.config/geordi/global.yml' unless settings_probability
17
+ puts "You can configure the probability for these hints by setting hint_probability to a unitless percent number in #{Settings::GLOBAL_SETTINGS_FILE_NAME}" unless settings_probability
18
18
  end
19
19
 
20
20
  generated_hints
@@ -8,7 +8,15 @@ module Geordi
8
8
  GLOBAL_SETTINGS_FILE_NAME = Util.testing? ? './tmp/global_settings.yml'.freeze : File.join(ENV['HOME'], '.config/geordi/global.yml').freeze
9
9
  LOCAL_SETTINGS_FILE_NAME = Util.testing? ? './tmp/local_settings.yml'.freeze : './.geordi.yml'.freeze
10
10
 
11
- ALLOWED_GLOBAL_SETTINGS = %w[ pivotal_tracker_api_key auto_update_chromedriver pivotal_tracker_project_ids git_initials hint_probability ].freeze
11
+ ALLOWED_GLOBAL_SETTINGS = %w[
12
+ auto_update_chromedriver
13
+ git_initials
14
+ hint_probability
15
+ irb_flags
16
+ pivotal_tracker_api_key
17
+ pivotal_tracker_project_ids
18
+ ].freeze
19
+
12
20
  ALLOWED_LOCAL_SETTINGS = %w[ pivotal_tracker_project_ids ].freeze
13
21
 
14
22
  SETTINGS_WARNED = 'GEORDI_INVALID_SETTINGS_WARNED'
@@ -18,6 +26,10 @@ module Geordi
18
26
  end
19
27
 
20
28
  # Global settings
29
+ def irb_flags
30
+ @global_settings['irb_flags']
31
+ end
32
+
21
33
  def pivotal_tracker_api_key
22
34
  @global_settings['pivotal_tracker_api_key'] || gitpt_api_key_old || inquire_pt_api_key
23
35
  end
data/lib/geordi/util.rb CHANGED
@@ -37,7 +37,8 @@ module Geordi
37
37
  # show_cmd: Whether to print the command
38
38
  # confirm: Whether to ask for confirmation before running it
39
39
  # fail_message: The text to print on command failure
40
- def run!(command, show_cmd: false, confirm: false, fail_message: 'Something went wrong.')
40
+ # exec: Whether to run the command with `exec` instead of `system`
41
+ def run!(command, show_cmd: false, confirm: false, fail_message: 'Something went wrong.', exec: false)
41
42
  # Disable shell features for arrays https://stackoverflow.com/questions/13338147/ruby-system-method-arguments
42
43
  # Conversion: ['ls *', 'some arg'] => ['ls', '*', 'some arg']
43
44
  # If you need shell features, you need to pass in a String instead of an array.
@@ -60,19 +61,23 @@ module Geordi
60
61
 
61
62
  if testing?
62
63
  # Join with commas for precise argument distinction
63
- puts "Util.run! #{show_command.join(', ')}"
64
+ puts "Util.run!#{' (exec)' if exec} #{show_command.join(', ')}"
64
65
  else
66
+ method = exec ? :exec : :system
67
+
65
68
  # Remove Geordi's Bundler environment when running commands.
66
69
  success = if !defined?(Bundler)
67
- system(*command)
70
+ Kernel.public_send(method, *command)
68
71
  elsif Gem::Version.new(Bundler::VERSION) >= Gem::Version.new('1.17.3')
69
72
  Bundler.with_original_env do
70
- system(*command)
73
+ Kernel.public_send(method, *command)
71
74
  end
72
75
  else
73
- Bundler.clean_system(*command)
76
+ method = exec ? :clean_exec : :clean_system
77
+ Bundler.public_send(method, *command)
74
78
  end
75
79
 
80
+ # This part will never be reached when `exec` is true
76
81
  success || Interaction.fail(fail_message)
77
82
  end
78
83
  end
@@ -89,14 +94,15 @@ module Geordi
89
94
  elsif gem_major_version('rails') == 3
90
95
  "#{binstub_or_fallback('rails')} console #{environment}"
91
96
  else
92
- "#{binstub_or_fallback('rails')} console -e #{environment} #{use_multiline_flag}"
93
- end
94
- end
97
+ use_multiline = if irb_version >= Gem::Version.new('1.2') && ruby_version < Gem::Version.new('3.0')
98
+ Interaction.note 'Using --nomultiline switch for faster pasting'
99
+ '--nomultiline'
100
+ end
101
+
102
+ irb_flags = [use_multiline, Settings.new.irb_flags].join(' ').strip
103
+ irb_flags.prepend('-- ') unless irb_flags.empty?
95
104
 
96
- def use_multiline_flag
97
- if irb_version >= Gem::Version.new('1.2') && ruby_version < Gem::Version.new('3.0')
98
- Interaction.note 'Using --nomultiline switch for faster pasting'
99
- '-- --nomultiline'
105
+ "#{binstub_or_fallback('rails')} console -e #{environment} #{irb_flags}"
100
106
  end
101
107
  end
102
108
 
@@ -193,7 +199,7 @@ module Geordi
193
199
  version_string = if testing?
194
200
  ENV['GEORDI_TESTING_IRB_VERSION']
195
201
  else
196
- `irb --version`[/irb (\d\.\d\.\d)/, 1]
202
+ `irb --version`[/irb (\d+\.\d+\.\d+)/, 1]
197
203
  end
198
204
 
199
205
  Gem::Version.new(version_string)
@@ -1,3 +1,3 @@
1
1
  module Geordi
2
- VERSION = '9.6.1'.freeze
2
+ VERSION = '10.0.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: geordi
3
3
  version: !ruby/object:Gem::Version
4
- version: 9.6.1
4
+ version: 10.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Henning Koch
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-09-22 00:00:00.000000000 Z
11
+ date: 2024-03-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -117,7 +117,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
117
117
  - !ruby/object:Gem::Version
118
118
  version: '0'
119
119
  requirements: []
120
- rubygems_version: 3.1.6
120
+ rubygems_version: 3.0.3
121
121
  signing_key:
122
122
  specification_version: 4
123
123
  summary: Collection of command line tools we use in our daily work with Ruby, Rails