geordi 5.2.2 → 6.0.0.pre.rc1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/geordi/docker.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'geordi/interaction'
2
2
  require 'geordi/cucumber'
3
3
  require 'yaml'
4
+ require 'open3'
4
5
 
5
6
  module Geordi
6
7
  class Docker
@@ -28,13 +29,61 @@ module Geordi
28
29
  end
29
30
 
30
31
  def vnc
31
- Cucumber.new.launch_vnc_viewer('::5967')
32
+ check_installation_and_config
33
+ launch_vnc_viewer('::5967')
34
+ end
35
+
36
+ def setup_vnc
37
+ `clear`
38
+ Interaction.note 'This script will help you install a VNC viewer.'
39
+ Interaction.note 'Please open a second shell to execute instructions.'
40
+ Interaction.prompt 'Continue ...'
41
+
42
+ Interaction.announce 'Setup VNC viewer'
43
+ vnc_viewer_installed = system('which vncviewer > /dev/null 2>&1')
44
+ if vnc_viewer_installed
45
+ Interaction.success 'It appears you already have a VNC viewer installed. Good job!'
46
+ else
47
+ puts 'Please run:'
48
+ Interaction.note_cmd 'sudo apt-get install xtightvncviewer'
49
+ Interaction.prompt 'Continue ...'
50
+ end
51
+
52
+ puts
53
+ puts Util.strip_heredoc <<-TEXT
54
+ Done. You can view the VNC window with `geordi docker vnc`.
55
+ TEXT
56
+
57
+ Interaction.success 'Happy cuking!'
32
58
  end
33
59
 
34
60
  private
35
61
 
62
+ def launch_vnc_viewer(source)
63
+ fail('VNC viewer not found. Install it with `geordi docker vnc --setup`.') unless command_exists?('vncviewer')
64
+
65
+ fork do
66
+ error = capture_stderr do
67
+ system("vncviewer #{source}")
68
+ end
69
+ unless $?.success?
70
+ if $?.exitstatus == 127
71
+ fail('VNC viewer not found. Install it with `geordi docker vnc --setup`.')
72
+ else
73
+ fail("VNC viewer could not be opened: #{error}")
74
+ end
75
+ end
76
+ end
77
+ exit 0
78
+ end
79
+
36
80
  def attach_to_running_shell
37
- running_containers = execute(:`, 'docker-compose ps').split("\n")
81
+ # The command line output of docker-compose ps changes depending on the container name length, this is
82
+ # caused by the varying terminal length and results in the longer outputs, e.g the container name and id
83
+ # to be cut after x characters and the rest being placed in the line below.
84
+
85
+ stdout_str, _error_str = execute(:capture, {'COLUMNS' => '400'}, 'docker-compose ps')
86
+ running_containers = stdout_str.split("\n")
38
87
  if (main_container_line = running_containers.grep(/_main_run/).first)
39
88
  container_name = main_container_line.split(' ').first
40
89
  execute(:exec, 'docker', 'exec', '-it', container_name, 'bash')
@@ -54,13 +103,17 @@ module Geordi
54
103
  def execute(kind, *args)
55
104
  if ENV['GEORDI_TESTING']
56
105
  puts "Stubbed run #{args.join(' ')}"
57
- if kind == :`
106
+ if kind == :` || kind == :capture
58
107
  mock_parse(*args)
59
108
  else
60
109
  mock_run(*args)
61
110
  end
62
111
  else
63
- send(kind, *args)
112
+ if kind == :capture
113
+ Open3.capture2(*args)
114
+ else
115
+ send(kind, *args)
116
+ end
64
117
  end
65
118
  end
66
119
 
@@ -112,5 +165,19 @@ module Geordi
112
165
  []
113
166
  end
114
167
  end
168
+
169
+ def capture_stderr
170
+ old_stderr = $stderr.dup
171
+ io = Tempfile.new('cuc')
172
+ $stderr.reopen(io)
173
+ yield
174
+ io.rewind
175
+ io.read
176
+ ensure
177
+ io.close
178
+ io.unlink
179
+ $stderr.reopen(old_stderr)
180
+ end
181
+
115
182
  end
116
183
  end
@@ -57,7 +57,7 @@ module Geordi
57
57
  Interaction.note 'Source file: ' + dump_file
58
58
 
59
59
  source_command = send("#{config['adapter']}_command")
60
- Util.run! source_command, fail_message: "An error occured loading #{File.basename(dump_file)}"
60
+ Util.run! source_command, fail_message: "An error occurred loading #{File.basename(dump_file)}"
61
61
  end
62
62
 
63
63
  end
data/lib/geordi/gitpt.rb CHANGED
@@ -1,99 +1,185 @@
1
- class Gitpt
2
- require 'yaml'
3
- require 'highline'
4
- require 'tracker_api'
5
-
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__)
9
-
10
- def initialize
11
- self.highline = HighLine.new
12
- self.settings = Geordi::Settings.new
13
- self.client = build_client
14
- end
1
+ require 'yaml'
2
+ require 'highline'
3
+ require 'tracker_api'
4
+
5
+ module Geordi
6
+ class Gitpt
7
+
8
+ # This require-style is to prevent Ruby from loading files of a different
9
+ # version of Geordi.
10
+ require File.expand_path('settings', __dir__)
15
11
 
16
- def run(git_args)
17
- Geordi::Interaction.warn <<-WARNING unless Geordi::Util.staged_changes?
12
+ def initialize
13
+ self.highline = HighLine.new
14
+ self.settings = Settings.new
15
+ self.client = build_client
16
+ end
17
+
18
+ def run_commit(git_args)
19
+ Interaction.warn <<-WARNING unless Util.staged_changes?
18
20
  No staged changes. Will create an empty commit.
19
- WARNING
21
+ WARNING
20
22
 
21
- story = choose_story
22
- if story
23
- create_commit "[##{story.id}] #{story.name}", *git_args
23
+ story = choose_story
24
+ if story
25
+ create_commit "[##{story.id}] #{story.name}", *git_args
26
+ end
24
27
  end
25
- end
26
28
 
27
- private
29
+ def run_branch
30
+ story = choose_story || Interaction.fail('No story selected.')
28
31
 
29
- attr_accessor :highline, :client, :settings
32
+ normalized_story_name = normalize_string(story.name)
30
33
 
31
- def build_client
32
- TrackerApi::Client.new(token: settings.pivotal_tracker_api_key)
33
- end
34
+ branch_list_string = if Util.testing?
35
+ ENV['GEORDI_TESTING_GIT_BRANCHES'] || ''
36
+ else
37
+ `git branch --format="%(refname:short)"`
38
+ end
34
39
 
35
- def load_projects
36
- project_ids = settings.pivotal_tracker_project_ids
37
- project_ids.collect { |project_id| client.project(project_id) }
38
- end
40
+ if branch_list_string.nil? || branch_list_string.strip.empty?
41
+ Interaction.fail 'Could not determine local git branches.'
42
+ end
39
43
 
40
- def applicable_stories
41
- projects = load_projects
42
- projects.collect do |project|
43
- project.stories(filter: 'state:started,finished,rejected')
44
- end.flatten
45
- end
44
+ new_branch_name = "#{git_user_initials}/#{normalized_story_name}-#{story.id}"
46
45
 
47
- def choose_story
48
- if Geordi::Util.testing?
49
- return OpenStruct.new(id: 12, name: 'Test Story')
46
+ local_branches = branch_list_string.split("\n")
47
+ branch_name = local_branches.find { |branch_name| branch_name == new_branch_name }
48
+ branch_name ||= local_branches.find { |branch_name| branch_name.include? story.id.to_s }
49
+
50
+ if branch_name.present?
51
+ checkout_branch branch_name, new_branch: false
52
+ else
53
+ checkout_branch new_branch_name, new_branch: true
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ attr_accessor :highline, :client, :settings
60
+
61
+ def build_client
62
+ TrackerApi::Client.new(token: settings.pivotal_tracker_api_key)
50
63
  end
51
64
 
52
- loading_message = 'Connecting to Pivotal Tracker ...'
53
- print(loading_message)
54
- stories = applicable_stories
55
- reset_loading_message = "\r#{' ' * (loading_message.length + stories.length)}\r"
65
+ def load_projects
66
+ project_ids = settings.pivotal_tracker_project_ids
67
+ project_ids.collect do |project_id|
68
+ begin
69
+ client.project(project_id)
70
+ rescue TrackerApi::Errors::ClientError
71
+ puts # Start a new line
72
+ Geordi::Interaction.warn "Could not access project #{project_id}. Skipping."
73
+ end
74
+ end.compact
75
+ end
76
+
77
+ def applicable_stories
78
+ if Util.testing?
79
+ return ENV['GEORDI_TESTING_NO_PT_STORIES'] == 'true' ? [] : [OpenStruct.new(id: 12, name: 'Test Story')]
80
+ end
56
81
 
57
- highline.choose do |menu|
58
- menu.header = 'Choose a story'
82
+ projects = load_projects
83
+ projects.collect do |project|
84
+ project.stories(filter: 'state:started,finished,rejected', fields: ':default,owners(id,name)')
85
+ end.flatten
86
+ end
59
87
 
60
- stories.each do |story|
61
- print '.' # Progress
88
+ def choose_story
89
+ loading_message = 'Connecting to Pivotal Tracker ...'
90
+ print(loading_message)
91
+ stories = applicable_stories
92
+ reset_loading_message = "\r#{' ' * (loading_message.length + stories.length)}\r"
62
93
 
63
- state = story.current_state
64
- owners = story.owners
65
- owner_is_me = owners.collect(&:id).include?(client.me.id)
94
+ Geordi::Interaction.fail('No stories to offer.') if stories.empty?
66
95
 
67
- if state == 'started'
68
- state = HighLine::GREEN + state + HighLine::RESET
69
- elsif state != 'finished'
70
- state = HighLine::RED + state + HighLine::RESET
96
+ if Util.testing?
97
+ return stories[0]
98
+ end
99
+
100
+ my_id = client.me.id
101
+
102
+ highline.choose do |menu|
103
+ menu.header = 'Choose a story'
104
+
105
+ stories.each do |story|
106
+ print '.' # Progress
107
+
108
+ state = story.current_state
109
+ owners = story.owners
110
+ owner_is_me = owners.collect(&:id).include?(my_id)
111
+
112
+ if state == 'started'
113
+ state = HighLine::GREEN + state + HighLine::RESET
114
+ elsif state != 'finished'
115
+ state = HighLine::RED + state + HighLine::RESET
116
+ end
117
+
118
+ state += HighLine::BOLD if owner_is_me
119
+
120
+ label = "(#{owners.collect(&:name).join(', ')}, #{state}) #{story.name}"
121
+ label = bold(label) if owner_is_me
122
+
123
+ menu.choice(label) { return story }
71
124
  end
72
125
 
73
- state += HighLine::BOLD if owner_is_me
126
+ menu.hidden ''
127
+ print reset_loading_message # Once menu is build
128
+ end
129
+
130
+ nil # Return nothing
131
+ end
74
132
 
75
- label = "(#{owners.collect(&:name).join(', ')}, #{state}) #{story.name}"
76
- label = bold(label) if owner_is_me
133
+ def create_commit(message, *git_args)
134
+ extra = highline.ask("\nAdd an optional message").strip
135
+ message << ' - ' << extra if extra != ''
77
136
 
78
- menu.choice(label) { return story }
137
+ Util.run!(['git', 'commit', '--allow-empty', '-m', message, *git_args])
138
+ end
139
+
140
+ def bold(string)
141
+ HighLine::BOLD + string + HighLine::RESET
142
+ end
143
+
144
+ def checkout_branch(name, new_branch: false)
145
+ if new_branch
146
+ Util.run! ['git', 'checkout', 'master']
147
+ Util.run! ['git', 'checkout', '-b', name]
148
+ else
149
+ Util.run! ['git', 'checkout', name]
79
150
  end
151
+ end
80
152
 
81
- menu.hidden ''
82
- print reset_loading_message # Once menu is build
153
+ def normalize_string(name)
154
+ name.gsub!('ä', 'ae')
155
+ name.gsub!('ö', 'oe')
156
+ name.gsub!('ü', 'ue')
157
+ name.gsub!('ß', 'ss')
158
+ name.tr!('^A-Za-z0-9_ ', '')
159
+ name.squeeze! ' '
160
+ name.gsub!(' ', '-')
161
+ name.downcase!
162
+ name
83
163
  end
84
164
 
85
- nil # Return nothing
86
- end
165
+ def git_user_initials
166
+ stdout_str = if Util.testing?
167
+ ENV['GEORDI_TESTING_GIT_USERNAME']
168
+ else
169
+ `git config user.name`
170
+ end
87
171
 
88
- def create_commit(message, *git_args)
89
- extra = highline.ask("\nAdd an optional message").strip
90
- message << ' - ' << extra if extra != ''
172
+ git_user_initials = unless stdout_str.nil?
173
+ stdout_str.strip.split(' ').map(&:chars).map(&:first).join.downcase
174
+ end
91
175
 
92
- Geordi::Util.run!(['git', 'commit', '--allow-empty', '-m', message, *git_args])
93
- end
176
+ git_user_initials = Interaction.prompt 'Enter your initals:', git_user_initials
94
177
 
95
- def bold(string)
96
- HighLine::BOLD + string + HighLine::RESET
178
+ if git_user_initials.nil?
179
+ Interaction.fail('Could not determine the git user\'s initials.')
180
+ else
181
+ git_user_initials
182
+ end
183
+ end
97
184
  end
98
-
99
185
  end
@@ -11,8 +11,9 @@ module Geordi
11
11
  puts "\e[4;34m#{message}\e[0m" # blue underline
12
12
  end
13
13
 
14
- # Any hints, comments, infos or explanations should be `note`d. Please do
15
- # not print any output (data, file contents, lists) with `note`.
14
+ # Any meta information, i.e. hints, comments, infos or explanations should
15
+ # be printed with `note`.
16
+ # Please do not use it for command output (data, file contents, lists etc).
16
17
  def note(text)
17
18
  puts '> ' + text
18
19
  end
@@ -38,7 +39,8 @@ module Geordi
38
39
  exit(1)
39
40
  end
40
41
 
41
- # When you're done, inform the user with a `success` and a short message
42
+ # When you're done, inform the user with a `success` and a short message. It
43
+ # should be a sentence (i.e. ending with [.!?]).
42
44
  def success(text)
43
45
  message = "\n> #{text}"
44
46
  puts "\e[32m#{message}\e[0m" # green
data/lib/geordi/remote.rb CHANGED
@@ -3,6 +3,7 @@ require 'geordi/interaction'
3
3
  require 'geordi/util'
4
4
  require 'highline/import'
5
5
  require 'pathname'
6
+ require 'fileutils'
6
7
 
7
8
  module Geordi
8
9
  class Remote
@@ -59,7 +60,7 @@ module Geordi
59
60
  server_option = options[:select_server]
60
61
  server_number = server_option.to_i
61
62
 
62
- server = if server_option == 'select_server'
63
+ server = if server_option == 'select_server'
63
64
  select_server
64
65
  elsif server_number != 0 && server_number <= @config.servers.count
65
66
  server_index = server_number - 1
@@ -8,8 +8,10 @@ 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 ].freeze
12
- ALLOWED_LOCAL_SETTINGS = %w[ use_vnc pivotal_tracker_project_ids ].freeze
11
+ ALLOWED_GLOBAL_SETTINGS = %w[ pivotal_tracker_api_key auto_update_chromedriver pivotal_tracker_project_ids ].freeze
12
+ ALLOWED_LOCAL_SETTINGS = %w[ pivotal_tracker_project_ids ].freeze
13
+
14
+ SETTINGS_WARNED = 'GEORDI_INVALID_SETTINGS_WARNED'
13
15
 
14
16
  def initialize
15
17
  read_settings
@@ -34,26 +36,14 @@ module Geordi
34
36
  save_global_settings
35
37
  end
36
38
 
37
- # Local settings
38
- # They should not be changed by geordi to avoid unexpected diffs, therefore
39
- # there are no setters for these settings
40
- def use_vnc?
41
- @local_settings.fetch('use_vnc', true)
42
- end
43
-
44
39
  def pivotal_tracker_project_ids
45
- project_ids = @local_settings['pivotal_tracker_project_ids'] || pt_project_ids_old
40
+ local_project_ids = @local_settings['pivotal_tracker_project_ids'] || pt_project_ids_old
41
+ global_project_ids = @global_settings['pivotal_tracker_project_ids']
46
42
 
47
- case project_ids
48
- when Array
49
- # nothing to do
50
- when String
51
- project_ids = project_ids.split(/[\s]+/).map(&:to_i)
52
- when Integer
53
- project_ids = [project_ids]
54
- else
55
- project_ids = []
56
- end
43
+ local_project_ids = array_wrap_project_ids(local_project_ids)
44
+ global_project_ids = array_wrap_project_ids(global_project_ids)
45
+
46
+ project_ids = local_project_ids | global_project_ids
57
47
 
58
48
  if project_ids.empty?
59
49
  puts
@@ -78,14 +68,20 @@ module Geordi
78
68
  global_path = GLOBAL_SETTINGS_FILE_NAME
79
69
  local_path = LOCAL_SETTINGS_FILE_NAME
80
70
 
81
- if File.exists?(global_path)
82
- global_settings = YAML.safe_load(File.read(global_path))
83
- check_for_invalid_keys(global_settings, ALLOWED_GLOBAL_SETTINGS, global_path)
71
+ global_settings = if File.exists?(global_path)
72
+ YAML.safe_load(File.read(global_path))
73
+ end
74
+ local_settings = if File.exists?(local_path)
75
+ YAML.safe_load(File.read(local_path))
84
76
  end
85
77
 
86
- if File.exists?(local_path)
87
- local_settings = YAML.safe_load(File.read(local_path))
78
+ # Prevent duplicate warnings caused by another instance of Settings
79
+ unless ENV[SETTINGS_WARNED]
80
+ check_for_invalid_keys(global_settings, ALLOWED_GLOBAL_SETTINGS, global_path)
88
81
  check_for_invalid_keys(local_settings, ALLOWED_LOCAL_SETTINGS, local_path)
82
+ Interaction.warn "Unsupported config file \".firefox-version\". Please remove it." if File.exists?('.firefox-version')
83
+
84
+ ENV[SETTINGS_WARNED] = 'true'
89
85
  end
90
86
 
91
87
  @global_settings = global_settings || {}
@@ -93,21 +89,12 @@ module Geordi
93
89
  end
94
90
 
95
91
  def check_for_invalid_keys(settings, allowed_keys, file)
92
+ return if settings.nil?
93
+
96
94
  invalid_keys = settings.keys - allowed_keys
97
95
  unless invalid_keys.empty?
98
- Geordi::Interaction.warn "Geordi detected unknown keys in #{file}.\n"
99
-
100
- invalid_keys.sort.each do |key|
101
- puts "* #{key}"
102
- end
103
-
104
- puts "\nAllowed keys are:"
105
- allowed_keys.sort.each do |key|
106
- puts "* #{key}"
107
- end
108
- puts
109
-
110
- exit 1
96
+ Interaction.warn "Unknown settings in #{file}: #{invalid_keys.join(", ")}"
97
+ Interaction.note "Supported settings in #{file} are: #{allowed_keys.join(", ")}"
111
98
  end
112
99
  end
113
100
 
@@ -160,5 +147,18 @@ module Geordi
160
147
  end
161
148
  end
162
149
 
150
+ def array_wrap_project_ids(project_ids)
151
+ case project_ids
152
+ when Array
153
+ project_ids
154
+ when String
155
+ project_ids.split(/[\s]+/).map(&:to_i)
156
+ when Integer
157
+ [project_ids]
158
+ else
159
+ []
160
+ end
161
+ end
162
+
163
163
  end
164
164
  end