enfcli 4.1.0.pre.beta → 4.2.1.pre.alpha

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: 27d832f6ee49be44d9463b1b77931d15db08f79f6d382c1bc243635ef9eca137
4
- data.tar.gz: 070eabadf3b810b47c948dd5ca84cc362852dde237dba5a95a7e4e90e011f8b0
3
+ metadata.gz: 27b28906253cb5aa863c719da49ea29361254f44d7cc638e332d531f404e3887
4
+ data.tar.gz: b6c231170699644a0628d9911368b822214b47916c61b66424384d19b5eb48fd
5
5
  SHA512:
6
- metadata.gz: afc068399c4e7477a16388c3ead24346520823cb214b19befe9a4a4d58a19f14b4f45868b31c66b7841c24073970241fb81b68f54e5bc23534d547e586436de4
7
- data.tar.gz: 3d9e53a84414a42d17e35c8c01852af895a149d5e906000d3e84fa7282150d49542a91c130c73b35b8a010139771adcac0267a31991686799b62346f42daf6ba
6
+ metadata.gz: 84a41a3805664bf9e341d6a545daf92bc82c1271f5ccb92695808ae9e4737ecd8c148259d904f28f0bdbf65a8e6f34333c503adccf39b63c261d7b6bb1976d41
7
+ data.tar.gz: 94706731090498b60bde0dcc0181cabf7ba7bddd9088c879cc049f4f7e87990bdf5e5bc05774a61506f3c3b9cffd1dff0314b75fb9fb2b65c43c7fa7875e2549
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- enfcli (4.1.0.pre.beta)
4
+ enfcli (4.2.1.pre.alpha)
5
5
  clipboard
6
6
  ffi
7
7
  readline
@@ -32,13 +32,13 @@ GEM
32
32
  io-console (0.5.6)
33
33
  mime-types (3.3.1)
34
34
  mime-types-data (~> 3.2015)
35
- mime-types-data (3.2019.1009)
35
+ mime-types-data (3.2020.0425)
36
36
  netrc (0.11.0)
37
37
  public_suffix (4.0.3)
38
38
  rake (10.5.0)
39
39
  readline (0.0.2)
40
40
  reline
41
- reline (0.1.3)
41
+ reline (0.1.4)
42
42
  io-console (~> 0.5)
43
43
  rest-client (2.1.0)
44
44
  http-accept (>= 1.7.0, < 2.0)
data/lib/enfapi.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright 2018 Xaptum,Inc
2
+ # Copyright 2020 Xaptum,Inc
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.
@@ -284,6 +284,89 @@ module EnfApi
284
284
  json = EnfApi.to_json(profile_updates)
285
285
  EnfApi::API.instance.put "/api/captive/v1/profile/#{id}", json
286
286
  end
287
+
288
+ # Get the list of devices
289
+ ## NO_TEST
290
+ def list_firmware_images()
291
+ url = "/api/captive/v1/firmware/release"
292
+ EnfApi::API.instance.get url
293
+ end
294
+
295
+ # Upload the firmware image
296
+ # Current version sends an empty body - captive server doesn't support
297
+ # sending the binary image yet.
298
+ ## NO_TEST
299
+ def upload_firmware_image(version, image_name)
300
+ url = "/api/captive/v1/firmware/release/#{version}/#{image_name}"
301
+ EnfApi::API.instance.put url
302
+ end
303
+
304
+ # gets the details for a specific firmware version
305
+ ## NO_TEST
306
+ def get_firmware_info(version)
307
+ url = "/api/captive/v1/firmware/release/#{version}"
308
+ EnfApi::API.instance.get url
309
+ end
310
+
311
+ # creates a new schedule object via POST
312
+ ## NO_TEST
313
+ def create_schedule(sched_hash)
314
+ json = EnfApi.to_json(sched_hash)
315
+ EnfApi::API.instance.post "/api/captive/v1/schedule", json
316
+ end
317
+
318
+ # GETs a list of all existing schedules
319
+ ## NO_TEST
320
+ def list_schedules
321
+ EnfApi::API.instance.get "/api/captive/v1/schedule"
322
+ end
323
+
324
+ # GETs the details of a specific schedule
325
+ ## NO_TEST
326
+ def get_schedule(id)
327
+ EnfApi::API.instance.get "/api/captive/v1/schedule/#{id}"
328
+ end
329
+
330
+ # update the details of a specific schedule
331
+ ## NO_TEST
332
+ def update_schedule(id, sched_hash)
333
+ json = EnfApi.to_json(sched_hash)
334
+ url = "/api/captive/v1/schedule/#{id}"
335
+ EnfApi::API.instance.put url, json
336
+ end
337
+
338
+ # DELETEs the specified schedule
339
+ ## NO_TEST
340
+ def delete_schedule(id)
341
+ EnfApi::API.instance.delete "/api/captive/v1/schedule/#{id}"
342
+ end
343
+
344
+ # GET the list of existing update objects
345
+ ## NO_TEST
346
+ def list_firmware_updates()
347
+ EnfApi::API.instance.get "/api/captive/v1/firmware/update"
348
+ end
349
+
350
+ # GET details of a specified firmware update task
351
+ ## NO_TEST
352
+ def get_firmware_update(update_id)
353
+ EnfApi::API.instance.get "/api/captive/v1/firmware/update/#{update_id}"
354
+ end
355
+
356
+ # POSTs a new firmware-udpate object
357
+ ## NO_TEST
358
+ def create_firmware_update(update_hash)
359
+ json = EnfApi.to_json(update_hash)
360
+ EnfApi::API.instance.post "/api/captive/v1/firmware/update", json
361
+ end
362
+
363
+ # uses PUT to modify an existing firmware update task
364
+ ## NO_TEST
365
+ def modify_firmware_update(update_id, update_hash)
366
+ url = "/api/captive/v1/firmware/update/#{update_id}"
367
+ json = EnfApi.to_json(update_hash)
368
+ EnfApi::API.instance.put url, json
369
+ end
287
370
  end
288
371
 
289
372
  class NetworkManager
@@ -569,6 +652,13 @@ module EnfApi
569
652
  rescue JSON::ParserError => e
570
653
  raise EnfApi::ERROR, "Not Found"
571
654
  end
655
+ when 500
656
+ begin
657
+ JSON.parse(response.body)
658
+ raise EnfApi::ERROR, api_error_msg(from_json(response.body))
659
+ rescue JSON::ParserError => e
660
+ raise EnfApi::ERROR, "Server error was received without details."
661
+ end
572
662
  else
573
663
  raise EnfApi::ERROR, "Unexpected error! Please try again!"
574
664
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #
4
- # Copyright 2018-2019 Xaptum,Inc
4
+ # Copyright 2018-2020 Xaptum,Inc
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
7
7
  # you may not use this file except in compliance with the License.
@@ -278,6 +278,10 @@ module EnfCli
278
278
  desc: "secure-host or passthrough"
279
279
  method_option :'wifi-id', type: :string, default: nil, banner: "WIFI-ID",
280
280
  desc: "WIFI-ID is the UUID of the wifi record that the profile will use. wifi record must already exist"
281
+ method_option :'update-id', type: :string, default: nil,
282
+ banner: "UPDATE-ID",
283
+ desc: "UPDATE-ID is the UUID of the firmware update record. " \
284
+ "The firmware-update record must already exist."
281
285
 
282
286
  def create_profile
283
287
  try_with_rescue_in_session do
@@ -293,6 +297,12 @@ module EnfCli
293
297
  new_profile_hash[:config][:wifi][:id] = wifi
294
298
  end
295
299
 
300
+ fw_update = options[:'update-id']
301
+ if fw_update
302
+ new_profile_hash[:config][:firmware] = {}
303
+ new_profile_hash[:config][:firmware][:id] = fw_update if fw_update
304
+ end
305
+
296
306
  # send the POST to create a new profile
297
307
  profile = EnfApi::Captive.instance.create_profile new_profile_hash
298
308
  display_profile profile
@@ -341,6 +351,10 @@ module EnfCli
341
351
  desc: "secure-host or passthrough"
342
352
  method_option :'wifi-id', type: :string, default: nil, banner: "WIFI-ID",
343
353
  desc: "WIFI-ID is the UUID of the wifi record that the profile will use. wifi record must already exist"
354
+ method_option :'update-id', type: :string, default: nil,
355
+ banner: "UPDATE-ID",
356
+ desc: "UPDATE-ID is the UUID of the firmware update record. " \
357
+ "The firmware-update record must already exist."
344
358
 
345
359
  def update_profile
346
360
  try_with_rescue_in_session do
@@ -348,23 +362,250 @@ module EnfCli
348
362
  name = options[:'profile-name']
349
363
  mode = options[:'device-mode']
350
364
  wifi_id = options[:'wifi-id']
365
+ fw_update = options[:'update-id']
351
366
 
352
- raise "At least one option needs to change." if name == nil && mode == nil && wifi_id == nil
367
+ raise "At least one option needs to change." if name == nil && mode == nil && wifi_id == nil && fw_update == nil
353
368
 
354
369
  update_hash = {}
355
370
  update_hash[:name] = name if name
356
- update_hash[:config] = {} if wifi_id || mode
371
+ update_hash[:config] = {} if wifi_id || mode || fw_update
357
372
  update_hash[:config][:mode] = mode if mode
358
373
  if wifi_id
359
374
  update_hash[:config][:wifi] = {}
360
375
  update_hash[:config][:wifi][:id] = wifi_id
361
376
  end
362
377
 
378
+ if fw_update
379
+ update_hash[:config][:firmware] = {}
380
+ update_hash[:config][:firmware][:id] = fw_update
381
+ end
363
382
  profile = EnfApi::Captive.instance.update_profile id, update_hash
364
383
  display_profile profile
365
384
  end
366
385
  end
367
386
 
387
+ desc "list-firmware-images",
388
+ "Lists all of the available versions of the firmware"
389
+
390
+ def list_firmware_images
391
+ try_with_rescue_in_session do
392
+ data = EnfApi::Captive.instance.list_firmware_images
393
+ display_firmware_image_list data
394
+ end
395
+ end
396
+
397
+ desc "upload-firmware-image",
398
+ "Uploads a new firmware image. Each version will have multiple " \
399
+ "images -- one for each type of hardware. \nNOTE: This version " \
400
+ "doesn't actually upload the file, it merely informs the server of " \
401
+ "its existance."
402
+ method_option :'image-file', required: true, type: :string,
403
+ banner: "<file>", desc: "<file> is the firmware binary image."
404
+ method_option :version, required: true, type: :string,
405
+ banner: "VERSION",
406
+ desc: "VERSION is the release version."
407
+ method_option :'image-name', type: :string, default: nil, banner: "NAME",
408
+ desc: "NAME is the name to associate with the firmware " \
409
+ "image. This is usually the generated filename. " \
410
+ "Defaults to the base name of <file>"
411
+
412
+ def upload_firmware_image
413
+ try_with_rescue_in_session do
414
+ filename = options[:'image-file']
415
+ version = options[:version]
416
+ image_name = options[:'image-name']
417
+
418
+ temp_img_name = File.basename(filename)
419
+ image_name ||= temp_img_name
420
+
421
+ raise "image-name does not match image-file" if temp_img_name != image_name
422
+
423
+ # This version PUTs an empty body
424
+ resp = EnfApi::Captive.instance.upload_firmware_image version, image_name
425
+
426
+ if (resp.code == 200)
427
+ say "Upload complete."
428
+ else
429
+ say "Upload failed with code #{resp.code}"
430
+ end
431
+ end
432
+ end
433
+
434
+ desc "get-firmware-info",
435
+ "Prints details of an existing firmware version."
436
+ method_option :version, required: true, type: :string,
437
+ banner: "VERSION", desc: "VERSION is the release version."
438
+
439
+ def get_firmware_info
440
+ try_with_rescue_in_session do
441
+ version = options[:version]
442
+
443
+ fw_info = EnfApi::Captive.instance.get_firmware_info version
444
+
445
+ display_firmware_detail fw_info
446
+ end
447
+ end
448
+
449
+ desc "create-schedule",
450
+ "Creates a schedule object that will be used to determine when a " \
451
+ "device or group of devices may be upgraded. The schedule will be " \
452
+ "uploaded from a JSON-formatted file."
453
+ method_option :schedule, required: true, type: :string,
454
+ banner: "<file>",
455
+ desc: "<file> is a JSON file containing the desired schedule."
456
+
457
+ def create_schedule
458
+ try_with_rescue_in_session do
459
+ filename = options[:schedule]
460
+
461
+ # reading the whole file - shouldn't get more than a few KB
462
+ content = File.read filename
463
+ sched_hash = JSON.parse(content)
464
+
465
+ resp_data = EnfApi::Captive.instance.create_schedule sched_hash
466
+ display_schedule_detail resp_data
467
+ end
468
+ end
469
+
470
+ desc "list-schedules",
471
+ "Prints a summary list of all existing schedules."
472
+
473
+ def list_schedules
474
+ try_with_rescue_in_session do
475
+ sched_list = EnfApi::Captive.instance.list_schedules
476
+ display_schedule_list sched_list
477
+ end
478
+ end
479
+
480
+ desc "get-schedule",
481
+ "Prints the detail of a specific, existing schedule."
482
+ method_option :'schedule-id', type: :string, required: true, banner: "ID",
483
+ desc: "ID is the system-assigned UUID of the schedule " \
484
+ "to retrieve."
485
+
486
+ def get_schedule
487
+ try_with_rescue_in_session do
488
+ sched_id = options[:'schedule-id']
489
+ sched_data = EnfApi::Captive.instance.get_schedule sched_id
490
+ display_schedule_detail sched_data
491
+ end
492
+ end
493
+
494
+ desc "update-schedule",
495
+ "Modifies an existing schedule by uploading an updated schedule in " \
496
+ "the form of a JSON file."
497
+ method_option :'schedule-id', type: :string, required: true, banner: "ID",
498
+ desc: "ID is the UUID of the schedule to update."
499
+ method_option :schedule, type: :string, required: true, bannder: "<file>",
500
+ desc: "<file> is a JSON file containing the desired schedule."
501
+
502
+ def update_schedule
503
+ try_with_rescue_in_session do
504
+ sched_id = options[:'schedule-id']
505
+ filename = options[:schedule]
506
+
507
+ # read in whole schedule file
508
+ content = File.read filename
509
+ sched_hash = JSON.parse(content)
510
+
511
+ resp_data = EnfApi::Captive.instance.update_schedule sched_id, sched_hash
512
+ display_schedule_detail resp_data
513
+ end
514
+ end
515
+
516
+ desc "delete-schedule",
517
+ "Deletes the specified, existing schedule."
518
+ method_option :'schedule-id', type: :string, required: true, banner: "ID",
519
+ desc: "ID is the system-assigned UUID of the schedule " \
520
+ "to delete."
521
+
522
+ def delete_schedule
523
+ try_with_rescue_in_session do
524
+ sched_id = options[:'schedule-id']
525
+
526
+ # get the name of the schedule
527
+ resp = EnfApi::Captive.instance.get_schedule(sched_id)
528
+ name = resp[:name]
529
+
530
+ resp = EnfApi::Captive.instance.delete_schedule sched_id
531
+
532
+ if (resp.code == 200)
533
+ say "Successfully deleted schedule named: #{name}"
534
+ elsif (resp.code == 409)
535
+ say "Schedule #{name} is being used by pending updates and cannot be deleted."
536
+ else
537
+ say "Failed to delete schedule #{name} with code #{resp.code}"
538
+ end
539
+ end
540
+ end
541
+
542
+ desc "list-firmware-updates",
543
+ "Lists the existing firmware update tasks."
544
+
545
+ def list_firmware_updates
546
+ try_with_rescue_in_session do
547
+ updates_list = EnfApi::Captive.instance.list_firmware_updates
548
+ display_updates_list updates_list
549
+ end
550
+ end
551
+
552
+ desc "get-firmware-update",
553
+ "Prints the details of the specified firmware update task."
554
+ method_option :'update-id', type: :string, required: true, banner: "ID",
555
+ desc: "ID is the UUID of the firmware update task"
556
+
557
+ def get_firmware_update
558
+ try_with_rescue_in_session do
559
+ update_id = options[:'update-id']
560
+
561
+ update_data = EnfApi::Captive.instance.get_firmware_update update_id
562
+ display_update_detail update_data
563
+ end
564
+ end
565
+
566
+ desc "create-firmware-update",
567
+ "Creates a firmware-update task that the system will use to update " \
568
+ "router-card firmware as prescribed in the specified JSON file."
569
+ method_option :update, type: :string, required: true, banner: "<file>",
570
+ desc: "<file> is a JSON file containing the details of " \
571
+ "the update task."
572
+
573
+ def create_firmware_update
574
+ try_with_rescue_in_session do
575
+ filename = options[:update]
576
+
577
+ # reading the whole file - shouldn't get more than a few KB
578
+ content = File.read filename
579
+ update_hash = JSON.parse(content)
580
+
581
+ resp_data = EnfApi::Captive.instance.create_firmware_update update_hash
582
+ display_update_detail resp_data
583
+ end
584
+ end
585
+
586
+ desc "modify-firmware-update",
587
+ "Modifies an existing firmware-update task with the information " \
588
+ "contained in the specified JSON file"
589
+ method_option :'update-id', type: :string, required: true, banner: "ID",
590
+ desc: "ID is the UUID of the firmware update task to be modified."
591
+ method_option :update, type: :string, required: true, banner: "<file>",
592
+ desc: "<file> is a JSON file containing the details of " \
593
+ "the update task."
594
+
595
+ def modify_firmware_update
596
+ try_with_rescue_in_session do
597
+ update_id = options[:'update-id']
598
+ filename = options[:update]
599
+
600
+ # reading the whole file - shouldn't get more than a few KB
601
+ content = File.read filename
602
+ update_hash = JSON.parse(content)
603
+
604
+ resp_data = EnfApi::Captive.instance.modify_firmware_update update_id, update_hash
605
+ display_update_detail resp_data
606
+ end
607
+ end
608
+
368
609
  #########################################################################
369
610
  #
370
611
  # Helper functions
@@ -613,7 +854,6 @@ module EnfCli
613
854
  # [hash[:serial_number], hash[:device_name], hash[:device_address],
614
855
  # hash[:status][:router_mode], hash[:status][:wifi][:connected],
615
856
  # hash[:status][:wifi][:SSID]]
616
-
617
857
  render_table(headings, rows)
618
858
  end
619
859
 
@@ -623,11 +863,20 @@ module EnfCli
623
863
  def display_profile(profile, summary = false)
624
864
  indent = summary ? " " : ""
625
865
 
866
+ firmware = profile[:config][:firmware]
867
+ fw_id = "< not configured >"
868
+ if firmware
869
+ fw_id = firmware[:id]
870
+ fw_version = firmware[:version]
871
+ end
872
+
626
873
  say indent + "Name : #{profile[:name]}", nil, true
627
874
  say indent + "Profile ID : #{profile[:id]}", nil, true
628
875
  say indent + "Configuration version : #{profile[:config][:version]}", nil, true
876
+ say indent + "Firmware Update ID : #{fw_id}", nil, true
629
877
  unless summary
630
- say "Mode : #{profile[:config][:mode]}", nil, true
878
+ say indent + " Update version : #{fw_version}", nil, true if fw_version
879
+ say indent + "Mode : #{profile[:config][:mode]}", nil, true
631
880
  display_wifi_summary profile[:config][:wifi]
632
881
  end
633
882
  end
@@ -654,13 +903,114 @@ module EnfCli
654
903
  # display the profile summary list
655
904
  #
656
905
  def display_profile_list(profiles)
657
- headings = ["Profile Name", "Profile ID", "Version"]
906
+ headings = ["Profile Name", "Profile ID", "Version", "Mode", "Wifi ID", "Firmware Update ID"]
658
907
  rows = profiles.map do |hash|
659
- [hash[:name], hash[:id], hash[:config][:version]]
908
+ config = hash[:config]
909
+ fw_id = config[:firmware] ? config[:firmware][:id] : "none"
910
+ [hash[:name], hash[:id], config[:version], config[:mode],
911
+ config[:wifi][:id], fw_id]
660
912
  end
913
+ render_table(headings, rows)
914
+ end
661
915
 
916
+ #
917
+ # Displays the list of firmware releases
918
+ #
919
+ def display_firmware_image_list(images)
920
+ headings = ["Firmware Version"]
921
+ rows = images.map do |element|
922
+ [element[:version]]
923
+ end
662
924
  render_table(headings, rows)
663
925
  end
926
+
927
+ #
928
+ # Displays firmware detail
929
+ #
930
+ def display_firmware_detail(fw_info)
931
+ version = fw_info[:version] || "< not available >"
932
+ images = fw_info[:images]
933
+
934
+ say "Release version: #{version}"
935
+ display_image_list images
936
+ end
937
+
938
+ #
939
+ # Displays the list of images available for a release version.
940
+ #
941
+ def display_image_list(images)
942
+ headings = ["Image Name", "Hardware Model", "Update Type", "SHA256"]
943
+ rows = images.map do |hash|
944
+ [hash[:name], hash[:model], hash[:type], hash[:sha256]]
945
+ end
946
+ render_table(headings, rows)
947
+ end
948
+
949
+ #
950
+ # Displays the full SCHEDULE object detail
951
+ #
952
+ def display_schedule_detail(sched_data)
953
+ name = sched_data[:name]
954
+ id = sched_data[:id]
955
+ domain = sched_data[:domain]
956
+ times = sched_data[:times]
957
+
958
+ say "Schedule Name : #{name}", nil, true
959
+ say "Shedule ID : #{id}", nil, true
960
+ say "domain : #{domain}", nil, true
961
+ say "Times:", nil, true
962
+ display_time_list(times)
963
+ end
964
+
965
+ #
966
+ # Displays the list of times in a schedule as a table
967
+ #
968
+ def display_time_list(times)
969
+ headings = ["Name", "year", "Month", "Date", "Weekday", "Hour",
970
+ "Minute", "id"]
971
+ rows = times.map do |hash|
972
+ [hash[:name], hash[:year], hash[:month], hash[:day_of_month],
973
+ hash[:day_of_week], hash[:hour], hash[:minute], hash[:id]]
974
+ end
975
+ render_table(headings, rows)
976
+ end
977
+
978
+ #
979
+ # Displays a list of schedules
980
+ #
981
+ def display_schedule_list(sched_list)
982
+ headings = ["Name", "ID"]
983
+ rows = sched_list.map do |hash|
984
+ [hash[:name], hash[:id]]
985
+ end
986
+ render_table(headings, rows)
987
+ end
988
+
989
+ #
990
+ # Displays a list of firmware updates
991
+ #
992
+ def display_updates_list(updates_list)
993
+ headings = ["Update ID", "Update Version"]
994
+ rows = updates_list.map do |hash|
995
+ [hash[:id], hash[:version]]
996
+ end
997
+ render_table(headings, rows)
998
+ end
999
+
1000
+ #
1001
+ # Displays detail of a specific firmware update task
1002
+ #
1003
+ def display_update_detail(update_data)
1004
+ id = update_data[:id]
1005
+ sched_id = update_data[:schedule_id]
1006
+ version = update_data[:version]
1007
+ percent = update_data[:update_percentage]
1008
+
1009
+ say "Update Task ID : #{id}"
1010
+ say "Schedule ID : #{sched_id}"
1011
+ say "Firmware Version : #{version}"
1012
+ say "Percent of devices to update : #{percent}"
1013
+ end
664
1014
  end
665
1015
  end
666
1016
  end
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright 2018 Xaptum,Inc
2
+ # Copyright 2018-2020 Xaptum,Inc
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.
@@ -14,5 +14,5 @@
14
14
  # limitations under the License.
15
15
  #
16
16
  module EnfCli
17
- VERSION = "4.1.0-beta"
17
+ VERSION = "4.2.1-alpha"
18
18
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: enfcli
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.0.pre.beta
4
+ version: 4.2.1.pre.alpha
5
5
  platform: ruby
6
6
  authors:
7
7
  - Venkatakumar Srinivasan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-24 00:00:00.000000000 Z
11
+ date: 2020-04-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor