Class: Cnvrg::CLI

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

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.is_response_success(response, should_exit = true) ⇒ Object



541
542
543
544
545
546
547
548
549
550
551
552
# File 'lib/cnvrg/cli.rb', line 541

def self.is_response_success(response, should_exit=true)
  if response["status"]!= 200
    error = response['message']
    say("<%= color('Error: #{error}', RED) %>")
    if should_exit
      exit(1)
    else
      return false
    end
  end
  return true
end

Instance Method Details

#__print_versionObject



28
29
30
# File 'lib/cnvrg/cli.rb', line 28

def __print_version
  puts Cnvrg::VERSION
end

#clone(project_url) ⇒ Object



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
184
185
186
187
188
189
# File 'lib/cnvrg/cli.rb', line 143

def clone(project_url)
  verify_logged_in()
  url_parts = project_url.split("/")
  project_index = Cnvrg::Helpers.look_for_in_path(project_url, "projects")
  slug = url_parts[project_index+1]
  owner = url_parts[project_index-1]
  response = Cnvrg::API.request("users/#{owner}/projects/#{slug}/get_project", 'GET')
  Cnvrg::CLI.is_response_success(response)
  response = JSON.parse response["result"]
  project_name = response["title"]
  say "Cloning #{project_name}", Thor::Shell::Color::BLUE
  if Dir.exists? project_name or File.exists? project_name
    say "Error: Conflict with dir/file #{project_name}", Thor::Shell::Color::RED
    exit(1)
  end

  if Project.clone_dir(slug, owner, project_name)
    project_home = Dir.pwd+"/"+project_name
    @project = Project.new(project_home)
    @files = Cnvrg::Files.new(@project.owner, slug)
    response = @project.clone
    Cnvrg::CLI.is_response_success response
    idx = {commit: response["result"]["commit"], tree: response["result"]["tree"]}
    File.open(project_name + "/.cnvrg/idx.yml", "w+") { |f| f.write idx.to_yaml }
    successful_changes = []
    say "Downloading files", Thor::Shell::Color::BLUE
    response["result"]["tree"].each do |f|
      relative_path = f[0].gsub(/^#{@project.local_path}/, "")
      if f[0].end_with? "/"
        # dir
        if @files.download_dir(f[0], relative_path, project_home)
          successful_changes << relative_path
        end
      else
        # blob
        if @files.download_file(f[0], relative_path, project_home)
          successful_changes << relative_path
        end
      end
    end
    say "Done.\nDownloaded total of #{successful_changes.size} files", Thor::Shell::Color::BLUE
  else
    say "Error: Couldn't create directory: #{project_name}", Thor::Shell::Color::RED
    exit(1)
  end

end

#downloadObject



366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
# File 'lib/cnvrg/cli.rb', line 366

def download
  verify_logged_in()
  project_home = get_project_home
  @project = Project.new(project_home)
  @files = Cnvrg::Files.new(@project.owner, @project.slug)

  res = @project.compare_idx["result"]
  result = res["tree"]
  commit = res["commit"]
  if result["updated_on_server"].empty? and result["conflicts"] and result["deleted"].empty?
    say "Project is up to date", Thor::Shell::Color::GREEN
    return true
  end
  update_count = 0
  update_total = result["updated_on_server"].size + result["conflicts"].size

  successful_changes = []
  if update_total ==1
    say "Downloading #{update_total} file", Thor::Shell::Color::BLUE
  else
    say "Downloading #{update_total} files", Thor::Shell::Color::BLUE

  end

  result["conflicts"].each do |f|
    relative_path = f.gsub(/^#{@project.local_path}/, "")
    if @files.download_file(f, relative_path, project_home, conflict=true)
      successful_changes << relative_path
    end

  end
  result["updated_on_server"].each do |f|
    relative_path = f.gsub(/^#{@project.local_path}/, "")
    if f.end_with? "/"
      # dir
      if @files.download_dir(f, relative_path, project_home)
        successful_changes << relative_path
      end
    else
      # blob
      if @files.download_file(f, relative_path, project_home)
        successful_changes << relative_path
      end
    end

  end
  if update_total == successful_changes.size
    # update idx with latest commit
    @project.update_idx_with_commit!(commit)
    say "Done. Downloaded:", Thor::Shell::Color::GREEN
    say successful_changes.join("\n"), Thor::Shell::Color::GREEN
    say "Total of #{successful_changes.size} / #{update_total} files.", Thor::Shell::Color::GREEN
  end
end

#exec(*cmd) ⇒ Object



438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
# File 'lib/cnvrg/cli.rb', line 438

def exec(*cmd)
  verify_logged_in()

  project_home = get_project_home
  @project = Project.new(project_home)
  sync_before = options["sync_before"]
  sync_after = options["sync_after"]
  print_log = options["log"]
  title = options["title"]
  if sync_before
    # Sync before run
    say "Syncing project before running", Thor::Shell::Color::BLUE
    say 'Checking for new updates from remote version', Thor::Shell::Color::BLUE
    download()
    upload()
    say "Done Syncing", Thor::Shell::Color::BLUE
  end

  start_commit = @project.last_local_commit
  cmd = cmd.join("\s")
  log = []
  say "Running: #{cmd}", Thor::Shell::Color::BLUE

  @exp = Experiment.new(@project.owner, @project.slug)

  platform = RUBY_PLATFORM
  machine_name = Socket.gethostname

  @exp.start(cmd, platform, machine_name, start_commit, title)
  unless @exp.slug.nil?
    real = Time.now
    cpu = Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID)
    exp_success = true
    memory_total = []
    cpu_total = []
    begin

      PTY.spawn(cmd) do |stdout, stdin, pid, stderr|
        begin
          stdout.each do |line|
            cur_time = Time.now
            monitor = %x{ps aux|awk  '{print $2,$3,$4}'|grep #{pid} }
            monitor_by = monitor.split(" ")
            memory = monitor_by[2]
            cpu = monitor_by[1]
            memory_total << memory.to_f
            cpu_total << cpu.to_f
            cur_log = {time: cur_time,
                       message: line,
                       type: "stdout",
                       rss: memory,
                       cpu: cpu,
                       real: Time.now-real}
            if print_log
              puts cur_log
            end

            log << cur_log

          end
          if stderr
            stderr.each do |err|
              log << {time: Time.now, message: err, type: "stderr"}
            end
          end
          Process.wait(pid)
        rescue Errno::EIO
          break
        end
      end
    rescue Errno::ENOENT
      exp_success = false
      say "command \"#{cmd}\" couldn't be executed, verify command is valid", Thor::Shell::Color::RED
    rescue PTY::ChildExited
      exp_success = false
      puts "The process exited!"
    end
    cpu_average = cpu_total.inject(0) { |sum, el| sum + el }.to_f / cpu_total.size
    memory_average = memory_total.inject(0) { |sum, el| sum + el }.to_f / memory_total.size
    exit_status = $?.exitstatus
    if !exp_success
      end_commit = @project.last_local_commit
      res = @exp.end(log, exit_status, end_commit,cpu_average,memory_average)
      say "Experiment has failed", Thor::Shell::Color::RED
      exit(0)
    end
    if sync_after
      say "Syncing project after running", Thor::Shell::Color::BLUE
      # Sync after run
      download()
      upload()
      say "Done Syncing", Thor::Shell::Color::BLUE
    end
    end_commit = @project.last_local_commit

    res = @exp.end(log, exit_status, end_commit,cpu_average,memory_average)
    check = Helpers.checkmark()
    say "#{check} Done. Experiment's result: #{Cnvrg::Helpers.remote_url}/#{@project.owner}/projects/#{@project.slug}/experiments/#{@exp.slug}", Thor::Shell::Color::GREEN
  else
    # didnt run
  end
end


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
# File 'lib/cnvrg/cli.rb', line 115

def link
  verify_logged_in()
  sync = options["sync"]
  project_name =File.basename(Dir.getwd)
  say "Linking #{project_name}", Thor::Shell::Color::BLUE
  if File.directory?(Dir.getwd+"/.cnvrg")
    config = YAML.load_file("#{Dir.getwd}/.cnvrg/config.yml")
    say "Directory is already linked to #{config[:project_slug]}", Thor::Shell::Color::RED
    exit(0)
  end
  if Project.link(project_name)
    path = Dir.pwd
    @project = Project.new(path)
    @project.generate_idx()
    if sync
      upload(true)
    end

    url = @project.url
    say "#{project_name}'s location is: #{url}\n", Thor::Shell::Color::BLUE
  else
    say "Error linking project, please contact support.", Thor::Shell::Color::RED
    exit(0)
  end
end

#loginObject



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
# File 'lib/cnvrg/cli.rb', line 35

def 
  cmd = HighLine.new

  say 'Authenticating with cnvrg', Thor::Shell::Color::YELLOW

  @auth = Cnvrg::Auth.new
  netrc = Netrc.read
  @email, token = netrc[Cnvrg::Helpers.netrc_domain]

  if @email and token
    say 'Seems you\'re already logged in', Thor::Shell::Color::BLUE
    exit(0)
  end
  @email = ask("Enter your Email:")
  password = cmd.ask("Enter your password (hidden):") { |q| q.echo = "*" }

  if (token = @auth.(@email, password))
    netrc[Cnvrg::Helpers.netrc_domain] = @email, token
    netrc.save

    say "Authenticated successfully as #{@email}", Thor::Shell::Color::GREEN

  else
    say "Failed to authenticate, wrong email/password", Thor::Shell::Color::RED
    exit false
  end
end

#logoutObject



65
66
67
68
69
70
# File 'lib/cnvrg/cli.rb', line 65

def logout
  netrc = Netrc.read
  netrc.delete(Cnvrg::Helpers.netrc_domain)
  netrc.save
  say "Logged out successfully.\n", Thor::Shell::Color::GREEN
end

#meObject



74
75
76
77
78
79
80
81
82
# File 'lib/cnvrg/cli.rb', line 74

def me
  verify_logged_in()
  auth = Cnvrg::Auth.new
  if (email = auth.get_email)
    say "Logged in as: #{email}", Thor::Shell::Color::GREEN
  else
    say "You're not logged in.", Thor::Shell::Color::RED
  end
end

#new(project_name) ⇒ Object

Projects



86
# File 'lib/cnvrg/cli.rb', line 86

desc 'new', 'Create a new cnvrg project'

#statusObject



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
# File 'lib/cnvrg/cli.rb', line 193

def status
  verify_logged_in()
  @project = Project.new(get_project_home)
  result = @project.compare_idx["result"]
  commit = result["commit"]
  result = result["tree"]
  say "Comparing local changes with remote version:", Thor::Shell::Color::BLUE
  if result["added"].empty? and result["updated_on_local"].empty? and result["updated_on_server"].empty? and result["deleted"].empty? and result["conflicts"].empty?
    say "Project is up to date", Thor::Shell::Color::GREEN
    return true
  end
  if result["added"].size > 0
    say "Added files:\n", Thor::Shell::Color::BLUE
    result["added"].each do |a|
      say "\t\tA:\t#{a}", Thor::Shell::Color::GREEN
    end
  end

  if result["deleted"].size > 0
    say "Deleted files:\n", Thor::Shell::Color::BLUE
    result["deleted"].each do |a|
      say "\t\tD:\t#{a}", Thor::Shell::Color::GREEN
    end
  end
  if result["updated_on_local"].size > 0
    say "Local changes:\n", Thor::Shell::Color::BLUE
    result["updated_on_local"].each do |a|
      say "\t\tM:\t#{a}", Thor::Shell::Color::GREEN
    end
  end

  if result["updated_on_server"].size > 0
    say "Remote changes:\n", Thor::Shell::Color::BLUE
    result["updated_on_server"].each do |a|
      say "\t\tM:\t#{a}", Thor::Shell::Color::GREEN
    end
  end

  if result["conflicts"].size > 0
    say "Conflicted changes:\n", Thor::Shell::Color::BLUE
    result["conflicts"].each do |a|
      say "\t\tC:\t#{a}", Thor::Shell::Color::RED
    end
  end
end

#syncObject



424
425
426
427
428
# File 'lib/cnvrg/cli.rb', line 424

def sync
  say 'Checking for new updates from remote version', Thor::Shell::Color::BLUE
  invoke :download
  invoke :upload
end

#upload(link = false, sync = false) ⇒ Object



243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
# File 'lib/cnvrg/cli.rb', line 243

def upload(link=false, sync=false)

  verify_logged_in()
  @project = Project.new(get_project_home)

  @files = Cnvrg::Files.new(@project.owner, @project.slug)
  ignore = options[:ignore] || []
  if !@project.update_ignore_list(ignore)
    say "Couldn't append new ignore files to .cnvrgignore", Thor::Shell::Color::YELLOW
  end
  result = @project.compare_idx
  commit = result["result"]["commit"]
  if !link
    if commit != @project.last_local_commit and !@project.last_local_commit.nil?
      say "Remote server has an updated version, please run `cnvrg download` first, or alternatively: `cnvrg sync`", Thor::Shell::Color::YELLOW
      exit(1)
    end
    say "Comparing local changes with remote version:", Thor::Shell::Color::BLUE
  end
  result = result["result"]["tree"]
  if result["added"].empty? and result["updated_on_local"].empty? and result["deleted"].empty?
    say "Project is up to date", Thor::Shell::Color::GREEN
    return true
  end
  update_count = 0
  update_total = result["added"].size + result["updated_on_local"].size + result["deleted"].size
  successful_updates = []
  successful_deletions = []
  if update_total == 1
    say "Updating #{update_total} file", Thor::Shell::Color::BLUE
  else
    say "Updating #{update_total} files", Thor::Shell::Color::BLUE
  end

  # Start commit

  commit_sha1 = @files.start_commit["result"]["commit_sha1"]

  # upload / update
  begin
    (result["added"] + result["updated_on_local"]).each do |f|
      puts f
      relative_path = f.gsub(/^#{@project.local_path + "/"}/, "")

      if File.directory?(f)
        resDir = @files.create_dir(f, relative_path, commit_sha1)
        if resDir
          update_count += 1
          successful_updates<< relative_path
        end
      else
          res = @files.upload_file(f, relative_path, commit_sha1)
        if res
          update_count += 1
          successful_updates<< relative_path
        else
          @files.rollback_commit(commit_sha1)
          say "Couldn't upload, Rolling Back all changes.", Thor::Shell::Color::RED
          exit(0)
        end
      end
    end

    # delete
    result["deleted"].each do |f|
      relative_path = f.gsub(/^#{@project.local_path + "/"}/, "")
      if relative_path.end_with?("/")
        if @files.delete_dir(f, relative_path, commit_sha1)
          update_count += 1
          successful_updates<< relative_path
        end
      else
        if @files.delete_file(f, relative_path, commit_sha1)
          update_count += 1
          successful_updates<< relative_path
        end
      end
    end

  rescue Interrupt
    @files.rollback_commit(commit_sha1)
    say "User aborted, Rolling Back all changes.", Thor::Shell::Color::RED
    exit(0)
  end
  if update_count == update_total
    res = @files.end_commit(commit_sha1)
    if (Cnvrg::CLI.is_response_success(res, false))
      # save idx
      begin
	  @project.update_idx_with_files_commits!((successful_deletions+successful_updates), res["result"]["commit_time"])

      @project.update_idx_with_commit!(commit_sha1)
	  rescue 
@files.rollback_commit(commit_sha1)
     	 say "Couldn't commit updates, Rolling Back all changes.", Thor::Shell::Color::RED
		 exit(1)

	  end

	  say "Done", Thor::Shell::Color::BLUE
      if successful_updates.size >0
        say "Updated:", Thor::Shell::Color::GREEN
 suc = successful_updates.map {|x| x=Helpers.checkmark() +" "+x}
 say suc.join("\n"), Thor::Shell::Color::GREEN
      end
      if successful_deletions.size >0
        say "Deleted:", Thor::Shell::Color::GREEN
 del = successful_updates.map {|x| x=Helpers.checkmark() +" "+x}
 say del.join("\n"), Thor::Shell::Color::GREEN
      end
      say "Total of #{update_count} / #{update_total} files.", Thor::Shell::Color::GREEN
    else
      @files.rollback_commit(commit_sha1)
      say "Error. Rolling Back all changes.", Thor::Shell::Color::RED
    end
  else
    @files.rollback_commit(commit_sha1)
  end

end