cnvrg 2.1.13 → 2.1.14
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Readme.md +4 -1
- data/cnvrg.gemspec +1 -0
- data/lib/cnvrg/dataset.rb +1 -1
- data/lib/cnvrg/helpers.rb +81 -53
- data/lib/cnvrg/project.rb +88 -103
- data/lib/cnvrg/version.rb +1 -1
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 944b6505221d4431fb485095b6005a477376eedbe8f3eeb719e8b91fbce95c1b
|
4
|
+
data.tar.gz: 22b865c7df6e46610041c9e5c2e0d496408e422588955a141cea14926e30c2cb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4396183d600780db95f3cffad3495593d7cd388a37779b160f199cd33ddeb85f4a4deb20133e1ea6932663d80088da3f14e2f4f590938c2b80d8c921044b69aa
|
7
|
+
data.tar.gz: 739e381136764fc68b95cf45007c1281b509b814626a769bf7ffe76369d99f94b08880b3b3f1b71fec011a0b299e36d9b6906d8a520eee4b1fd18b0eeca57f64
|
data/Readme.md
CHANGED
@@ -147,4 +147,7 @@ h2. 🤔 Assumptions
|
|
147
147
|
# *QA tests will be performed in different set ups (aks/eks/gke, windows device, mac m1)*
|
148
148
|
|
149
149
|
|
150
|
-
Note: this is not the most detailed epic, as most things already exist in cliv1 or sdkv2. for any questions please contact [~accountid:5fb5461f4a09640069dbf768]
|
150
|
+
Note: this is not the most detailed epic, as most things already exist in cliv1 or sdkv2. for any questions please contact [~accountid:5fb5461f4a09640069dbf768]
|
151
|
+
## Version v2.1.14
|
152
|
+
2023-05-29
|
153
|
+
* DEV-18350 - Bug: Error occured, undefined method `[]' for false:FalseClass is displayed when running any command inside the debug mode of experiment
|
data/cnvrg.gemspec
CHANGED
data/lib/cnvrg/dataset.rb
CHANGED
@@ -13,7 +13,7 @@ module Cnvrg
|
|
13
13
|
if project_home.present?
|
14
14
|
@local_path = project_home
|
15
15
|
@working_dir = project_home
|
16
|
-
config =
|
16
|
+
config = Cnvrg::Helpers.get_config_v2_dataset(project_home)
|
17
17
|
@title = config[:dataset_name]
|
18
18
|
@slug = config[:dataset_slug]
|
19
19
|
@owner = config[:owner]
|
data/lib/cnvrg/helpers.rb
CHANGED
@@ -2,17 +2,19 @@ module Cnvrg
|
|
2
2
|
module Helpers
|
3
3
|
|
4
4
|
extend self
|
5
|
+
|
5
6
|
def parallel_threads
|
6
7
|
15
|
7
8
|
end
|
8
9
|
|
9
10
|
def self.parallel_options
|
10
11
|
{
|
11
|
-
|
12
|
-
|
13
|
-
|
12
|
+
in_processes: Cnvrg::CLI::ParallelProcesses,
|
13
|
+
in_thread: Cnvrg::CLI::ParallelThreads,
|
14
|
+
isolation: true
|
14
15
|
}
|
15
16
|
end
|
17
|
+
|
16
18
|
def checkmark
|
17
19
|
return "" if Cnvrg::Helpers.windows?
|
18
20
|
checkmark = "\u2713"
|
@@ -47,8 +49,8 @@ module Cnvrg
|
|
47
49
|
home_dir = File.expand_path('~')
|
48
50
|
config = {}
|
49
51
|
begin
|
50
|
-
if File.exist? home_dir+"/.cnvrg/config.yml"
|
51
|
-
config = YAML.load_file(home_dir+"/.cnvrg/config.yml")
|
52
|
+
if File.exist? home_dir + "/.cnvrg/config.yml"
|
53
|
+
config = YAML.load_file(home_dir + "/.cnvrg/config.yml")
|
52
54
|
end
|
53
55
|
end
|
54
56
|
return config
|
@@ -56,17 +58,16 @@ module Cnvrg
|
|
56
58
|
|
57
59
|
def set_config(config)
|
58
60
|
home_dir = File.expand_path('~')
|
59
|
-
File.open("#{home_dir}/.cnvrg/config.yml", "w"){|f| f.write config.to_yaml }
|
61
|
+
File.open("#{home_dir}/.cnvrg/config.yml", "w") { |f| f.write config.to_yaml }
|
60
62
|
return config
|
61
63
|
end
|
62
64
|
|
63
|
-
|
64
65
|
def remote_url
|
65
66
|
home_dir = File.expand_path('~')
|
66
67
|
config = ""
|
67
68
|
begin
|
68
|
-
if File.exist? home_dir+"/.cnvrg/config.yml"
|
69
|
-
config = YAML.load_file(home_dir+"/.cnvrg/config.yml")
|
69
|
+
if File.exist? home_dir + "/.cnvrg/config.yml"
|
70
|
+
config = YAML.load_file(home_dir + "/.cnvrg/config.yml")
|
70
71
|
else
|
71
72
|
return "https://app.cnvrg.io"
|
72
73
|
end
|
@@ -99,10 +100,10 @@ module Cnvrg
|
|
99
100
|
home_dir = File.expand_path('~')
|
100
101
|
config = ""
|
101
102
|
begin
|
102
|
-
if File.exist? home_dir+"/.cnvrg/config.yml"
|
103
|
-
config = YAML.load_file(home_dir+"/.cnvrg/config.yml")
|
103
|
+
if File.exist? home_dir + "/.cnvrg/config.yml"
|
104
|
+
config = YAML.load_file(home_dir + "/.cnvrg/config.yml")
|
104
105
|
else
|
105
|
-
|
106
|
+
return true
|
106
107
|
|
107
108
|
end
|
108
109
|
|
@@ -262,105 +263,105 @@ parameters:
|
|
262
263
|
Process.clock_gettime(Process::CLOCK_MONOTONIC, :microsecond)
|
263
264
|
end
|
264
265
|
|
265
|
-
def decrypt(key,iv,str)
|
266
|
+
def decrypt(key, iv, str)
|
266
267
|
begin
|
267
268
|
|
268
|
-
|
269
|
-
|
270
|
-
|
269
|
+
cipher = OpenSSL::Cipher.new("aes-256-cbc").decrypt
|
270
|
+
cipher.key = key
|
271
|
+
cipher.iv = Base64.decode64 iv.encode('utf-8')
|
271
272
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
273
|
+
result = Base64.decode64 (str.encode('utf-8'))
|
274
|
+
result = cipher.update(result)
|
275
|
+
result << cipher.final
|
276
|
+
return result.force_encoding('utf-8')
|
276
277
|
|
277
|
-
|
278
|
+
# return result
|
278
279
|
rescue => e
|
279
280
|
puts e
|
280
281
|
|
281
|
-
|
282
282
|
end
|
283
283
|
|
284
|
-
|
285
284
|
end
|
286
285
|
|
287
286
|
# memory
|
288
287
|
#
|
289
|
-
def get_mem(pid)
|
290
|
-
end
|
288
|
+
def get_mem(pid) end
|
291
289
|
|
292
|
-
def get_s3_props(files)
|
290
|
+
def get_s3_props(files)
|
291
|
+
#will return client and decryptor
|
293
292
|
sts_path = files["path_sts"]
|
294
293
|
retries = 0
|
295
|
-
success= false
|
294
|
+
success = false
|
296
295
|
while !success and retries < 20
|
297
296
|
begin
|
298
297
|
if !Helpers.is_verify_ssl
|
299
|
-
body = open(sts_path, {ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE}).read
|
298
|
+
body = open(sts_path, { ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE }).read
|
300
299
|
else
|
301
300
|
body = open(sts_path).read
|
302
301
|
end
|
303
302
|
success = true
|
304
303
|
rescue => e
|
305
|
-
retries +=1
|
304
|
+
retries += 1
|
306
305
|
sleep(5)
|
307
306
|
|
308
307
|
end
|
309
308
|
end
|
310
309
|
if !success
|
311
|
-
return Cnvrg::Result.new(false,"couldn't download some files", "error in sts", ""
|
310
|
+
return Cnvrg::Result.new(false, "couldn't download some files", "error in sts", "")
|
312
311
|
end
|
313
312
|
split = body.split("\n")
|
314
313
|
key = split[0]
|
315
314
|
iv = split[1]
|
316
315
|
|
317
|
-
access =
|
316
|
+
access = Cnvrg::Helpers.decrypt(key, iv, files["sts_a"])
|
318
317
|
|
319
|
-
secret =
|
318
|
+
secret = Cnvrg::Helpers.decrypt(key, iv, files["sts_s"])
|
320
319
|
|
321
|
-
session =
|
322
|
-
region =
|
320
|
+
session = Cnvrg::Helpers.decrypt(key, iv, files["sts_st"])
|
321
|
+
region = Cnvrg::Helpers.decrypt(key, iv, files["region"])
|
323
322
|
|
324
|
-
bucket =
|
323
|
+
bucket = Cnvrg::Helpers.decrypt(key, iv, files["bucket"])
|
325
324
|
is_s3 = files["is_s3"]
|
326
|
-
server_side_encryption =files["server_side_encryption"]
|
325
|
+
server_side_encryption = files["server_side_encryption"]
|
327
326
|
|
328
327
|
if is_s3 or is_s3.nil?
|
329
328
|
client = Aws::S3::Client.new(
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
329
|
+
:access_key_id => access,
|
330
|
+
:secret_access_key => secret,
|
331
|
+
:session_token => session,
|
332
|
+
:region => region,
|
333
|
+
:http_open_timeout => 60, :retry_limit => 20)
|
335
334
|
use_accelerate_endpoint = true
|
336
335
|
else
|
337
336
|
|
338
|
-
endpoint = Cnvrg::Helpers.decrypt(key,iv, files["endpoint"])
|
337
|
+
endpoint = Cnvrg::Helpers.decrypt(key, iv, files["endpoint"])
|
339
338
|
client = Aws::S3::Client.new(
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
339
|
+
:access_key_id => access,
|
340
|
+
:secret_access_key => secret,
|
341
|
+
:region => region,
|
342
|
+
:endpoint => endpoint, :force_path_style => true, :ssl_verify_peer => false,
|
343
|
+
:http_open_timeout => 60, :retry_limit => 20)
|
345
344
|
use_accelerate_endpoint = false
|
346
345
|
end
|
347
346
|
|
348
347
|
if !server_side_encryption
|
349
|
-
upload_options = {:use_accelerate_endpoint => use_accelerate_endpoint}
|
348
|
+
upload_options = { :use_accelerate_endpoint => use_accelerate_endpoint }
|
350
349
|
else
|
351
|
-
upload_options = {:use_accelerate_endpoint => use_accelerate_endpoint, :server_side_encryption => server_side_encryption}
|
350
|
+
upload_options = { :use_accelerate_endpoint => use_accelerate_endpoint, :server_side_encryption => server_side_encryption }
|
352
351
|
end
|
353
|
-
return {client: client, key: key, iv: iv, bucket: bucket, upload_options: upload_options}
|
352
|
+
return { client: client, key: key, iv: iv, bucket: bucket, upload_options: upload_options }
|
354
353
|
end
|
355
354
|
|
356
355
|
def get_experiment_events_log_from_server(exp, project, commit: nil)
|
357
356
|
dest_dir = exp["slug"]
|
358
357
|
commit = commit || exp["end_commit"]
|
359
358
|
response = project.clone(0, commit)
|
360
|
-
Cnvrg::CLI.is_response_success(response, should_exit=false)
|
359
|
+
Cnvrg::CLI.is_response_success(response, should_exit = false)
|
361
360
|
commit_sha1 = response["result"]["commit"]
|
362
361
|
files = response["result"]["tree"].keys
|
363
|
-
files = files.select do |f|
|
362
|
+
files = files.select do |f|
|
363
|
+
f.include?("tfevents")
|
364
|
+
end
|
364
365
|
@files = Cnvrg::Files.new(project.owner, project.slug, project_home: "", project: project)
|
365
366
|
@files.download_files(files, commit_sha1, progress: nil)
|
366
367
|
FileUtils.rm_rf("#{dest_dir}")
|
@@ -374,6 +375,33 @@ parameters:
|
|
374
375
|
end
|
375
376
|
return num_of_new_files
|
376
377
|
end
|
377
|
-
end
|
378
378
|
|
379
|
+
def get_config_v2_project(dir, owner, slug)
|
380
|
+
config = {}
|
381
|
+
if File.exist? dir + "/.cnvrg/config.yml"
|
382
|
+
config = YAML.load_file(dir + "/.cnvrg/config.yml")
|
383
|
+
elsif File.exist? dir + "/.cnvrg/cnvrg.config"
|
384
|
+
cnvrgv2_config = YAML.load_file(dir + "/.cnvrg/cnvrg.config")
|
385
|
+
config[:project_name] = cnvrgv2_config["project_slug"]
|
386
|
+
config[:project_slug] = cnvrgv2_config["project_slug"]
|
387
|
+
config[:owner] = ENV['CNVRG_OWNER']
|
388
|
+
config[:git] = cnvrgv2_config["git"] || false
|
389
|
+
else
|
390
|
+
return { owner: owner, project_slug: slug }
|
391
|
+
end
|
392
|
+
config
|
393
|
+
end
|
394
|
+
def get_config_v2_dataset(dir)
|
395
|
+
config = {}
|
396
|
+
if File.exist? dir + "/.cnvrg/config.yml"
|
397
|
+
config = YAML.load_file(dir + "/.cnvrg/config.yml")
|
398
|
+
elsif File.exist? dir + "/.cnvrg/cnvrg.config"
|
399
|
+
cnvrgv2_config = YAML.load_file(dir + "/.cnvrg/cnvrg.config")
|
400
|
+
config[:dataset_name] = cnvrgv2_config["dataset_slug"]
|
401
|
+
config[:dataset_slug] = cnvrgv2_config["dataset_slug"]
|
402
|
+
config[:owner] = ENV['CNVRG_OWNER']
|
403
|
+
end
|
404
|
+
config
|
405
|
+
end
|
406
|
+
end
|
379
407
|
end
|
data/lib/cnvrg/project.rb
CHANGED
@@ -8,12 +8,12 @@ module Cnvrg
|
|
8
8
|
RemoteURL ||= "https://cnvrg.io"
|
9
9
|
IDXParallelThreads ||= Cnvrg::Helpers.parallel_threads
|
10
10
|
|
11
|
-
|
12
|
-
def initialize(project_home=nil, slug: nil, owner: nil)
|
11
|
+
def initialize(project_home = nil, slug: nil, owner: nil)
|
13
12
|
begin
|
14
13
|
@local_path = project_home
|
15
14
|
@working_dir = project_home
|
16
|
-
|
15
|
+
# read from env and new config file
|
16
|
+
config = Cnvrg::Helpers.get_config_v2_project(project_home, owner, slug)
|
17
17
|
@title = config[:project_name]
|
18
18
|
@slug = config[:project_slug]
|
19
19
|
@owner = config[:owner]
|
@@ -29,12 +29,10 @@ module Cnvrg
|
|
29
29
|
"users/#{@owner}/projects/#{@slug}/"
|
30
30
|
end
|
31
31
|
|
32
|
-
|
33
32
|
def last_local_commit
|
34
33
|
YAML.load_file(@local_path + "/.cnvrg/idx.yml")[:commit] rescue nil
|
35
34
|
end
|
36
35
|
|
37
|
-
|
38
36
|
def url
|
39
37
|
url = Cnvrg::Helpers.remote_url
|
40
38
|
"#{url}/#{self.owner}/projects/#{self.slug}"
|
@@ -142,11 +140,10 @@ module Cnvrg
|
|
142
140
|
]
|
143
141
|
end
|
144
142
|
|
145
|
-
|
146
143
|
list_files = [
|
147
|
-
|
148
|
-
|
149
|
-
|
144
|
+
project_name + "/README.md",
|
145
|
+
project_name + "/.cnvrgignore",
|
146
|
+
project_name + "/.cnvrg/config.yml"
|
150
147
|
]
|
151
148
|
cnvrgreadme = Helpers.readme_content
|
152
149
|
cnvrgignore = Helpers.cnvrgignore_content
|
@@ -155,22 +152,22 @@ module Cnvrg
|
|
155
152
|
begin
|
156
153
|
|
157
154
|
owner = Cnvrg::CLI.get_owner()
|
158
|
-
response = Cnvrg::API.request("cli/create_project", 'POST', {title: project_name, owner: owner, is_docker: with_docker, bucket: bucket})
|
155
|
+
response = Cnvrg::API.request("cli/create_project", 'POST', { title: project_name, owner: owner, is_docker: with_docker, bucket: bucket })
|
159
156
|
Cnvrg::CLI.is_response_success(response)
|
160
157
|
response = JSON.parse response["result"]
|
161
158
|
project_slug = response["slug"]
|
162
159
|
|
163
|
-
config = {project_name: project_name,
|
164
|
-
|
165
|
-
|
166
|
-
|
160
|
+
config = { project_name: project_name,
|
161
|
+
project_slug: project_slug,
|
162
|
+
owner: owner,
|
163
|
+
docker: with_docker }
|
167
164
|
FileUtils.mkdir_p list_dirs
|
168
165
|
FileUtils.touch list_files
|
169
166
|
|
170
|
-
File.open(project_name + "/.cnvrg/config.yml", "w+") {|f| f.write config.to_yaml}
|
171
|
-
File.open(project_name + "/.cnvrgignore", "w+") {|f| f.write cnvrgignore}
|
172
|
-
File.open(project_name + "/README.md", "w+") {|f| f.write cnvrgreadme}
|
173
|
-
File.open(project_name + "/src/hyper.yaml", "w+") {|f| f.write cnvrghyper}
|
167
|
+
File.open(project_name + "/.cnvrg/config.yml", "w+") { |f| f.write config.to_yaml }
|
168
|
+
File.open(project_name + "/.cnvrgignore", "w+") { |f| f.write cnvrgignore }
|
169
|
+
File.open(project_name + "/README.md", "w+") { |f| f.write cnvrgreadme }
|
170
|
+
File.open(project_name + "/src/hyper.yaml", "w+") { |f| f.write cnvrghyper }
|
174
171
|
|
175
172
|
rescue
|
176
173
|
return false
|
@@ -183,32 +180,32 @@ module Cnvrg
|
|
183
180
|
list_dirs = [".cnvrg"
|
184
181
|
]
|
185
182
|
list_files = [
|
186
|
-
|
183
|
+
".cnvrg/config.yml"
|
187
184
|
]
|
188
185
|
if !ignore_exits
|
189
186
|
list_files <<
|
190
|
-
|
187
|
+
".cnvrgignore"
|
191
188
|
end
|
192
189
|
|
193
190
|
cnvrgreadme = Helpers.readme_content
|
194
191
|
cnvrgignore = Helpers.cnvrgignore_content
|
195
192
|
begin
|
196
|
-
response = Cnvrg::API.request("cli/create_project", 'POST', {title: project_name, owner: owner, is_docker: docker, bucket: bucket})
|
193
|
+
response = Cnvrg::API.request("cli/create_project", 'POST', { title: project_name, owner: owner, is_docker: docker, bucket: bucket })
|
197
194
|
Cnvrg::CLI.is_response_success(response)
|
198
195
|
response = JSON.parse response["result"]
|
199
196
|
project_slug = response["slug"]
|
200
197
|
|
201
|
-
config = {project_name: project_name,
|
202
|
-
|
203
|
-
|
204
|
-
|
198
|
+
config = { project_name: project_name,
|
199
|
+
project_slug: project_slug,
|
200
|
+
owner: owner,
|
201
|
+
git: git }
|
205
202
|
FileUtils.mkdir_p list_dirs
|
206
203
|
FileUtils.touch list_files
|
207
|
-
File.open(".cnvrg/config.yml", "w+") {|f| f.write config.to_yaml}
|
208
|
-
File.open(".cnvrgignore", "w+") {|f| f.write cnvrgignore} unless ignore_exits
|
204
|
+
File.open(".cnvrg/config.yml", "w+") { |f| f.write config.to_yaml }
|
205
|
+
File.open(".cnvrgignore", "w+") { |f| f.write cnvrgignore } unless ignore_exits
|
209
206
|
if !File.exist? "README" and !File.exist? "README.md"
|
210
207
|
FileUtils.touch ["README.md"]
|
211
|
-
File.open("README.md", "w+") {|f| f.write cnvrgreadme}
|
208
|
+
File.open("README.md", "w+") { |f| f.write cnvrgreadme }
|
212
209
|
end
|
213
210
|
|
214
211
|
rescue => e
|
@@ -223,23 +220,21 @@ module Cnvrg
|
|
223
220
|
project_name + "/.cnvrg"
|
224
221
|
]
|
225
222
|
|
226
|
-
|
227
223
|
list_files = [
|
228
|
-
|
229
|
-
|
224
|
+
project_name + "/.cnvrg/config.yml",
|
225
|
+
project_name + "/.cnvrgignore",
|
230
226
|
]
|
231
227
|
begin
|
232
|
-
config = {project_name: project_name,
|
233
|
-
|
234
|
-
|
235
|
-
|
228
|
+
config = { project_name: project_name,
|
229
|
+
project_slug: project_slug,
|
230
|
+
owner: project_owner,
|
231
|
+
git: is_git }
|
236
232
|
FileUtils.mkdir_p list_dirs
|
237
233
|
FileUtils.touch list_files
|
238
234
|
cnvrgignore = Helpers.cnvrgignore_content
|
239
235
|
|
240
|
-
|
241
|
-
File.open(project_name + "/.
|
242
|
-
File.open(project_name + "/.cnvrgignore", "w+") {|f| f.write cnvrgignore}
|
236
|
+
File.open(project_name + "/.cnvrg/config.yml", "w+") { |f| f.write config.to_yaml }
|
237
|
+
File.open(project_name + "/.cnvrgignore", "w+") { |f| f.write cnvrgignore }
|
243
238
|
|
244
239
|
rescue
|
245
240
|
return false
|
@@ -256,11 +251,11 @@ module Cnvrg
|
|
256
251
|
if !ignore_exits
|
257
252
|
begin
|
258
253
|
list_files = [
|
259
|
-
|
254
|
+
path
|
260
255
|
]
|
261
256
|
FileUtils.touch list_files
|
262
257
|
cnvrgignore = Helpers.cnvrgignore_content
|
263
|
-
File.open(path, "w+") {|f| f.write cnvrgignore}
|
258
|
+
File.open(path, "w+") { |f| f.write cnvrgignore }
|
264
259
|
rescue => e
|
265
260
|
return false
|
266
261
|
end
|
@@ -272,32 +267,28 @@ module Cnvrg
|
|
272
267
|
cli = Cnvrg::CLI.new()
|
273
268
|
begin
|
274
269
|
list_dirs = [
|
275
|
-
|
270
|
+
".cnvrg"
|
276
271
|
]
|
277
272
|
|
278
|
-
|
279
273
|
list_files = [
|
280
|
-
|
274
|
+
".cnvrg/config.yml",
|
281
275
|
|
282
276
|
]
|
283
277
|
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
git: is_git
|
278
|
+
config = { project_name: project_name,
|
279
|
+
project_slug: project_slug,
|
280
|
+
owner: project_owner,
|
281
|
+
git: is_git
|
289
282
|
}
|
290
283
|
FileUtils.mkdir_p list_dirs
|
291
284
|
FileUtils.touch list_files
|
292
285
|
|
293
|
-
|
294
|
-
File.open(".cnvrg/config.yml", "w+") {|f| f.write config.to_yaml}
|
286
|
+
File.open(".cnvrg/config.yml", "w+") { |f| f.write config.to_yaml }
|
295
287
|
if !File.exist? ".cnvrgignore"
|
296
288
|
FileUtils.touch ".cnvrgignore"
|
297
289
|
list_files << ".cnvrgignore"
|
298
290
|
cnvrgignore = Helpers.cnvrgignore_content
|
299
|
-
File.open(".cnvrgignore", "w+") {|f| f.write cnvrgignore}
|
300
|
-
|
291
|
+
File.open(".cnvrgignore", "w+") { |f| f.write cnvrgignore }
|
301
292
|
|
302
293
|
end
|
303
294
|
true
|
@@ -311,7 +302,7 @@ module Cnvrg
|
|
311
302
|
def update_is_new_branch(new_branch)
|
312
303
|
config = YAML.load_file(@working_dir + "/.cnvrg/config.yml")
|
313
304
|
config[:new_branch] = new_branch
|
314
|
-
File.open(@working_dir + "/.cnvrg/config.yml", "w+") {|f| f.write config.to_yaml}
|
305
|
+
File.open(@working_dir + "/.cnvrg/config.yml", "w+") { |f| f.write config.to_yaml }
|
315
306
|
end
|
316
307
|
|
317
308
|
def get_new_branch
|
@@ -347,7 +338,7 @@ module Cnvrg
|
|
347
338
|
end
|
348
339
|
|
349
340
|
def get_storage_client_fallback
|
350
|
-
response = Cnvrg::API.request("users/#{@owner}/projects/#{@slug}/download_files", "POST", {files: [], commit: get_latest_commit})
|
341
|
+
response = Cnvrg::API.request("users/#{@owner}/projects/#{@slug}/download_files", "POST", { files: [], commit: get_latest_commit })
|
351
342
|
raise StandardError.new("Can't find project credentials") unless Cnvrg::CLI.is_response_success(response, false)
|
352
343
|
files = response['result']
|
353
344
|
storage = files['is_s3'] ? 's3' : 'minio'
|
@@ -368,13 +359,13 @@ module Cnvrg
|
|
368
359
|
if slug.blank? or owner.blank? or name.blank?
|
369
360
|
return
|
370
361
|
end
|
371
|
-
File.open(@working_dir + "/.cnvrg/config.yml", "w+") {|f| f.write config.to_yaml}
|
362
|
+
File.open(@working_dir + "/.cnvrg/config.yml", "w+") { |f| f.write config.to_yaml }
|
372
363
|
end
|
373
364
|
|
374
365
|
def remove_new_branch
|
375
366
|
config = YAML.load_file(@working_dir + "/.cnvrg/config.yml")
|
376
367
|
new_config = config.except(:new_branch)
|
377
|
-
File.open(@working_dir + "/.cnvrg/config.yml", "w+") {|f| f.write new_config.to_yaml}
|
368
|
+
File.open(@working_dir + "/.cnvrg/config.yml", "w+") { |f| f.write new_config.to_yaml }
|
378
369
|
end
|
379
370
|
|
380
371
|
def generate_git_diff
|
@@ -424,16 +415,15 @@ module Cnvrg
|
|
424
415
|
# upload_list << output_dir + "/"
|
425
416
|
# end
|
426
417
|
|
427
|
-
|
428
418
|
return upload_list
|
429
419
|
|
430
420
|
end
|
431
421
|
|
432
422
|
def generate_idx(deploy: false, files: [])
|
433
423
|
if File.exists? "#{self.local_path}/.cnvrg/idx.yml"
|
434
|
-
old_idx = YAML.load_file("#{self.local_path}/.cnvrg/idx.yml") rescue {:tree => {}, :commit => nil}
|
424
|
+
old_idx = YAML.load_file("#{self.local_path}/.cnvrg/idx.yml") rescue { :tree => {}, :commit => nil }
|
435
425
|
else
|
436
|
-
old_idx = {:tree => {}, :commit => nil}
|
426
|
+
old_idx = { :tree => {}, :commit => nil }
|
437
427
|
end
|
438
428
|
|
439
429
|
tree_idx = Hash.new(0)
|
@@ -441,7 +431,7 @@ module Cnvrg
|
|
441
431
|
### if file specified, just take them, dont calculate everything from scratch
|
442
432
|
list_ignore = self.get_ignore_list()
|
443
433
|
if files.blank?
|
444
|
-
list = Dir.glob("#{self.local_path}/**/*", File::FNM_DOTMATCH).reject {|x| (x =~ /\/\.{1,2}$/) or (x =~ /^#{self.local_path}\/\.cnvrg\/*/) or (x =~ /^#{self.local_path}\/\.git\/*/) or (x =~ /^#{self.local_path}\/\.cnvrgignore.conflict*/) and not (x =~ /^#{self.local_path}\/\.cnvrgignore/)}
|
434
|
+
list = Dir.glob("#{self.local_path}/**/*", File::FNM_DOTMATCH).reject { |x| (x =~ /\/\.{1,2}$/) or (x =~ /^#{self.local_path}\/\.cnvrg\/*/) or (x =~ /^#{self.local_path}\/\.git\/*/) or (x =~ /^#{self.local_path}\/\.cnvrgignore.conflict*/) and not (x =~ /^#{self.local_path}\/\.cnvrgignore/) }
|
445
435
|
else
|
446
436
|
list = files
|
447
437
|
end
|
@@ -449,7 +439,7 @@ module Cnvrg
|
|
449
439
|
list_ignore += ["main.py", "main.pyc", "__init__.py", "uwsgi.ini"]
|
450
440
|
list_ignore.flatten!
|
451
441
|
end
|
452
|
-
list_ignore_new = list_ignore.map{|x| x.gsub("//","/")} rescue []
|
442
|
+
list_ignore_new = list_ignore.map { |x| x.gsub("//", "/") } rescue []
|
453
443
|
# list.each do |e|
|
454
444
|
Parallel.map(list, in_threads: IDXParallelThreads) do |e|
|
455
445
|
label = e.sub(self.local_path + "/", "")
|
@@ -474,23 +464,23 @@ module Cnvrg
|
|
474
464
|
end
|
475
465
|
|
476
466
|
if old_idx.nil? or old_idx.to_h[:tree].nil?
|
477
|
-
tree_idx[label] = {sha1: sha1, commit_time: nil, last_modified: last_modified}
|
467
|
+
tree_idx[label] = { sha1: sha1, commit_time: nil, last_modified: last_modified }
|
478
468
|
elsif file_in_idx.nil? or file_in_idx[:sha1] != sha1 or file_in_idx[:last_modified].blank? or file_in_idx[:last_modified] != last_modified
|
479
|
-
tree_idx[label] = {sha1: sha1, commit_time: nil, last_modified: last_modified}
|
469
|
+
tree_idx[label] = { sha1: sha1, commit_time: nil, last_modified: last_modified }
|
480
470
|
else
|
481
471
|
tree_idx[label] = old_idx[:tree][label]
|
482
472
|
end
|
483
473
|
end
|
484
474
|
end
|
485
475
|
old_idx[:tree] = tree_idx
|
486
|
-
File.open("#{self.local_path}/.cnvrg/idx.yml", 'w') {|f| f.write old_idx.to_yaml}
|
476
|
+
File.open("#{self.local_path}/.cnvrg/idx.yml", 'w') { |f| f.write old_idx.to_yaml }
|
487
477
|
return YAML.load_file("#{self.local_path}/.cnvrg/idx.yml")
|
488
478
|
end
|
489
479
|
|
490
480
|
def get_idx
|
491
481
|
unless File.exists? "#{self.local_path}/.cnvrg/idx.yml"
|
492
|
-
empty_idx = {:commit => nil, :tree => {}}
|
493
|
-
File.open("#{self.local_path}/.cnvrg/idx.yml", 'w') {|f| f.write empty_idx.to_yaml}
|
482
|
+
empty_idx = { :commit => nil, :tree => {} }
|
483
|
+
File.open("#{self.local_path}/.cnvrg/idx.yml", 'w') { |f| f.write empty_idx.to_yaml }
|
494
484
|
return empty_idx
|
495
485
|
end
|
496
486
|
YAML.load_file("#{self.local_path}/.cnvrg/idx.yml")
|
@@ -498,16 +488,16 @@ module Cnvrg
|
|
498
488
|
|
499
489
|
def set_idx(idx)
|
500
490
|
FileUtils.mkdir_p("#{self.local_path}/.cnvrg")
|
501
|
-
File.open("#{self.local_path}/.cnvrg/idx.yml", 'w') {|f| f.write idx.to_yaml}
|
491
|
+
File.open("#{self.local_path}/.cnvrg/idx.yml", 'w') { |f| f.write idx.to_yaml }
|
502
492
|
end
|
503
493
|
|
504
494
|
def clone(remote = 0, commit)
|
505
|
-
response = Cnvrg::API.request("users/#{self.owner}/projects/#{self.slug}/clone", 'POST', {project_slug: self.slug, remote: remote, commit: commit})
|
495
|
+
response = Cnvrg::API.request("users/#{self.owner}/projects/#{self.slug}/clone", 'POST', { project_slug: self.slug, remote: remote, commit: commit })
|
506
496
|
return response
|
507
497
|
end
|
508
498
|
|
509
499
|
def git_download_commit(commit)
|
510
|
-
response = Cnvrg::API.request("users/#{self.owner}/projects/#{self.slug}/git_download_commit", 'POST', {commit_sha1: commit})
|
500
|
+
response = Cnvrg::API.request("users/#{self.owner}/projects/#{self.slug}/git_download_commit", 'POST', { commit_sha1: commit })
|
511
501
|
CLI.is_response_success(response, true)
|
512
502
|
return response
|
513
503
|
end
|
@@ -519,7 +509,7 @@ module Cnvrg
|
|
519
509
|
return commit
|
520
510
|
end
|
521
511
|
|
522
|
-
def compare_idx(new_branch, force:false, deploy: false, in_exp:false, specific_files: [], download: false)
|
512
|
+
def compare_idx(new_branch, force: false, deploy: false, in_exp: false, specific_files: [], download: false)
|
523
513
|
is_download = download
|
524
514
|
if is_download
|
525
515
|
local_idx = self.get_idx
|
@@ -550,8 +540,8 @@ module Cnvrg
|
|
550
540
|
local_idx[:tree] = {} if Cnvrg::Helpers.server_version > 0
|
551
541
|
end
|
552
542
|
|
553
|
-
response = Cnvrg::API.request(@base_resource + "status", 'POST', {idx: local_idx, new_branch: new_branch,
|
554
|
-
|
543
|
+
response = Cnvrg::API.request(@base_resource + "status", 'POST', { idx: local_idx, new_branch: new_branch,
|
544
|
+
current_commit: commit, ignore: ignore_list, force: force, in_exp: in_exp, download: download })
|
555
545
|
|
556
546
|
CLI.is_response_success(response, true)
|
557
547
|
if is_download
|
@@ -565,13 +555,13 @@ module Cnvrg
|
|
565
555
|
end
|
566
556
|
@files = self.get_files
|
567
557
|
local_tree = @files.calculate_sha1(resolve.keys)
|
568
|
-
changed_files = resolve.keys.select {|file| local_tree[file] != resolve[file]}
|
558
|
+
changed_files = resolve.keys.select { |file| local_tree[file] != resolve[file] }
|
569
559
|
|
570
560
|
# means that the user changed the file locally
|
571
561
|
response['result']['tree']['update_local'] = changed_files
|
572
562
|
|
573
563
|
# means that we already downloaded this file and we dont need it anymore
|
574
|
-
downloaded_files = destination_files.keys.select {|file| local_tree[file] == destination_files[file]}
|
564
|
+
downloaded_files = destination_files.keys.select { |file| local_tree[file] == destination_files[file] }
|
575
565
|
response['result']['tree']['added'] -= downloaded_files
|
576
566
|
response['result']['tree']['updated_on_server'] -= downloaded_files
|
577
567
|
end
|
@@ -590,8 +580,8 @@ module Cnvrg
|
|
590
580
|
tree = local_idx[:tree]
|
591
581
|
response = Cnvrg::API.request("users/#{self.owner}/projects/#{self.slug}/jump_to",
|
592
582
|
'POST',
|
593
|
-
{tree: tree, ignore: ignore_list,
|
594
|
-
|
583
|
+
{ tree: tree, ignore: ignore_list,
|
584
|
+
dest_commit: destination, current_commit: current_commit })
|
595
585
|
CLI.is_response_success(response, false)
|
596
586
|
response
|
597
587
|
end
|
@@ -601,7 +591,7 @@ module Cnvrg
|
|
601
591
|
@is_branch = !is_latest
|
602
592
|
config[:is_branch] = @is_branch
|
603
593
|
|
604
|
-
File.open(@working_dir + "/.cnvrg/config.yml", 'w') {|f| f.write config.to_yaml}
|
594
|
+
File.open(@working_dir + "/.cnvrg/config.yml", 'w') { |f| f.write config.to_yaml }
|
605
595
|
|
606
596
|
end
|
607
597
|
|
@@ -609,7 +599,7 @@ module Cnvrg
|
|
609
599
|
if commit.nil? or commit.empty?
|
610
600
|
commit = last_local_commit
|
611
601
|
end
|
612
|
-
response = Cnvrg::API.request("users/#{self.owner}/projects/#{self.slug}/commit/compare", 'POST', {current_commit: commit})
|
602
|
+
response = Cnvrg::API.request("users/#{self.owner}/projects/#{self.slug}/commit/compare", 'POST', { current_commit: commit })
|
613
603
|
CLI.is_response_success(response, false)
|
614
604
|
update_is_new_branch(response["result"]["new_branch"])
|
615
605
|
return response["result"]["new_branch"]
|
@@ -621,20 +611,20 @@ module Cnvrg
|
|
621
611
|
files.each do |path|
|
622
612
|
idx_hash[:tree].to_h[path].to_h[:commit_time] = commit_time
|
623
613
|
end
|
624
|
-
File.open("#{self.local_path}/.cnvrg/idx.yml", 'w') {|f| f.write idx_hash.to_yaml}
|
614
|
+
File.open("#{self.local_path}/.cnvrg/idx.yml", 'w') { |f| f.write idx_hash.to_yaml }
|
625
615
|
|
626
616
|
return true
|
627
617
|
end
|
628
618
|
|
629
619
|
def deploy(file_to_run, function, input_params, commit_to_run, instance_type, image_slug, scheduling_query, local_timestamp, workers, file_input, title)
|
630
|
-
response = Cnvrg::API.request("users/#{@owner}/projects/#{@slug}/deploy", 'POST', {file_to_run: file_to_run, function: function,
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
620
|
+
response = Cnvrg::API.request("users/#{@owner}/projects/#{@slug}/deploy", 'POST', { file_to_run: file_to_run, function: function,
|
621
|
+
image_slug: image_slug, input_params: input_params,
|
622
|
+
commit_sha1: commit_to_run,
|
623
|
+
instance_type: instance_type,
|
624
|
+
scheduling_query: scheduling_query,
|
625
|
+
local_timestamp: local_timestamp,
|
626
|
+
workers: workers, file_input: file_input,
|
627
|
+
title: title })
|
638
628
|
return response
|
639
629
|
end
|
640
630
|
|
@@ -644,23 +634,21 @@ module Cnvrg
|
|
644
634
|
return response
|
645
635
|
end
|
646
636
|
|
647
|
-
|
648
637
|
def get_experiments
|
649
638
|
response = Cnvrg::API.request("users/#{self.owner}/projects/#{self.slug}/experiments/list", 'GET')
|
650
639
|
CLI.is_response_success(response)
|
651
640
|
return response
|
652
641
|
end
|
653
642
|
|
654
|
-
|
655
643
|
def get_experiment(slug)
|
656
644
|
response = Cnvrg::API.request("users/#{self.owner}/projects/#{self.slug}/experiments/#{slug}", 'GET')
|
657
|
-
response['status']=200
|
645
|
+
response['status'] = 200
|
658
646
|
CLI.is_response_success(response)
|
659
647
|
return response
|
660
648
|
end
|
661
649
|
|
662
650
|
def fetch_webapp_slugs(webapp_slug, slugs: nil)
|
663
|
-
response = Cnvrg::API_V2.request("#{self.owner}/projects/#{self.slug}/webapps/#{webapp_slug}"
|
651
|
+
response = Cnvrg::API_V2.request("#{self.owner}/projects/#{self.slug}/webapps/#{webapp_slug}", 'GET')
|
664
652
|
|
665
653
|
if response.key?("experiments")
|
666
654
|
return response["experiments"]
|
@@ -675,7 +663,7 @@ module Cnvrg
|
|
675
663
|
idx_hash[:commit] = commit
|
676
664
|
self.set_on_branch(latest) unless latest.nil?
|
677
665
|
|
678
|
-
File.open("#{self.local_path}/.cnvrg/idx.yml", 'w') {|f| f.write idx_hash.to_yaml}
|
666
|
+
File.open("#{self.local_path}/.cnvrg/idx.yml", 'w') { |f| f.write idx_hash.to_yaml }
|
679
667
|
return true
|
680
668
|
end
|
681
669
|
|
@@ -696,12 +684,11 @@ module Cnvrg
|
|
696
684
|
@machines || []
|
697
685
|
end
|
698
686
|
|
699
|
-
|
700
687
|
def update_job_jupyter_token(job_type, job_id, token)
|
701
688
|
owner = self.owner || ENV['CNVRG_OWNER']
|
702
689
|
slug = self.slug || ENV['CNVRG_PROJECT']
|
703
690
|
base_url = "users/#{owner}/projects/#{slug}/jobs/#{job_type.underscore}/#{job_id}"
|
704
|
-
Cnvrg::API.request("#{base_url}/update_jupyter_token", "POST", {token: token})
|
691
|
+
Cnvrg::API.request("#{base_url}/update_jupyter_token", "POST", { token: token })
|
705
692
|
end
|
706
693
|
|
707
694
|
def check_machine(machine)
|
@@ -711,7 +698,6 @@ module Cnvrg
|
|
711
698
|
machines.include? machine
|
712
699
|
end
|
713
700
|
|
714
|
-
|
715
701
|
def fetch_project
|
716
702
|
resp = Cnvrg::API.request("users/#{@owner}/projects/#{@slug}/get_project", "GET")
|
717
703
|
res = JSON.parse(resp['result']) rescue nil
|
@@ -733,12 +719,11 @@ module Cnvrg
|
|
733
719
|
raise StandardError.new("Cant find job env variables")
|
734
720
|
end
|
735
721
|
logs.each_slice(10).each do |temp_logs|
|
736
|
-
Cnvrg::API.request("users/#{@owner}/projects/#{@slug}/jobs/#{job_type.underscore}/#{job_id}/log", "POST", {job_type: job_type, job_id: job_id, logs: temp_logs, log_level: level, step: step, timestamp: Time.now})
|
722
|
+
Cnvrg::API.request("users/#{@owner}/projects/#{@slug}/jobs/#{job_type.underscore}/#{job_id}/log", "POST", { job_type: job_type, job_id: job_id, logs: temp_logs, log_level: level, step: step, timestamp: Time.now })
|
737
723
|
sleep(1)
|
738
724
|
end
|
739
725
|
end
|
740
726
|
|
741
|
-
|
742
727
|
def job_commands
|
743
728
|
job_type, job_id = ENV['CNVRG_JOB_TYPE'], ENV['CNVRG_JOB_ID']
|
744
729
|
resp = Cnvrg::API.request("users/#{@owner}/projects/#{@slug}/jobs/#{job_type.underscore}/#{job_id}/commands", "GET")
|
@@ -754,7 +739,7 @@ module Cnvrg
|
|
754
739
|
begin
|
755
740
|
url = URI.parse('http://169.254.169.254/latest/meta-data/spot/termination-time')
|
756
741
|
req = Net::HTTP::Get.new(url.to_s)
|
757
|
-
res = Net::HTTP.start(url.host, url.port) {|http|
|
742
|
+
res = Net::HTTP.start(url.host, url.port) { |http|
|
758
743
|
http.request(req)
|
759
744
|
}
|
760
745
|
unless res.body.include? "404"
|
@@ -776,7 +761,7 @@ module Cnvrg
|
|
776
761
|
end
|
777
762
|
|
778
763
|
def send_restart_request(job_id: nil, job_type: nil, ma_id: nil)
|
779
|
-
Cnvrg::API.request("#{base_resource}/spot_restart", 'POST', {job_type: job_type, job_id: job_id, machine_activity: ma_id})
|
764
|
+
Cnvrg::API.request("#{base_resource}/spot_restart", 'POST', { job_type: job_type, job_id: job_id, machine_activity: ma_id })
|
780
765
|
end
|
781
766
|
|
782
767
|
def get_machine_activity
|
@@ -796,7 +781,7 @@ module Cnvrg
|
|
796
781
|
if job_type.blank? or job_id.blank?
|
797
782
|
raise StandardError.new("Cant find job env variables")
|
798
783
|
end
|
799
|
-
Cnvrg::API.request("users/#{@owner}/projects/#{@slug}/jobs/#{job_type.underscore}/#{job_id}/set_pod_restart", "POST", {job_type: job_type, job_id: job_id})
|
784
|
+
Cnvrg::API.request("users/#{@owner}/projects/#{@slug}/jobs/#{job_type.underscore}/#{job_id}/set_pod_restart", "POST", { job_type: job_type, job_id: job_id })
|
800
785
|
end
|
801
786
|
|
802
787
|
def check_job_pod_restart(job_type: nil, job_id: nil)
|
@@ -805,7 +790,7 @@ module Cnvrg
|
|
805
790
|
if job_type.blank? or job_id.blank?
|
806
791
|
raise StandardError.new("Cant find job env variables")
|
807
792
|
end
|
808
|
-
resp = Cnvrg::API.request("users/#{@owner}/projects/#{@slug}/jobs/#{job_type.underscore}/#{job_id}/check_pod_restart", "GET", {job_type: job_type, job_id: job_id})
|
793
|
+
resp = Cnvrg::API.request("users/#{@owner}/projects/#{@slug}/jobs/#{job_type.underscore}/#{job_id}/check_pod_restart", "GET", { job_type: job_type, job_id: job_id })
|
809
794
|
return [false, false] if resp.blank?
|
810
795
|
Cnvrg::Logger.log_info("Checked for pod restart got response #{resp}")
|
811
796
|
[resp['project_downloaded'], resp['dataset_downloaded']]
|
@@ -817,7 +802,7 @@ module Cnvrg
|
|
817
802
|
if job_type.blank? or job_id.blank?
|
818
803
|
raise StandardError.new("Cant find job env variables")
|
819
804
|
end
|
820
|
-
Cnvrg::API.request("users/#{@owner}/projects/#{@slug}/jobs/#{job_type.underscore}/#{job_id}/pre_pod_restart", "POST", {job_type: job_type, job_id: job_id})
|
805
|
+
Cnvrg::API.request("users/#{@owner}/projects/#{@slug}/jobs/#{job_type.underscore}/#{job_id}/pre_pod_restart", "POST", { job_type: job_type, job_id: job_id })
|
821
806
|
end
|
822
807
|
|
823
808
|
def set_job_started
|
@@ -826,7 +811,7 @@ module Cnvrg
|
|
826
811
|
if job_type.blank? or job_id.blank?
|
827
812
|
raise StandardError.new("Cant find job env variables")
|
828
813
|
end
|
829
|
-
Cnvrg::API.request("users/#{@owner}/projects/#{@slug}/jobs/#{job_type.underscore}/#{job_id}/set_started", "POST", {job_type: job_type, job_id: job_id})
|
814
|
+
Cnvrg::API.request("users/#{@owner}/projects/#{@slug}/jobs/#{job_type.underscore}/#{job_id}/set_started", "POST", { job_type: job_type, job_id: job_id })
|
830
815
|
end
|
831
816
|
|
832
817
|
def self.stop_if_project_present(project_home, project_name, owner)
|
data/lib/cnvrg/version.rb
CHANGED
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: 2.1.
|
4
|
+
version: 2.1.14
|
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: 2023-
|
13
|
+
date: 2023-05-29 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: bundler
|
@@ -434,6 +434,20 @@ dependencies:
|
|
434
434
|
- - ">="
|
435
435
|
- !ruby/object:Gem::Version
|
436
436
|
version: '0'
|
437
|
+
- !ruby/object:Gem::Dependency
|
438
|
+
name: nokogiri
|
439
|
+
requirement: !ruby/object:Gem::Requirement
|
440
|
+
requirements:
|
441
|
+
- - "~>"
|
442
|
+
- !ruby/object:Gem::Version
|
443
|
+
version: 1.13.10
|
444
|
+
type: :runtime
|
445
|
+
prerelease: false
|
446
|
+
version_requirements: !ruby/object:Gem::Requirement
|
447
|
+
requirements:
|
448
|
+
- - "~>"
|
449
|
+
- !ruby/object:Gem::Version
|
450
|
+
version: 1.13.10
|
437
451
|
description: A CLI tool for interacting with cnvrg.io.
|
438
452
|
email:
|
439
453
|
- info@cnvrg.io
|