Class: Geordi::Util

Inherits:
Object
  • Object
show all
Defined in:
lib/geordi/util.rb

Class Method Summary collapse

Class Method Details

.binstub_or_fallback(executable) ⇒ Object



84
85
86
87
88
# File 'lib/geordi/util.rb', line 84

def binstub_or_fallback(executable)
  binstub_file = "bin/#{executable}"

  File.exist?(binstub_file) ? binstub_file : "bundle exec #{executable}"
end

.cmd_exists?(cmd) ⇒ Boolean

check if given cmd is executable. Absolute path or command in $PATH allowed.

Returns:

  • (Boolean)


154
155
156
157
# File 'lib/geordi/util.rb', line 154

def cmd_exists?(cmd)
  system("which #{cmd} > /dev/null")
  $?.exitstatus.zero?
end

.console_command(environment) ⇒ Object



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/geordi/util.rb', line 90

def console_command(environment)
  if gem_major_version('rails') == 2
    "script/console #{environment}"
  elsif gem_major_version('rails') == 3
    "#{binstub_or_fallback('rails')} console #{environment}"
  else

    irb_flags_from_config, source = Settings.new.irb_flags
    irb_flags = ""

    if irb_version >= Gem::Version.new('1.2') && ruby_version < Gem::Version.new('3.0') && (source != :local)
      Interaction.note 'Using --nomultiline switch for faster pasting'
      irb_flags << '--nomultiline '
    end

    irb_flags << irb_flags_from_config if irb_flags_from_config

    irb_flags.prepend('-- ').strip! unless irb_flags.empty?

    "#{binstub_or_fallback('rails')} console -e #{environment} #{irb_flags}"
  end
end

.cucumber_path?(path) ⇒ Boolean

Returns:

  • (Boolean)


215
216
217
# File 'lib/geordi/util.rb', line 215

def cucumber_path?(path)
  %r{(^|\/)features|\.feature($|:)}.match?(path)
end

.deploy_targetsObject



137
138
139
140
141
# File 'lib/geordi/util.rb', line 137

def deploy_targets
  Dir['config/deploy/*'].map do |f|
    File.basename f, '.rb' # Filename without .rb extension
  end
end

.determine_issues_to_move(source_branch, target_branch, target_stage, linear_client: LinearClient.new) ⇒ Object



223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/geordi/util.rb', line 223

def determine_issues_to_move(source_branch, target_branch, target_stage, linear_client: LinearClient.new)
  commit_messages = []
  if CapistranoConfig.task_defined?('app:revision')
    revision = CapistranoConfig.current_app_revisions(target_stage)

    if revision.empty?
      Interaction.fail 'Could not read a server revision from app:revision output.'
    elsif revision.size > 1
      Interaction.warn 'Currently deployed revision differs on servers.'
      Interaction.confirm_or_cancel('Continue deployment without moving Linear issues?')
    else
      # there is one consistent revision deployed across all target servers => Linear issues can be moved
      commit_messages = Git.commits_between(revision.first, source_branch)
    end
  else
    Interaction.warn 'Missing Capistrano task "app:revision". See `geordi help deploy`.'
    puts 'Without it, Geordi can only consider pushed commits for moving Linear issues.'
    commit_messages = Git.commits_between("origin/#{target_branch}", source_branch)
  end

  linear_issue_ids = LinearClient.extract_issue_ids(commit_messages)
  linear_issue_ids = linear_client.filter_existing_issue_ids(linear_issue_ids)
  relevant_commits = LinearClient.filter_by_issue_ids(commit_messages, linear_issue_ids)
  [linear_issue_ids, relevant_commits]
end

.dumple_command(environment, options) ⇒ Object



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/geordi/util.rb', line 121

def dumple_command(environment, options)
  compress = if options[:compress] == 'compress'
    '--compress'
  elsif options[:compress]
    "--compress=#{options[:compress]}"
  end

  cmd = ['dumple']
  cmd << environment
  cmd << options[:database]
  cmd << compress
  cmd << '--for_download' if options[:for_download]

  cmd.compact.join(' ')
end

.file_containing?(file, regex) ⇒ Boolean

Returns:

  • (Boolean)


192
193
194
# File 'lib/geordi/util.rb', line 192

def file_containing?(file, regex)
  File.exist?(file) && File.read(file).scan(regex).any?
end

.gem_available?(gem) ⇒ Boolean

Returns:

  • (Boolean)


172
173
174
# File 'lib/geordi/util.rb', line 172

def gem_available?(gem)
  !!gem_version(gem)
end

.gem_major_version(gem) ⇒ Object

Get the major version or for the given gem by parsing the Gemfile.lock. Returns nil if the gem is not used.



178
179
180
181
# File 'lib/geordi/util.rb', line 178

def gem_major_version(gem)
  gem_version = gem_version(gem)
  gem_version && gem_version.segments[0]
end

.gem_version(gem) ⇒ Object

Get the version for the given gem by parsing Gemfile.lock. Returns nil if the gem is not used.



185
186
187
188
189
190
# File 'lib/geordi/util.rb', line 185

def gem_version(gem)
  lock_file = Bundler::LockfileParser.new(Bundler.read_file(Bundler.default_lockfile))
  spec = lock_file.specs.detect { |spec| spec.name == gem }

  spec && spec.version
end

.get_texteditorObject



143
144
145
146
147
148
149
150
151
# File 'lib/geordi/util.rb', line 143

def get_texteditor
  %w[/usr/bin/editor vi].find { |c| cmd_exists?(c) }.tap do |editor|
    if editor == '/usr/bin/editor'
      Interaction.note 'Choose text editor with: sudo update-alternatives --config editor'
    elsif editor == nil
      Interaction.fail 'Could not find a text editor.'
    end
  end
end

.installing_missing_gemsObject

Geordi commands sometimes require external gems. However, we don't want all employed gems as runtime dependencies because that would unnecessarily slow down all commands. Thus, we have this handy method here.



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/geordi/util.rb', line 13

def installing_missing_gems
  yield
rescue LoadError => error
  error.message =~ /-- (\S+)\Z/
  Regexp.last_match(1) || raise # could not extract a gem name from the error message, re-raise the error

  gem_name = Regexp.last_match(1).strip.split('/').first
  install_command = 'gem install ' + gem_name

  # install missing gem
  Interaction.warn 'Probably missing gem: ' + gem_name
  Interaction.confirm_or_cancel('Install it now?', 'Missing Gems.')
  Util.run!(install_command, show_cmd: true)

  # retry
  Gem.clear_paths
  Interaction.note 'Retrying ...'
  require gem_name
  retry
end

.irb_versionObject



200
201
202
203
204
205
206
207
208
# File 'lib/geordi/util.rb', line 200

def irb_version
  version_string = if testing?
    ENV['GEORDI_TESTING_IRB_VERSION']
  else
    `irb --version`[/irb (\d+\.\d+\.\d+)/, 1]
  end

  Gem::Version.new(version_string)
end

.is_port_open?(port) ⇒ Boolean

Returns:

  • (Boolean)


159
160
161
162
163
164
165
# File 'lib/geordi/util.rb', line 159

def is_port_open?(port)
  socket = TCPSocket.new('127.0.0.1', port)
  socket.close
  true
rescue Errno::ECONNREFUSED
  false
end

.rspec_path?(path) ⇒ Boolean

Returns:

  • (Boolean)


219
220
221
# File 'lib/geordi/util.rb', line 219

def rspec_path?(path)
  %r{(^|\/)spec|_spec\.rb($|:)}.match?(path)
end

.ruby_versionObject



210
211
212
213
# File 'lib/geordi/util.rb', line 210

def ruby_version
  version_string = testing? ? ENV['GEORDI_TESTING_RUBY_VERSION'] : RUBY_VERSION
  Gem::Version.new(version_string)
end

.run!(command, show_cmd: false, confirm: false, fail_message: 'Something went wrong.', exec: false) ⇒ Object

Run a command with a clean environment. Print an error message and exit if the command fails.

show_cmd: Whether to print the command confirm: Whether to ask for confirmation before running it fail_message: The text to print on command failure exec: Whether to run the command with exec instead of system



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/geordi/util.rb', line 41

def run!(command, show_cmd: false, confirm: false, fail_message: 'Something went wrong.', exec: false)
  # Disable shell features for arrays https://stackoverflow.com/questions/13338147/ruby-system-method-arguments
  # Conversion: ['ls *', 'some arg'] => ['ls', '*', 'some arg']
  # If you need shell features, you need to pass in a String instead of an array.
  if command.is_a?(Array)
    real_command, *arguments = *command
    command = [real_command.split(' '), arguments].flatten
    show_command = command
  else
    show_command = [command]
  end

  if show_cmd || confirm
    Interaction.note_cmd show_command.join(' ')
  end

  if confirm
    Interaction.confirm_or_cancel('Run this now?')
  end

  if testing?
    # Join with commas for precise argument distinction
    puts "Util.run!#{' (exec)' if exec} #{show_command.join(', ')}"
  else
    method = exec ? :exec : :system

    # Remove Geordi's Bundler environment when running commands.
    success = if !defined?(Bundler)
      Kernel.public_send(method, *command)
    elsif Gem::Version.new(Bundler::VERSION) >= Gem::Version.new('1.17.3')
      Bundler.with_original_env do
        Kernel.public_send(method, *command)
      end
    else
      method = exec ? :clean_exec : :clean_system
      Bundler.public_send(method, *command)
    end

    # This part will never be reached when `exec` is true
    success || Interaction.fail(fail_message)
  end
end

.server_commandObject



113
114
115
116
117
118
119
# File 'lib/geordi/util.rb', line 113

def server_command
  if gem_major_version('rails') == 2
    'script/server ""'
  else
    "#{binstub_or_fallback('rails')} server"
  end
end

.stripped_lines(input_string) ⇒ Object

splint lines e.g. read from a file into lines and clean those up



168
169
170
# File 'lib/geordi/util.rb', line 168

def stripped_lines(input_string)
  input_string.lines.map(&:chomp).map(&:strip)
end

.testing?Boolean

Returns:

  • (Boolean)


196
197
198
# File 'lib/geordi/util.rb', line 196

def testing?
  !!ENV['GEORDI_TESTING']
end