Class: Sesh::Cli

Inherits:
Object
  • Object
show all
Defined in:
lib/sesh/cli.rb

Class Method Summary collapse

Class Method Details

.handle_command!Object



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
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
248
249
250
251
252
253
254
255
# File 'lib/sesh/cli.rb', line 185

def self.handle_command!
  if @command
    case @command
    when 'start'   then @tmux_control.start_project!
    when 'stop'    then @tmux_control.stop_project!
    when 'restart' then @tmux_control.restart_project!
    when 'new'
      config = Tmuxinator::Config.project @options[:project]
      unless Tmuxinator::Config.exists?(@options[:project])
        erb = Erubis::Eruby.new(File.read(@options[:template])).result binding
        File.open(config, 'w') { |f| f.write(erb) }
      end

      Kernel.system("#{Inferences.infer_default_editor} #{config}") ||
                       Tmuxinator::Cli.new.doctor
      puts
    when 'edit'
      config = Tmuxinator::Config.project(@options[:project])
      if Tmuxinator::Config.exists? @options[:project]
        Kernel.system("#{Inferences.infer_default_editor} #{config}") ||
                       Tmuxinator::Cli.new.doctor
        puts
      else
        Logger.fatal "Sesh project '#{@options[:project]}' does not exist yet!"
      end
    when 'list'
      running_projects = TmuxControl.get_running_projects
      pcount = running_projects.count
      if pcount > 0
        Logger.success "#{pcount} project#{pcount>1 ? 's':''} currently running:"
        running_projects.each do |rp|
          puts; Logger.info "Project: #{rp}", 1
          tc = TmuxControl.new rp
          tc_clients = tc.connected_client_devices
          if tc_clients.any?
            Logger.success "Connected Client Devices:", 2
            tc_clients.each_with_index do |c, i|
              Logger.info "#{i+1}: #{c}: #{tc.get_ip_from_device(c)}", 3
            end
          else
            Logger.warn 'No clients connected.', 2
          end
        end
        puts
      else Logger.fatal "There are no Sesh projects currently running." end
    when 'connect'
      if @options[:ssh][:local_addr] == @options[:ssh][:remote_addr]
        unless @tmux_control.already_running?
          Logger.fatal "Sesh project '#{@options[:project]}' is not running!"
        end
      end
      system @ssh_control.enter_slave_mode_command
    when 'enslave'
      Logger.fatal("Sesh project '#{@options[:project]}' is not running!") unless @tmux_control.already_running?
      Logger.fatal("You must specify a machine to enslave! Eg: user@ip") if @options[:ssh][:remote_addr].nil?
      Logger.info "Attempting to connect #{@options[:ssh][:remote_addr]} to Sesh project '#{@options[:project]}'..."
      if @ssh_control.enslave_peer!
        Logger.success "Sesh client connected successfully."
      else Logger.fatal 'Sesh client failed to connect.' end
    when 'begin' then @tmux_control.begin_tmuxinator_session!
    when 'run', 'rspec'
      @tmux_control.do_shell_operation! @options[:shell]
    when 'detach' then @tmux_control.disconnect_client! ARGV.join(' ')
    else
      Logger.fatal "Command not recognized!"
    end
    exit 0
  end

  Logger.fatal 'You must specify a command.'
end

.parse_options!Object



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/sesh/cli.rb', line 21

def self.parse_options!
  @command = ARGV.shift

  # Load config from a YAML file in the project root if available.
  @config_filepath = nil
  @config_friendly_filepath = nil
  POSSIBLE_CONFIG_LOCATIONS.each do |path|
    fullpath = File.join Dir.pwd, path
    next unless File.exists? fullpath
    @config_filepath  = fullpath
    @config_friendly_filepath = path
    break
  end
  @defaults = DEFAULT_OPTIONS.dup
  if @config_filepath.nil?
    @config_friendly_filepath = POSSIBLE_CONFIG_LOCATIONS[0]
  else
    loaded_config = deep_symbolize YAML::load_file(@config_filepath)
    @defaults.deep_merge! loaded_config
  end

  # Parse options given to the command.
  parsed_options = @defaults.dup
  OptionParser.new do |opts|
    opts.banner = HELP_BANNER

    opts.on("-p", "--project=project", 'Project') {|v|
      parsed_options[:project] = v }

    # Options for "new" command
    if @command == 'new'
      opts.on('-T', '--template=path', 'Path to Tmuxinator Template') {|v|
        parsed_options[:template] = v }
    end

    # Options for "connect" and "enslave" commands
    if %w(connect enslave).include? @command
      opts.on('-N', '--new-window', 'Connect in a new terminal window') {|v|
        parsed_options[:ssh][:connect_in_new_window] = true }
      opts.on('-F', '--fullscreen', 'Fullscreen new terminal window after connecting') {|v|
        parsed_options[:ssh][:connect_fullscreen] = true }
    end

    # SSH options
    opts.on("-L", "--local-ssh-addr=addr", 'Local SSH Address') {|v|
      parsed_options[:ssh][:local_addr] = v }
    opts.on("-R", "--remote-ssh-addr=addr", 'Remote SSH Address') {|v|
      parsed_options[:ssh][:remote_addr] = v }

    # Tmux options
    # opts.on('-S', '--tmux-socket-file=path', 'Path to Tmux Socket File') {|v|
    #   # fatal("Socket file #{v} does not exist.") unless File.exist?(v)
    #   parsed_options[:tmux][:socket_file] = v }
    # opts.on('--tmux-pids-file=path', 'Path to Tmux Pids File') {|v|
    #   parsed_options[:tmux][:pids_file] = v }

    # Shell options for remote commands
    opts.on('-C', '--shell-command=cmd', 'Shell Command to Execute') {|v|
      parsed_options[:shell][:command] = v }
    opts.on('-P', '--shell-pane=pane', 'Pane to Execute Shell Command in') {|v|
      parsed_options[:shell][:pane] = v }
    opts.on('--spec-path=path', 'Path to Spec File to Run') {|v|
      parsed_options[:shell][:spec] = v }
    opts.on('--rspec-prefix=prefix', 'Prefix for rspec command') {|v|
      parsed_options[:shell][:rspec_prefix] = v }
    opts.on('--and-return', 'Return after Execution') {|v|
      parsed_options[:shell][:and_return] = true }

    # # target_opts = DEPLOYMENT_TARGETS.join '|'
    # opts.on("-T", "--target=target", 'Titanium Deployment Target') do |v|
    #   if DEPLOYMENT_TARGETS.include? v
    #     platform = parsed_options[:titanium_command][:platform]
    #     if v == 'xcode'
    #       parsed_options[:titanium_command][:run_with_xcode] = true
    #       v = 'simulator'
    #     end
    #     if v == 'emulator' && platform == 'ios' then v = 'simulator'
    #     elsif v == 'simulator' && platform == 'android' then v = 'emulator' end
    #     parsed_options[:titanium_command][:target] = v
    #   else fatal "Target \"#{v}\" not recognized." end
    # end
    #
    # opts.on('-O', '--output-dir=dir', 'Titanium Output Directory') {|v|
    #   parsed_options[:titanium_command][:output_dir] = v }
    #
    # opts.on('-L', '--log-level=level', 'Titanium Log Level') {|v|
    #   parsed_options[:titanium_command][:log_level] = v }
    #
    # opts.on('-V', '--developer-certificate=certificate',
    #         'iOS Developer Certificate') {|v|
    #   parsed_options[:ios_credentials][:development][:developer_certificate] = v
    #   parsed_options[:ios_credentials][:production][:developer_certificate] = v }
    # opts.on('-P', '--provisioning-profile=profile',
    #         'iOS Provisioning Profile') {|v|
    #   parsed_options[:ios_credentials][:development][:provisioning_profile] = v
    #   parsed_options[:ios_credentials][:production][:provisioning_profile] = v }
    #
    # opts.on('-U', '--simulator-udid=udid', 'iOS Simulator UDID') {|v|
    #   parsed_options[:ios_simulator][:udid] = v }
    # opts.on('-D', '--devicetypeid=dtid', 'iOS Simulator DeviceTypeID') {|v|
    #   parsed_options[:ios_simulator][:devicetypeid] = v }
    #
    # opts.on('--no-autofocus', 'Disable iOS Simulator Autofocus') {
    #   parsed_options[:ios_simulator][:autofocus] = false }

    opts.on('--help', 'Prints this help') { puts opts.banner; exit }
  end.parse!
  @options = parsed_options
  if @options[:project].nil?
    project_required = !%w(new list).include?(@command)
    ARGV.each {|a|
      if Tmuxinator::Config.exists? a
        @options[:project] = ARGV.delete a
        break end } if project_required && ARGV.any?
    @options[:project] ||= Inferences.infer_project_from_current_directory
    if project_required && @options[:project].nil?
      Logger.warn 'A matching Sesh project could not be found.'
      Logger.fatal 'Hint: run sesh new or specify an existing project with your commmand.'
    end
  end
  if %w(new).include? @command
    @options[:project]  ||= Inferences.infer_current_directory
    @options[:template] ||= File.join(
      File.dirname(File.expand_path(__FILE__)), 'assets', 'sample.yml' )
  end
  # @options[:tmux][:socket_file] ||= "/tmp/#{@options[:project]}.sock"
  # @options[:tmux][:pids_file]   ||= "/tmp/#{@options[:project]}.pids.txt"
  if %w(enslave connect).include? @command
    @options[:ssh][:local_addr] ||= Sesh::Inferences.infer_local_ssh_addr
    if @options[:ssh][:remote_addr].nil? && ARGV.any?
      ARGV.each {|a|
        if a =~ /\.local$/ || a =~ /[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/ || a =~ /^(.+)@(.+)$/
          @options[:ssh][:remote_addr] = a
          break end }
      @options[:ssh][:remote_addr] ||= ARGV.shift
    end
    if @options[:ssh][:remote_addr].nil?
      if %w(enslave).include? @command
        Logger.warn 'A remote address is required.'
        Logger.fatal 'Hint: specify a remote ssh address using the -R flag.'
      elsif %w(connect).include? @command
        @options[:ssh][:remote_addr] = Inferences.infer_local_ssh_addr
      end
    end
  end
  # TODO: parse a spec file
  if @command == 'rspec' && @options[:shell][:spec].nil?
    ARGV.each{|a| if a =~ /spec\//
      @options[:shell][:spec] = a; break end } if ARGV.any?
    @options[:shell][:spec] ||= 'spec'
  end
  # This block should always go at the end because it eats the rest of ARGV.
  if %w(run).include?(@command) && @options[:shell][:command].nil?
    if ARGV.any?
      argv_cmd_parts = []
      argv_cmd_parts << ARGV.shift while ARGV.any?
      @options[:shell][:command] = argv_cmd_parts.join(' ')
    end
    if @options[:shell][:command].nil? || @options[:shell][:command].length == 0
      Logger.fatal 'You must specify a command to run.'
    end
  end
end

.startObject



11
12
13
14
15
16
17
18
19
# File 'lib/sesh/cli.rb', line 11

def self.start
  puts; puts "  Sesh v#{VERSION}".green; puts '  ==========='.green; puts
  if ARGV.empty? or ARGV.include? '-h' or ARGV.include? '--help'
    puts HELP_BANNER.blue; exit end
  parse_options!
  @tmux_control = TmuxControl.new @options[:project], @options[:tmux]
  @ssh_control  = SshControl.new @tmux_control, @options[:ssh]
  handle_command!
end