cnvrg 1.6.0.8 → 1.6.0.9

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: 319b1e08fd56c2a26834b3a2e2fc94565e2abad1d0244c72e4e45c11a8cc7d3d
4
- data.tar.gz: 60edde88d619c8aae0afded8314c2688545712445718e99576a7ec20222d795e
3
+ metadata.gz: 3a578311b787d26b7a510ce7c6ff9b845d55300b82a5c75e70a787e334693779
4
+ data.tar.gz: 7bea7b961282523b23dfbe01014d658147dfa354240246ff4722e165d5603895
5
5
  SHA512:
6
- metadata.gz: 9dc2c22f8c1f4c696c0580f2ab2c3122d57061a95591bff13403a2e93412f6268b50a23119ba5ccbaa0830bd8f87474490be50ccf6d128c378b39d4a1cffaca3
7
- data.tar.gz: 4628fee3352fb89fb639ed3aef3e76ce2cadff9c916d153a940416d25ac81f8e700b2891cca6682c95e9526342c089ef631f2c16d9df9ca84cc4faccaac70e28
6
+ metadata.gz: f8d23528d8fe271b4f4dde1c585b2a0991e8aaf4445a4b33436355caaed5dff153dd72afa1574123de627a894d38ba78157caa79ba77ba4e52f13ba92e0c6c45
7
+ data.tar.gz: 1cfa9cddbd3af489be308bf496f0e4b1079a9a007eafe97cf5255b31187fbb15ab9978797b2a8fbec3b8f4b08c8a74ec042a7088f6e3b799cf0a41e80792c5f3
@@ -30,6 +30,7 @@ module Cnvrg
30
30
  end
31
31
  end
32
32
  def self.request(resource, method = 'GET', data = {}, parse_request = true)
33
+ resource = URI::encode resource
33
34
  begin
34
35
  n = Netrc.read
35
36
  rescue => e
@@ -163,22 +164,25 @@ module Cnvrg
163
164
 
164
165
  # what if windows?
165
166
  # data[:file] = Faraday::UploadIO.new(data[:absolute_path], content_type)
167
+ if not File.exists? data[:relative_path]
166
168
  file_base = File.basename(data[:relative_path])
167
- begin
168
- temp_path = File.expand_path('~')+"/.cnvrg/tmp_files/#{file_base}"
169
- FileUtils.touch(temp_path)
170
- rescue
171
- temp_path ="/tmp/#{file_base}"
172
- FileUtils.touch(temp_path)
173
- end
174
169
 
170
+ begin
171
+ temp_path = File.expand_path('~')+"/.cnvrg/tmp_files/#{file_base}"
172
+ FileUtils.touch(temp_path)
173
+ rescue
174
+ temp_path ="/tmp/#{file_base}"
175
+ FileUtils.touch(temp_path)
176
+ end
177
+ else
178
+ temp_path = data[:relative_path]
179
+ end
175
180
 
176
181
 
177
- data[:file] = Faraday::UploadIO.new("#{temp_path}", "plain/text")
182
+ data[:file] = Faraday::UploadIO.new("#{temp_path}", "application/tar+gzip")
178
183
 
179
184
  response = conn.post "#{endpoint_uri}/#{resource}", data
180
185
  Cnvrg::API.parse_version(response)
181
-
182
186
  FileUtils.rm_rf(temp_path)
183
187
  if response.to_hash[:status] == 404
184
188
  return false
@@ -41,6 +41,7 @@ require 'cnvrg/org_helpers'
41
41
  require 'cnvrg/cli/subcommand'
42
42
  require 'cnvrg/cli/flow'
43
43
  require 'cnvrg/cli/task'
44
+ require 'cnvrg/cli/library_cli'
44
45
  require 'cnvrg/image_cli'
45
46
  require 'cnvrg/helpers/executer'
46
47
  require 'cnvrg/downloader/client'
@@ -177,6 +178,9 @@ module Cnvrg
177
178
  desc "image [COMMAND]", "build existing images", :hide => true
178
179
  subcommand "image", ImageCli
179
180
 
181
+ desc "library [COMMAND]", "Upload and manage datasets", :hide => false
182
+ subcommand "library", LibraryCli
183
+
180
184
 
181
185
  desc "flow", "mange project flows", :hide => true
182
186
  subcommand "flow", Cnvrg::Commands::Flow
@@ -944,8 +948,7 @@ module Cnvrg
944
948
  Cnvrg::CLI.is_response_success(response,true)
945
949
  dataset_name = response["results"]["name"]
946
950
  dataset_slug = response["results"]["slug"]
947
- # dataset_home = Dir.pwd+"/"+dataset_name
948
- dataset_home = Dir.pwd
951
+ dataset_home = File.join(Dir.pwd, dataset_name)
949
952
 
950
953
  if Dataset.blank_clone(owner, dataset_name, dataset_slug)
951
954
  dataset = Dataset.new(dataset_home)
@@ -1990,6 +1993,7 @@ module Cnvrg
1990
1993
  end
1991
1994
  end
1992
1995
  rescue => e
1996
+ Cnvrg::CLI.log_message(e.message, 'red')
1993
1997
  Cnvrg::Logger.log_error(e)
1994
1998
  say "\nAborting", Thor::Shell::Color::BLUE
1995
1999
  dataset_dir = is_cnvrg_dir(Dir.pwd)
@@ -3124,7 +3128,7 @@ module Cnvrg
3124
3128
  end
3125
3129
  end
3126
3130
  start_time = Time.now
3127
- PTY.spawn(cmd) do |stdout, stdin, pid, stderr|
3131
+ PTY.spawn(@exp.as_env, cmd) do |stdout, stdin, pid, stderr|
3128
3132
  begin
3129
3133
  stdout.each do |line|
3130
3134
  cur_time = Time.now
@@ -3189,7 +3193,7 @@ module Cnvrg
3189
3193
  end
3190
3194
 
3191
3195
  if sync_after
3192
- @project.job_log(["Syncing Experiment"])
3196
+ @exp.job_log(["Syncing Experiment"])
3193
3197
  # Sync after run
3194
3198
  if @project.is_git
3195
3199
  output_dir = output_dir || @exp.output_dir
@@ -3206,7 +3210,7 @@ module Cnvrg
3206
3210
  end
3207
3211
  end_commit = @project.last_local_commit
3208
3212
  if end_commit.present?
3209
- @project.job_log(["Experiment end commit: #{end_commit}"])
3213
+ @exp.job_log(["Experiment end commit: #{end_commit}"])
3210
3214
  end
3211
3215
 
3212
3216
  # log_thread.join
@@ -18,92 +18,149 @@ module Cnvrg
18
18
  end
19
19
  end
20
20
 
21
- desc "task", "Running Flow tasks", :hide => true
22
- subcommand 'task', Cnvrg::Commands::Task
23
-
24
- desc "flow verify", "verify that the flow is well formatted"
25
-
26
- def verify(path)
27
- unless @curr_dir.present?
28
- @cli.log_message("Cant run this command because you are not in project directory", Thor::Color::RED)
29
- return false
30
- end
31
- @flow = Cnvrg::Flows.new(@curr_dir, path)
32
- @cli.log_message("The Flow is Valid", Thor::Color::GREEN)
21
+ desc "flow import --file='flow.yaml'", "Import flow to the web"
22
+ method_option :file, :type => :string, :aliases => ["-f", "--file"]
23
+ def import()
24
+ run_flow_internal(file: options[:file], run: false)
33
25
  end
34
26
 
27
+ desc "flow run --file='flow.yaml'", "Import flow to the web"
28
+ method_option :file, :type => :string, :aliases => ["-f", "--file"]
29
+ def run()
30
+ run_flow_internal(file: options[:file], run: true)
31
+ end
35
32
 
36
- desc "flow run", "run a flow file"
37
- def run(flow_slug)
38
- unless @curr_dir.present?
39
- @cli.log_message("Cant run this command because you are not in project directory", Thor::Color::RED)
40
- return false
33
+ no_commands {
34
+ def run_flow_internal(file: nil, run: false)
35
+ if options[:file].blank?
36
+ Cnvrg::CLI.log_message("Flow title is required for export. please use --title='Flow 1'")
37
+ return
38
+ end
39
+ if not File.exists? options[:file]
40
+ Cnvrg::CLI.log_message("Cant find file in path #{options[:file]}.")
41
+ return
42
+ end
43
+ payload = YAML.safe_load(File.open(file).read)
44
+ @project = Cnvrg::Project.new(Cnvrg::CLI.get_project_home)
45
+ begin
46
+ @flow, version_slug = Flows.create_flow(@project, payload, run: run)
47
+ rescue => e
48
+ Cnvrg::CLI.log_message("Error while validating flow: #{e.message}", 'error')
49
+ return
41
50
  end
42
- @flow = Cnvrg::Flows.new(flow_slug)
43
- resp = @flow.run
44
- flow_version_href = resp["flow_version"]["href"]
45
- Cnvrg::CLI.log_message("Flow Live results: #{flow_version_href}")
46
- true
51
+ Cnvrg::CLI.log_message("New flow version was created successfuly!", 'green')
52
+ Cnvrg::CLI.log_message("you can find the flow in #{@flow.edit_version_href(version_slug)}", 'green')
53
+ rescue => e
54
+ Cnvrg::CLI.log_message("Failed while creating flow")
55
+ Cnvrg::Logger.log_error(e)
47
56
  end
57
+ }
48
58
 
49
- desc "flow create", "create a flow file"
50
-
51
- def create
52
- title = ask("Flow Title: ")
53
- title = title.presence || "cnvrg-flow"
54
- relations = []
55
- task = ask_for_relation
56
- while task.compact.size == 2
57
- from, to = task
58
- relations << {from: from, to: to}
59
- task = ask_for_relation
59
+ desc "flow export --file='flow.yaml' --title='Flow 1' --version='version 1'", "Export flow to the web"
60
+ method_option :file, :type => :string, :aliases => ["-f", "--file"]
61
+ method_option :title, :type => :string, :aliases => ["-t", "--title"]
62
+ method_option :version, :type => :string, :aliases => ["-v", "--version"], :default => "latest"
63
+ def export()
64
+ if options[:title].blank?
65
+ Cnvrg::CLI.log_message("Flow title is required for export. please use --title='Flow 1'", "red")
66
+ return
60
67
  end
61
- start_commit = ask("Flow Starting Commit (empty will set this value to 'latest')")
62
- Cnvrg::Flows.create_flow("#{title}.flow.yaml", {title: title, relations: relations, start_commit: start_commit})
63
- end
64
68
 
69
+ @project = Cnvrg::Project.new(Cnvrg::CLI.get_project_home)
70
+ @flow = Flows.new(options[:title], project: @project)
71
+ filename = @flow.export(options[:version], file: options[:file])
72
+ Cnvrg::CLI.log_message("Flow was saved successfuly to: #{filename}", 'green')
73
+ rescue => e
74
+ Cnvrg::CLI.log_message(e.message, 'red')
75
+ Cnvrg::Logger.log_error(e)
76
+ end
65
77
 
66
- # desc "flow resolve", "Resolve flow parameters"
67
- # def resolve(path)
68
- # @hyper = Cnvrg::Hyper.new(@curr_dir, path)
69
- # @hyper.resolve_params
78
+ # desc "task", "Running Flow tasks", :hide => true
79
+ # subcommand 'task', Cnvrg::Commands::Task
80
+ #
81
+ # desc "flow verify", "verify that the flow is well formatted"
82
+ #
83
+ # def verify(path)
84
+ # unless @curr_dir.present?
85
+ # @cli.log_message("Cant run this command because you are not in project directory", Thor::Color::RED)
86
+ # return false
87
+ # end
88
+ # @flow = Cnvrg::Flows.new(@curr_dir, path)
89
+ # @cli.log_message("The Flow is Valid", Thor::Color::GREEN)
70
90
  # end
71
91
  #
72
-
73
- private
74
-
75
- def init_tasks
76
- @tasks = Dir.glob("**/*.task*")
77
- end
78
-
79
- def ask_for_relation
80
- init_tasks if @tasks.blank?
81
- to = nil
82
- from = ask_for_task("Task To Start From: [#{@tasks.join(', ')}]")
83
- to = ask_for_task("Task To Go To: [#{@tasks.join(', ')}]") if from.present?
84
- [from, to]
85
- end
86
-
87
- def ask_for_task(text)
88
- verified = false
89
- task = nil
90
- while !verified
91
- task = ask(text)
92
- if task.blank?
93
- return nil
94
- end
95
- begin
96
- Cnvrg::Task.new(@curr_dir, path: task).verify_task
97
- verified = true
98
- rescue => e
99
- end
100
- end
101
- task
102
- end
103
-
104
- def get_all_tasks
105
- @tasks = Dir.glob("*/**.task.yaml")
106
- end
107
- end
92
+ #
93
+ # desc "flow run", "run a flow file"
94
+ # def run(flow_slug)
95
+ # unless @curr_dir.present?
96
+ # @cli.log_message("Cant run this command because you are not in project directory", Thor::Color::RED)
97
+ # return false
98
+ # end
99
+ # @flow = Cnvrg::Flows.new(flow_slug)
100
+ # resp = @flow.run
101
+ # flow_version_href = resp["flow_version"]["href"]
102
+ # Cnvrg::CLI.log_message("Flow Live results: #{flow_version_href}")
103
+ # true
104
+ # end
105
+ #
106
+ # desc "flow create", "create a flow file"
107
+ #
108
+ # def create
109
+ # title = ask("Flow Title: ")
110
+ # title = title.presence || "cnvrg-flow"
111
+ # relations = []
112
+ # task = ask_for_relation
113
+ # while task.compact.size == 2
114
+ # from, to = task
115
+ # relations << {from: from, to: to}
116
+ # task = ask_for_relation
117
+ # end
118
+ # start_commit = ask("Flow Starting Commit (empty will set this value to 'latest')")
119
+ # Cnvrg::Flows.create_flow("#{title}.flow.yaml", {title: title, relations: relations, start_commit: start_commit})
120
+ # end
121
+ #
122
+ #
123
+ # # desc "flow resolve", "Resolve flow parameters"
124
+ # # def resolve(path)
125
+ # # @hyper = Cnvrg::Hyper.new(@curr_dir, path)
126
+ # # @hyper.resolve_params
127
+ # # end
128
+ # #
129
+ #
130
+ # private
131
+ #
132
+ # def init_tasks
133
+ # @tasks = Dir.glob("**/*.task*")
134
+ # end
135
+ #
136
+ # def ask_for_relation
137
+ # init_tasks if @tasks.blank?
138
+ # to = nil
139
+ # from = ask_for_task("Task To Start From: [#{@tasks.join(', ')}]")
140
+ # to = ask_for_task("Task To Go To: [#{@tasks.join(', ')}]") if from.present?
141
+ # [from, to]
142
+ # end
143
+ #
144
+ # def ask_for_task(text)
145
+ # verified = false
146
+ # task = nil
147
+ # while !verified
148
+ # task = ask(text)
149
+ # if task.blank?
150
+ # return nil
151
+ # end
152
+ # begin
153
+ # Cnvrg::Task.new(@curr_dir, path: task).verify_task
154
+ # verified = true
155
+ # rescue => e
156
+ # end
157
+ # end
158
+ # task
159
+ # end
160
+ #
161
+ # def get_all_tasks
162
+ # @tasks = Dir.glob("*/**.task.yaml")
163
+ # end
108
164
  end
109
- end
165
+ end
166
+ end
@@ -0,0 +1,33 @@
1
+ module Cnvrg
2
+ class LibraryCli < SubCommandBase
3
+
4
+ desc "library import", description: ""
5
+ def import
6
+ unless File.exists? "library.yml"
7
+ Cnvrg::CLI.log_message("Can't find library.yml", 'red')
8
+ exit(1)
9
+ end
10
+ library = YAML.safe_load(File.open("library.yml").read)
11
+ Cnvrg::CLI.log_message("Archiving library #{library["title"]}")
12
+ files = Dir["**/*"].select{|file| not File.directory?(file)}
13
+ File.open("archive.tar.gz", "wb") do |file|
14
+ Zlib::GzipWriter.wrap(file) do |gzip|
15
+ Gem::Package::TarWriter.new(gzip) do |tar|
16
+ files.each do |filename|
17
+ f = File.open(filename)
18
+ tar.add_file_simple(filename, 0644, f.size) do |io|
19
+ io.write(f.read)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ response = Cnvrg::API.request(['users', Cnvrg::CLI.get_owner, 'libraries'].join("/"), "POST_FILE", {relative_path: "archive.tar.gz"})
26
+ if response["status"] != 200
27
+ Cnvrg::CLI.log_message("Can't create library: #{response["message"]}")
28
+ exit(1)
29
+ end
30
+ Cnvrg::CLI.log_message("Library Created successfuly", "green")
31
+ end
32
+ end
33
+ end
@@ -518,6 +518,9 @@ module Cnvrg
518
518
  #check if prefix exists do prefix/path otherwise path
519
519
  label = file.gsub(self.local_path + "/", "")
520
520
  label = "#{prefix}/#{label}" if prefix.present?
521
+ if not Cnvrg::Files.valid_file_name?(label)
522
+ raise StandardError.new("#{label} is not a valid file name.")
523
+ end
521
524
  if File.directory? file
522
525
  tree[label + "/"] = nil
523
526
  else
@@ -16,6 +16,15 @@ module Cnvrg
16
16
  @output_dir = nil
17
17
  end
18
18
 
19
+ def as_env
20
+ return {
21
+ CNVRG_JOB_ID: @slug,
22
+ CNVRG_JOB_TYPE: "Experiment",
23
+ CNVRG_PROJECT: @project_slug,
24
+ CNVRG_OWNER: @owner,
25
+ }.as_json
26
+ end
27
+
19
28
  def start(input, platform, machine_name, start_commit, name, email_notification, machine_activity,script_path,
20
29
  sync_before_terminate, periodic_sync)
21
30
 
@@ -88,6 +97,14 @@ module Cnvrg
88
97
 
89
98
  end
90
99
 
100
+ def job_log(logs, level: 'info', step: nil, job_type: nil, job_id: nil)
101
+ logs = [logs].flatten
102
+ logs.each_slice(10).each do |temp_logs|
103
+ Cnvrg::API.request("users/#{@owner}/projects/#{@project_slug}/jobs/experiment/#{@slug}/log", "POST", {job_type: "Experiment", job_id: @slug, logs: temp_logs, log_level: level, step: step, timestamp: Time.now})
104
+ sleep(1)
105
+ end
106
+ end
107
+
91
108
  def exec_remote(command, commit_to_run, instance_type, image_slug,schedule,local_timestamp, grid,path_to_cmd,data, data_commit,periodic_sync,
92
109
  sync_before_terminate, max_time, ds_sync_options=0,output_dir=nil,data_query=nil,
93
110
  git_commit=nil, git_branch=nil, restart_if_stuck=nil, local_folders=nil,title=nil, datasets=nil, prerun: true, requirements: true, recurring: nil)
@@ -6,7 +6,7 @@ require 'net/http'
6
6
  require 'cnvrg/result'
7
7
  module Cnvrg
8
8
  class Files
9
-
9
+ VALID_FILE_NAME = /[\x00\\:\*\?\"<>\|]/
10
10
  LARGE_FILE=1024*1024*5
11
11
  MULTIPART_SPLIT=10000000
12
12
 
@@ -29,6 +29,11 @@ module Cnvrg
29
29
  end
30
30
 
31
31
 
32
+ def self.valid_file_name?(fullpath)
33
+ VALID_FILE_NAME.match(fullpath).blank?
34
+ end
35
+
36
+
32
37
  def download_commit(sha1)
33
38
  response = @project.clone(false, sha1)
34
39
  log_error("Cant download commit #{sha1}") unless Cnvrg::CLI.is_response_success response, false
@@ -1,19 +1,71 @@
1
1
  module Cnvrg
2
2
  class Flows
3
- def initialize(flow_slug)
4
- @project = Cnvrg::Project.new(Cnvrg::CLI.get_project_home)
5
- @flow_slug = flow_slug
3
+ def initialize(flow_slug, project: nil)
4
+ @project = project || Cnvrg::Project.new(Cnvrg::CLI.get_project_home)
5
+ @flow_info= Flows.resolve_flow_title(flow_slug, project)
6
+ @slug = @flow_info["slug"]
6
7
  @tasks = {}
7
8
  @relations = {}
8
9
  @title = nil
9
- @slug = nil
10
- @base_resource = @project.base_resource + "flows"
11
-
10
+ @base_resource = @project.base_resource + "flows/#{@slug}"
11
+ @public_url = "#{@project.url}/flows/#{@slug}"
12
12
  # self.reload_flow
13
13
  end
14
14
 
15
- def self.create_flow(path, flow)
16
- File.open(path, "w"){|file| file.write flow.to_yaml}
15
+ def self.resolve_flow_title(title, project)
16
+ resp = Cnvrg::API.request("#{project.base_resource}/flows", 'GET')
17
+ if resp.blank?
18
+ raise StandardError.new("Can't resolve flow")
19
+ end
20
+ res = resp["result"].find{|flow| flow["slug"].downcase == title.downcase}
21
+ res ||= resp["result"].find{|flow| flow["title"].downcase == title.downcase}
22
+ if res.blank?
23
+ raise StandardError.new("Can't find flow with title #{title}")
24
+ end
25
+ res
26
+ end
27
+
28
+ def edit_href
29
+ "#{@public_url}/flow_versions/new"
30
+ end
31
+
32
+ def edit_version_href(version)
33
+ return "#{edit_href}?flow_version_slug=#{version}"
34
+ end
35
+
36
+ def version_href(version=nil)
37
+ "#{@base_resource}/flow_versions/#{version || 'latest'}"
38
+ end
39
+
40
+ def export(version, file: nil)
41
+ resp = Cnvrg::API.request(version_href(version), 'GET')
42
+ if resp["status"] != 200
43
+ raise StandardError.new("Cant find flow version: #{version} for flow: #{@slug}")
44
+ end
45
+ flow_version = resp["flow_version"]
46
+ api_recipe = flow_version["api_recipe"]
47
+ file = file.presence || "flow-#{@slug.downcase.gsub("\s", "_")}.yml"
48
+ File.open(file, "w"){|f| f.write api_recipe.to_yaml}
49
+ file
50
+ end
51
+
52
+ def get_version(version)
53
+
54
+
55
+ end
56
+
57
+ def self.create_flow(project, recipe, run: false)
58
+ url = "#{project.base_resource}flows"
59
+ if run
60
+ url += "/run"
61
+ end
62
+ resp = Cnvrg::API.request(url, 'POST', {flow_version: recipe.to_json}) || {}
63
+ if resp["status"] == 200
64
+ return [Flows.new(resp["flow_version"]["flow_id"], project: project), resp["flow_version"]["id"]]
65
+ elsif resp["status"] == 400
66
+ raise StandardError.new(resp["message"])
67
+ end
68
+ raise StandardError.new("Can't create new flow")
17
69
  end
18
70
 
19
71
  def get_flow
@@ -52,11 +104,11 @@ module Cnvrg
52
104
 
53
105
 
54
106
  def run
55
- resp = Cnvrg::API.request("#{@base_resource}/#{@flow_slug}/run", 'POST')
107
+ resp = Cnvrg::API.request("#{@base_resource}/#{@slug}/run", 'POST')
56
108
  if Cnvrg::CLI.is_response_success(resp)
57
109
  return resp
58
110
  end
59
- Cnvrg::CLI.log_message("Cant run flow #{@flow_slug}")
111
+ Cnvrg::CLI.log_message("Cant run flow #{@slug}")
60
112
  end
61
113
 
62
114
  ### in use for yaml file
@@ -428,7 +428,9 @@ module Cnvrg
428
428
  # list.each do |e|
429
429
  Parallel.map(list, in_threads: IDXParallelThreads) do |e|
430
430
  label = e.gsub(self.local_path + "/", "")
431
-
431
+ if not Cnvrg::Files.valid_file_name?(label)
432
+ raise StandardError.new("#{label} is not a valid file name")
433
+ end
432
434
  if list_ignore_new.include? label
433
435
  next
434
436
  end
@@ -1,4 +1,4 @@
1
1
  module Cnvrg
2
- VERSION = '1.6.0.8'
2
+ VERSION = '1.6.0.9'
3
3
  end
4
4
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cnvrg
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.0.8
4
+ version: 1.6.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yochay Ettun
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2019-12-01 00:00:00.000000000 Z
13
+ date: 2019-12-24 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: bundler
@@ -430,6 +430,7 @@ files:
430
430
  - lib/cnvrg/auth.rb
431
431
  - lib/cnvrg/cli.rb
432
432
  - lib/cnvrg/cli/flow.rb
433
+ - lib/cnvrg/cli/library_cli.rb
433
434
  - lib/cnvrg/cli/subcommand.rb
434
435
  - lib/cnvrg/cli/task.rb
435
436
  - lib/cnvrg/colors.rb