toys-core 0.11.5 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +62 -0
- data/LICENSE.md +1 -1
- data/README.md +5 -2
- data/docs/guide.md +1 -1
- data/lib/toys/acceptor.rb +13 -4
- data/lib/toys/arg_parser.rb +7 -7
- data/lib/toys/cli.rb +170 -120
- data/lib/toys/compat.rb +71 -23
- data/lib/toys/completion.rb +18 -6
- data/lib/toys/context.rb +24 -15
- data/lib/toys/core.rb +6 -2
- data/lib/toys/dsl/base.rb +87 -0
- data/lib/toys/dsl/flag.rb +26 -20
- data/lib/toys/dsl/flag_group.rb +18 -14
- data/lib/toys/dsl/internal.rb +206 -0
- data/lib/toys/dsl/positional_arg.rb +26 -16
- data/lib/toys/dsl/tool.rb +180 -218
- data/lib/toys/errors.rb +64 -8
- data/lib/toys/flag.rb +662 -656
- data/lib/toys/flag_group.rb +24 -10
- data/lib/toys/input_file.rb +13 -7
- data/lib/toys/loader.rb +293 -140
- data/lib/toys/middleware.rb +46 -22
- data/lib/toys/mixin.rb +10 -8
- data/lib/toys/positional_arg.rb +21 -20
- data/lib/toys/settings.rb +914 -0
- data/lib/toys/source_info.rb +147 -35
- data/lib/toys/standard_middleware/add_verbosity_flags.rb +2 -0
- data/lib/toys/standard_middleware/apply_config.rb +6 -4
- data/lib/toys/standard_middleware/handle_usage_errors.rb +1 -0
- data/lib/toys/standard_middleware/set_default_descriptions.rb +19 -18
- data/lib/toys/standard_middleware/show_help.rb +19 -5
- data/lib/toys/standard_middleware/show_root_version.rb +2 -0
- data/lib/toys/standard_mixins/bundler.rb +24 -15
- data/lib/toys/standard_mixins/exec.rb +43 -34
- data/lib/toys/standard_mixins/fileutils.rb +3 -1
- data/lib/toys/standard_mixins/gems.rb +21 -17
- data/lib/toys/standard_mixins/git_cache.rb +46 -0
- data/lib/toys/standard_mixins/highline.rb +8 -8
- data/lib/toys/standard_mixins/terminal.rb +5 -5
- data/lib/toys/standard_mixins/xdg.rb +56 -0
- data/lib/toys/template.rb +11 -9
- data/lib/toys/{tool.rb → tool_definition.rb} +292 -226
- data/lib/toys/utils/completion_engine.rb +7 -2
- data/lib/toys/utils/exec.rb +162 -132
- data/lib/toys/utils/gems.rb +85 -60
- data/lib/toys/utils/git_cache.rb +813 -0
- data/lib/toys/utils/help_text.rb +117 -37
- data/lib/toys/utils/terminal.rb +11 -3
- data/lib/toys/utils/xdg.rb +293 -0
- data/lib/toys/wrappable_string.rb +9 -2
- data/lib/toys-core.rb +18 -6
- metadata +14 -7
@@ -54,6 +54,7 @@ module Toys
|
|
54
54
|
|
55
55
|
##
|
56
56
|
# Internal completion method designed for testing.
|
57
|
+
#
|
57
58
|
# @private
|
58
59
|
#
|
59
60
|
def run_internal(line)
|
@@ -78,7 +79,9 @@ module Toys
|
|
78
79
|
end
|
79
80
|
|
80
81
|
class << self
|
81
|
-
##
|
82
|
+
##
|
83
|
+
# @private
|
84
|
+
#
|
82
85
|
def split(line)
|
83
86
|
words = []
|
84
87
|
field = ::String.new
|
@@ -97,7 +100,9 @@ module Toys
|
|
97
100
|
words
|
98
101
|
end
|
99
102
|
|
100
|
-
##
|
103
|
+
##
|
104
|
+
# @private
|
105
|
+
#
|
101
106
|
def format_candidate(candidate, quote_type)
|
102
107
|
str = candidate.to_s
|
103
108
|
partial = candidate.is_a?(Completion::Candidate) ? candidate.partial? : false
|
data/lib/toys/utils/exec.rb
CHANGED
@@ -16,8 +16,6 @@ module Toys
|
|
16
16
|
# This class is not loaded by default. Before using it directly, you should
|
17
17
|
# `require "toys/utils/exec"`
|
18
18
|
#
|
19
|
-
# ## Features
|
20
|
-
#
|
21
19
|
# ### Controlling processes
|
22
20
|
#
|
23
21
|
# A process can be started in the *foreground* or the *background*. If you
|
@@ -138,7 +136,7 @@ module Toys
|
|
138
136
|
# end
|
139
137
|
# exec_service.exec(["git", "init"], result_callback: my_callback)
|
140
138
|
#
|
141
|
-
#
|
139
|
+
# ### Configuration options
|
142
140
|
#
|
143
141
|
# A variety of options can be used to control subprocesses. These can be
|
144
142
|
# provided to any method that starts a subprocess. Youc an also set
|
@@ -282,7 +280,7 @@ module Toys
|
|
282
280
|
#
|
283
281
|
def exec_ruby(args, **opts, &block)
|
284
282
|
cmd = args.is_a?(::Array) ? [::RbConfig.ruby] + args : "#{::RbConfig.ruby} #{args}"
|
285
|
-
log_cmd =
|
283
|
+
log_cmd = "exec ruby: #{args.inspect}"
|
286
284
|
opts = {argv0: "ruby", log_cmd: log_cmd}.merge(opts)
|
287
285
|
exec(cmd, **opts, &block)
|
288
286
|
end
|
@@ -398,92 +396,6 @@ module Toys
|
|
398
396
|
exec(cmd, **opts, &block).exit_code
|
399
397
|
end
|
400
398
|
|
401
|
-
##
|
402
|
-
# An internal helper class storing the configuration of a subprocess invocation
|
403
|
-
# @private
|
404
|
-
#
|
405
|
-
class Opts
|
406
|
-
##
|
407
|
-
# Option keys that belong to exec configuration
|
408
|
-
# @private
|
409
|
-
#
|
410
|
-
CONFIG_KEYS = [
|
411
|
-
:argv0,
|
412
|
-
:background,
|
413
|
-
:cli,
|
414
|
-
:env,
|
415
|
-
:err,
|
416
|
-
:in,
|
417
|
-
:logger,
|
418
|
-
:log_cmd,
|
419
|
-
:log_level,
|
420
|
-
:name,
|
421
|
-
:out,
|
422
|
-
:result_callback,
|
423
|
-
].freeze
|
424
|
-
|
425
|
-
##
|
426
|
-
# Option keys that belong to spawn configuration
|
427
|
-
# @private
|
428
|
-
#
|
429
|
-
SPAWN_KEYS = [
|
430
|
-
:chdir,
|
431
|
-
:close_others,
|
432
|
-
:new_pgroup,
|
433
|
-
:pgroup,
|
434
|
-
:umask,
|
435
|
-
:unsetenv_others,
|
436
|
-
].freeze
|
437
|
-
|
438
|
-
## @private
|
439
|
-
def initialize(parent = nil)
|
440
|
-
if parent
|
441
|
-
@config_opts = ::Hash.new { |_h, k| parent.config_opts[k] }
|
442
|
-
@spawn_opts = ::Hash.new { |_h, k| parent.spawn_opts[k] }
|
443
|
-
elsif block_given?
|
444
|
-
@config_opts = ::Hash.new { |_h, k| yield k }
|
445
|
-
@spawn_opts = ::Hash.new { |_h, k| yield k }
|
446
|
-
else
|
447
|
-
@config_opts = {}
|
448
|
-
@spawn_opts = {}
|
449
|
-
end
|
450
|
-
end
|
451
|
-
|
452
|
-
## @private
|
453
|
-
def add(config)
|
454
|
-
config.each do |k, v|
|
455
|
-
if CONFIG_KEYS.include?(k)
|
456
|
-
@config_opts[k] = v
|
457
|
-
elsif SPAWN_KEYS.include?(k) || k.to_s.start_with?("rlimit_")
|
458
|
-
@spawn_opts[k] = v
|
459
|
-
else
|
460
|
-
raise ::ArgumentError, "Unknown key: #{k.inspect}"
|
461
|
-
end
|
462
|
-
end
|
463
|
-
self
|
464
|
-
end
|
465
|
-
|
466
|
-
## @private
|
467
|
-
def delete(*keys)
|
468
|
-
keys.each do |k|
|
469
|
-
if CONFIG_KEYS.include?(k)
|
470
|
-
@config_opts.delete(k)
|
471
|
-
elsif SPAWN_KEYS.include?(k) || k.to_s.start_with?("rlimit_")
|
472
|
-
@spawn_opts.delete(k)
|
473
|
-
else
|
474
|
-
raise ::ArgumentError, "Unknown key: #{k.inspect}"
|
475
|
-
end
|
476
|
-
end
|
477
|
-
self
|
478
|
-
end
|
479
|
-
|
480
|
-
## @private
|
481
|
-
attr_reader :config_opts
|
482
|
-
|
483
|
-
## @private
|
484
|
-
attr_reader :spawn_opts
|
485
|
-
end
|
486
|
-
|
487
399
|
##
|
488
400
|
# An object that controls a subprocess. This object is returned from an
|
489
401
|
# execution running in the background, or is yielded to a control block
|
@@ -492,28 +404,6 @@ module Toys
|
|
492
404
|
# send signals to the process, and get its result.
|
493
405
|
#
|
494
406
|
class Controller
|
495
|
-
## @private
|
496
|
-
def initialize(name, controller_streams, captures, pid, join_threads,
|
497
|
-
result_callback, mutex)
|
498
|
-
@name = name
|
499
|
-
@in = controller_streams[:in]
|
500
|
-
@out = controller_streams[:out]
|
501
|
-
@err = controller_streams[:err]
|
502
|
-
@captures = captures
|
503
|
-
@pid = @exception = @wait_thread = nil
|
504
|
-
case pid
|
505
|
-
when ::Integer
|
506
|
-
@pid = pid
|
507
|
-
@wait_thread = ::Process.detach(pid)
|
508
|
-
when ::Exception
|
509
|
-
@exception = pid
|
510
|
-
end
|
511
|
-
@join_threads = join_threads
|
512
|
-
@result_callback = result_callback
|
513
|
-
@mutex = mutex
|
514
|
-
@result = nil
|
515
|
-
end
|
516
|
-
|
517
407
|
##
|
518
408
|
# The subcommand's name.
|
519
409
|
# @return [Object]
|
@@ -748,8 +638,33 @@ module Toys
|
|
748
638
|
end
|
749
639
|
end
|
750
640
|
|
641
|
+
##
|
642
|
+
# @private
|
643
|
+
#
|
644
|
+
def initialize(name, controller_streams, captures, pid, join_threads,
|
645
|
+
result_callback, mutex)
|
646
|
+
@name = name
|
647
|
+
@in = controller_streams[:in]
|
648
|
+
@out = controller_streams[:out]
|
649
|
+
@err = controller_streams[:err]
|
650
|
+
@captures = captures
|
651
|
+
@pid = @exception = @wait_thread = nil
|
652
|
+
case pid
|
653
|
+
when ::Integer
|
654
|
+
@pid = pid
|
655
|
+
@wait_thread = ::Process.detach(pid)
|
656
|
+
when ::Exception
|
657
|
+
@exception = pid
|
658
|
+
end
|
659
|
+
@join_threads = join_threads
|
660
|
+
@result_callback = result_callback
|
661
|
+
@mutex = mutex
|
662
|
+
@result = nil
|
663
|
+
end
|
664
|
+
|
751
665
|
##
|
752
666
|
# Close the controller's streams.
|
667
|
+
#
|
753
668
|
# @private
|
754
669
|
#
|
755
670
|
def close_streams(which)
|
@@ -801,15 +716,6 @@ module Toys
|
|
801
716
|
# return the numeric signal code.
|
802
717
|
#
|
803
718
|
class Result
|
804
|
-
## @private
|
805
|
-
def initialize(name, out, err, status, exception)
|
806
|
-
@name = name
|
807
|
-
@captured_out = out
|
808
|
-
@captured_err = err
|
809
|
-
@status = status
|
810
|
-
@exception = exception
|
811
|
-
end
|
812
|
-
|
813
719
|
##
|
814
720
|
# The subcommand's name.
|
815
721
|
#
|
@@ -930,13 +836,129 @@ module Toys
|
|
930
836
|
code = exit_code
|
931
837
|
!code.nil? && !code.zero?
|
932
838
|
end
|
839
|
+
|
840
|
+
##
|
841
|
+
# @private
|
842
|
+
#
|
843
|
+
def initialize(name, out, err, status, exception)
|
844
|
+
@name = name
|
845
|
+
@captured_out = out
|
846
|
+
@captured_err = err
|
847
|
+
@status = status
|
848
|
+
@exception = exception
|
849
|
+
end
|
850
|
+
end
|
851
|
+
|
852
|
+
private
|
853
|
+
|
854
|
+
##
|
855
|
+
# An internal helper class storing the configuration of a subprocess invocation
|
856
|
+
#
|
857
|
+
# @private
|
858
|
+
#
|
859
|
+
class Opts
|
860
|
+
##
|
861
|
+
# Option keys that belong to exec configuration
|
862
|
+
#
|
863
|
+
# @private
|
864
|
+
#
|
865
|
+
CONFIG_KEYS = [
|
866
|
+
:argv0,
|
867
|
+
:background,
|
868
|
+
:cli,
|
869
|
+
:env,
|
870
|
+
:err,
|
871
|
+
:in,
|
872
|
+
:logger,
|
873
|
+
:log_cmd,
|
874
|
+
:log_level,
|
875
|
+
:name,
|
876
|
+
:out,
|
877
|
+
:result_callback,
|
878
|
+
].freeze
|
879
|
+
|
880
|
+
##
|
881
|
+
# Option keys that belong to spawn configuration
|
882
|
+
#
|
883
|
+
# @private
|
884
|
+
#
|
885
|
+
SPAWN_KEYS = [
|
886
|
+
:chdir,
|
887
|
+
:close_others,
|
888
|
+
:new_pgroup,
|
889
|
+
:pgroup,
|
890
|
+
:umask,
|
891
|
+
:unsetenv_others,
|
892
|
+
].freeze
|
893
|
+
|
894
|
+
##
|
895
|
+
# @private
|
896
|
+
#
|
897
|
+
def initialize(parent = nil)
|
898
|
+
if parent
|
899
|
+
@config_opts = ::Hash.new { |_h, k| parent.config_opts[k] }
|
900
|
+
@spawn_opts = ::Hash.new { |_h, k| parent.spawn_opts[k] }
|
901
|
+
elsif block_given?
|
902
|
+
@config_opts = ::Hash.new { |_h, k| yield k }
|
903
|
+
@spawn_opts = ::Hash.new { |_h, k| yield k }
|
904
|
+
else
|
905
|
+
@config_opts = {}
|
906
|
+
@spawn_opts = {}
|
907
|
+
end
|
908
|
+
end
|
909
|
+
|
910
|
+
##
|
911
|
+
# @private
|
912
|
+
#
|
913
|
+
def add(config)
|
914
|
+
config.each do |k, v|
|
915
|
+
if CONFIG_KEYS.include?(k)
|
916
|
+
@config_opts[k] = v
|
917
|
+
elsif SPAWN_KEYS.include?(k) || k.to_s.start_with?("rlimit_")
|
918
|
+
@spawn_opts[k] = v
|
919
|
+
else
|
920
|
+
raise ::ArgumentError, "Unknown key: #{k.inspect}"
|
921
|
+
end
|
922
|
+
end
|
923
|
+
self
|
924
|
+
end
|
925
|
+
|
926
|
+
##
|
927
|
+
# @private
|
928
|
+
#
|
929
|
+
def delete(*keys)
|
930
|
+
keys.each do |k|
|
931
|
+
if CONFIG_KEYS.include?(k)
|
932
|
+
@config_opts.delete(k)
|
933
|
+
elsif SPAWN_KEYS.include?(k) || k.to_s.start_with?("rlimit_")
|
934
|
+
@spawn_opts.delete(k)
|
935
|
+
else
|
936
|
+
raise ::ArgumentError, "Unknown key: #{k.inspect}"
|
937
|
+
end
|
938
|
+
end
|
939
|
+
self
|
940
|
+
end
|
941
|
+
|
942
|
+
##
|
943
|
+
# @private
|
944
|
+
#
|
945
|
+
attr_reader :config_opts
|
946
|
+
|
947
|
+
##
|
948
|
+
# @private
|
949
|
+
#
|
950
|
+
attr_reader :spawn_opts
|
933
951
|
end
|
934
952
|
|
935
953
|
##
|
936
954
|
# An object that manages the execution of a subcommand
|
955
|
+
#
|
937
956
|
# @private
|
938
957
|
#
|
939
958
|
class Executor
|
959
|
+
##
|
960
|
+
# @private
|
961
|
+
#
|
940
962
|
def initialize(exec_opts, spawn_cmd, block)
|
941
963
|
@fork_func = spawn_cmd.respond_to?(:call) ? spawn_cmd : nil
|
942
964
|
@spawn_cmd = spawn_cmd.respond_to?(:call) ? nil : spawn_cmd
|
@@ -952,6 +974,9 @@ module Toys
|
|
952
974
|
@mutex = ::Mutex.new
|
953
975
|
end
|
954
976
|
|
977
|
+
##
|
978
|
+
# @private
|
979
|
+
#
|
955
980
|
def execute
|
956
981
|
setup_in_stream
|
957
982
|
setup_out_stream(:out)
|
@@ -972,17 +997,23 @@ module Toys
|
|
972
997
|
def log_command
|
973
998
|
logger = @config_opts[:logger]
|
974
999
|
if logger && @config_opts[:log_level] != false
|
975
|
-
cmd_str = @config_opts[:log_cmd] || default_log_str
|
1000
|
+
cmd_str = @config_opts[:log_cmd] || default_log_str
|
976
1001
|
logger.add(@config_opts[:log_level] || ::Logger::INFO, cmd_str) if cmd_str
|
977
1002
|
end
|
978
1003
|
end
|
979
1004
|
|
980
|
-
def default_log_str
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
985
|
-
|
1005
|
+
def default_log_str
|
1006
|
+
if @fork_func
|
1007
|
+
"exec proc: #{@fork_func.inspect}"
|
1008
|
+
elsif @spawn_cmd
|
1009
|
+
if @spawn_cmd.size == 1 && @spawn_cmd.first.is_a?(::String)
|
1010
|
+
"exec sh: #{@spawn_cmd.first.inspect}"
|
1011
|
+
else
|
1012
|
+
cmd_binary = @spawn_cmd.first
|
1013
|
+
cmd_binary = cmd_binary.first if cmd_binary.is_a?(::Array)
|
1014
|
+
"exec: #{([cmd_binary] + @spawn_cmd[1..-1]).inspect}"
|
1015
|
+
end
|
1016
|
+
end
|
986
1017
|
end
|
987
1018
|
|
988
1019
|
def start_with_controller
|
@@ -1093,9 +1124,10 @@ module Toys
|
|
1093
1124
|
|
1094
1125
|
def interpret_out_array_within_fork(stream)
|
1095
1126
|
if stream.first == :child
|
1096
|
-
|
1127
|
+
case stream[1]
|
1128
|
+
when :err
|
1097
1129
|
$stderr
|
1098
|
-
|
1130
|
+
when :out
|
1099
1131
|
$stdout
|
1100
1132
|
end
|
1101
1133
|
else
|
@@ -1312,8 +1344,6 @@ module Toys
|
|
1312
1344
|
end
|
1313
1345
|
end
|
1314
1346
|
|
1315
|
-
private
|
1316
|
-
|
1317
1347
|
def canonical_binary_spec(cmd, exec_opts)
|
1318
1348
|
config_argv0 = exec_opts.config_opts[:argv0]
|
1319
1349
|
return cmd.to_s if !config_argv0 && !cmd.is_a?(::Array)
|
data/lib/toys/utils/gems.rb
CHANGED
@@ -132,8 +132,8 @@ module Toys
|
|
132
132
|
end
|
133
133
|
@on_conflict = on_conflict || :error
|
134
134
|
@terminal = terminal
|
135
|
-
@input = input ||
|
136
|
-
@output = output ||
|
135
|
+
@input = input || $stdin
|
136
|
+
@output = output || $stdout
|
137
137
|
end
|
138
138
|
|
139
139
|
##
|
@@ -185,17 +185,19 @@ module Toys
|
|
185
185
|
gemfile_path = Gems.find_gemfile(dir, gemfile_names: gemfile_names)
|
186
186
|
end
|
187
187
|
raise GemfileNotFoundError, "Gemfile not found" unless gemfile_path
|
188
|
+
gemfile_path = ::File.absolute_path(gemfile_path)
|
188
189
|
Gems.synchronize do
|
189
190
|
if configure_gemfile(gemfile_path)
|
190
|
-
activate("bundler", "~> 2.
|
191
|
+
activate("bundler", "~> 2.2")
|
191
192
|
require "bundler"
|
192
|
-
|
193
|
-
setup_bundle(gemfile_path, lockfile_path, groups: groups, retries: retries)
|
193
|
+
setup_bundle(gemfile_path, groups: groups, retries: retries)
|
194
194
|
end
|
195
195
|
end
|
196
196
|
end
|
197
197
|
|
198
|
+
##
|
198
199
|
# @private
|
200
|
+
#
|
199
201
|
def self.find_gemfile(search_dir, gemfile_names: nil)
|
200
202
|
gemfile_names ||= DEFAULT_GEMFILE_NAMES
|
201
203
|
Array(gemfile_names).each do |file|
|
@@ -207,7 +209,9 @@ module Toys
|
|
207
209
|
|
208
210
|
@global_mutex = ::Monitor.new
|
209
211
|
|
212
|
+
##
|
210
213
|
# @private
|
214
|
+
#
|
211
215
|
def self.synchronize(&block)
|
212
216
|
@global_mutex.synchronize(&block)
|
213
217
|
end
|
@@ -254,8 +258,7 @@ module Toys
|
|
254
258
|
def confirm_and_install_gem(name, requirements)
|
255
259
|
if @on_missing == :confirm
|
256
260
|
requirements_text = gem_requirements_text(name, requirements)
|
257
|
-
response = terminal.confirm("Gem needed: #{requirements_text}. Install? ",
|
258
|
-
default: @default_confirm)
|
261
|
+
response = terminal.confirm("Gem needed: #{requirements_text}. Install? ", default: @default_confirm)
|
259
262
|
unless response
|
260
263
|
raise InstallFailedError, "Canceled installation of needed gem: #{requirements_text}"
|
261
264
|
end
|
@@ -296,75 +299,96 @@ module Toys
|
|
296
299
|
if ::File.basename(gemfile_path) == "gems.rb"
|
297
300
|
::File.join(::File.dirname(gemfile_path), "gems.locked")
|
298
301
|
else
|
299
|
-
gemfile_path
|
302
|
+
"#{gemfile_path}.lock"
|
300
303
|
end
|
301
304
|
end
|
302
305
|
|
303
|
-
def setup_bundle(gemfile_path,
|
306
|
+
def setup_bundle(gemfile_path, groups: nil, retries: nil)
|
307
|
+
check_gemfile_compatibility(gemfile_path)
|
304
308
|
groups = Array(groups)
|
305
|
-
|
309
|
+
modified_gemfile_path = create_modified_gemfile(gemfile_path)
|
306
310
|
begin
|
307
|
-
|
308
|
-
::Bundler.ui.silence { ::Bundler.setup(*groups) }
|
311
|
+
attempt_setup_bundle(modified_gemfile_path, groups)
|
309
312
|
rescue ::Bundler::GemNotFound, ::Bundler::VersionConflict
|
310
|
-
restore_toys_libs
|
311
|
-
install_bundle(gemfile_path, retries: retries)
|
312
|
-
old_lockfile_contents = save_old_lockfile(lockfile_path)
|
313
313
|
::Bundler.reset!
|
314
|
-
|
315
|
-
|
314
|
+
restore_toys_libs
|
315
|
+
install_bundle(modified_gemfile_path, retries: retries)
|
316
|
+
attempt_setup_bundle(modified_gemfile_path, groups)
|
317
|
+
ensure
|
318
|
+
delete_modified_gemfile(modified_gemfile_path)
|
319
|
+
::ENV["BUNDLE_GEMFILE"] = gemfile_path
|
316
320
|
end
|
317
321
|
restore_toys_libs
|
318
|
-
ensure
|
319
|
-
restore_old_lockfile(lockfile_path, old_lockfile_contents)
|
320
|
-
end
|
321
|
-
|
322
|
-
def save_old_lockfile(lockfile_path)
|
323
|
-
return nil unless ::File.readable?(lockfile_path) && ::File.writable?(lockfile_path)
|
324
|
-
::File.read(lockfile_path)
|
325
322
|
end
|
326
323
|
|
327
|
-
def
|
328
|
-
|
329
|
-
|
330
|
-
|
324
|
+
def attempt_setup_bundle(modified_gemfile_path, groups)
|
325
|
+
::ENV["BUNDLE_GEMFILE"] = modified_gemfile_path
|
326
|
+
::Bundler.configure
|
327
|
+
::Bundler.settings.temporary({gemfile: modified_gemfile_path}) do
|
328
|
+
::Bundler.ui.silence do
|
329
|
+
::Bundler.setup(*groups)
|
331
330
|
end
|
332
331
|
end
|
333
332
|
end
|
334
333
|
|
335
|
-
def
|
334
|
+
def check_gemfile_compatibility(gemfile_path)
|
335
|
+
::Bundler.configure
|
336
336
|
builder = ::Bundler::Dsl.new
|
337
337
|
builder.eval_gemfile(gemfile_path)
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
add_gem_to_definition(builder, "toys-core")
|
342
|
-
if removed_toys || ::Toys.const_defined?(:VERSION)
|
343
|
-
add_gem_to_definition(builder, "toys")
|
344
|
-
toys_gems << "toys"
|
345
|
-
end
|
346
|
-
definition = builder.to_definition(lockfile_path, { gems: toys_gems })
|
347
|
-
::Bundler.instance_variable_set(:@definition, definition)
|
338
|
+
check_gemfile_gem_compatibility(builder, "toys-core")
|
339
|
+
check_gemfile_gem_compatibility(builder, "toys")
|
340
|
+
::Bundler.reset!
|
348
341
|
end
|
349
342
|
|
350
|
-
def
|
343
|
+
def check_gemfile_gem_compatibility(builder, name)
|
351
344
|
existing_dep = builder.dependencies.find { |dep| dep.name == name }
|
352
|
-
|
353
|
-
unless existing_dep.requirement.satisfied_by?(::Gem::Version.new(::Toys::Core::VERSION))
|
345
|
+
if existing_dep && !existing_dep.requirement.satisfied_by?(::Gem::Version.new(::Toys::Core::VERSION))
|
354
346
|
raise IncompatibleToysError,
|
355
347
|
"The bundle lists #{name} #{existing_dep.requirement} as a dependency, which is" \
|
356
|
-
" incompatible with the current version #{::Toys::Core::VERSION}."
|
348
|
+
" incompatible with the current toys version #{::Toys::Core::VERSION}."
|
357
349
|
end
|
358
|
-
builder.dependencies.delete(existing_dep)
|
359
|
-
true
|
360
350
|
end
|
361
351
|
|
362
|
-
def
|
363
|
-
|
364
|
-
|
352
|
+
def create_modified_gemfile(gemfile_path)
|
353
|
+
dir = ::File.dirname(gemfile_path)
|
354
|
+
modified_gemfile_path = loop do
|
355
|
+
timestamp = ::Time.now.strftime("%Y%m%d%H%M%S")
|
356
|
+
uniquifier = rand(3_656_158_440_062_976).to_s(36) # 10 digits in base 36
|
357
|
+
path = ::File.join(dir, ".toys-tmp-gemfile-#{timestamp}-#{uniquifier}")
|
358
|
+
break path unless ::File.exist?(path)
|
365
359
|
end
|
366
|
-
|
367
|
-
|
360
|
+
::File.open(modified_gemfile_path, "w") do |file|
|
361
|
+
modified_gemfile_content(gemfile_path).each do |line|
|
362
|
+
file.puts(line)
|
363
|
+
end
|
364
|
+
end
|
365
|
+
lockfile_path = find_lockfile_path(gemfile_path)
|
366
|
+
modified_lockfile_path = find_lockfile_path(modified_gemfile_path)
|
367
|
+
if ::File.readable?(lockfile_path)
|
368
|
+
lockfile_content = ::File.read(lockfile_path)
|
369
|
+
::File.open(modified_lockfile_path, "w") { |file| file.write(lockfile_content) }
|
370
|
+
end
|
371
|
+
modified_gemfile_path
|
372
|
+
end
|
373
|
+
|
374
|
+
def modified_gemfile_content(gemfile_path)
|
375
|
+
is_running_toys = ::Toys.const_defined?(:VERSION)
|
376
|
+
content = [::File.read(gemfile_path)]
|
377
|
+
content << "has_toys_dep = dependencies.any? { |dep| dep.name == 'toys' }" unless is_running_toys
|
378
|
+
content << "dependencies.delete_if { |dep| dep.name == 'toys-core' || dep.name == 'toys' }"
|
379
|
+
repo_root = ::File.dirname(::File.dirname(::Toys::CORE_LIB_PATH)) if ::ENV["TOYS_DEV"]
|
380
|
+
path = repo_root ? ::File.join(repo_root, "toys-core") : nil
|
381
|
+
content << "gem 'toys-core', #{::Toys::Core::VERSION.inspect}, path: #{path.inspect}"
|
382
|
+
path = repo_root ? ::File.join(repo_root, "toys") : nil
|
383
|
+
guard = is_running_toys ? "" : " if has_toys_dep"
|
384
|
+
content << "gem 'toys', #{::Toys::Core::VERSION.inspect}, path: #{path.inspect}#{guard}"
|
385
|
+
content
|
386
|
+
end
|
387
|
+
|
388
|
+
def delete_modified_gemfile(modified_gemfile_path)
|
389
|
+
::File.delete(modified_gemfile_path) if ::File.exist?(modified_gemfile_path)
|
390
|
+
modified_lockfile_path = find_lockfile_path(modified_gemfile_path)
|
391
|
+
::File.delete(modified_lockfile_path) if ::File.exist?(modified_lockfile_path)
|
368
392
|
end
|
369
393
|
|
370
394
|
def restore_toys_libs
|
@@ -383,8 +407,7 @@ module Toys
|
|
383
407
|
when :error
|
384
408
|
false
|
385
409
|
else
|
386
|
-
terminal.confirm("Your bundle requires additional gems. Install? ",
|
387
|
-
default: @default_confirm)
|
410
|
+
terminal.confirm("Your bundle requires additional gems. Install? ", default: @default_confirm)
|
388
411
|
end
|
389
412
|
end
|
390
413
|
|
@@ -392,17 +415,19 @@ module Toys
|
|
392
415
|
gemfile_dir = ::File.dirname(gemfile_path)
|
393
416
|
unless permission_to_bundle?
|
394
417
|
raise BundleNotInstalledError,
|
395
|
-
"Your bundle is not installed. Consider running"
|
396
|
-
" `cd #{gemfile_dir} && bundle install`"
|
418
|
+
"Your bundle is not installed. Consider running `cd #{gemfile_dir} && bundle install`"
|
397
419
|
end
|
398
420
|
retries = retries.to_i
|
399
|
-
args =
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
421
|
+
args = ["--gemfile=#{gemfile_path}"]
|
422
|
+
args << "--retry=#{retries}" if retries.positive?
|
423
|
+
bundler_bin = ::Gem.bin_path("bundler", "bundle", ::Bundler::VERSION)
|
424
|
+
result = exec_util.exec_ruby([bundler_bin, "install"] + args)
|
425
|
+
if result.error?
|
404
426
|
terminal.puts("Failed to install. Trying update...")
|
405
|
-
|
427
|
+
result = exec_util.exec_ruby([bundler_bin, "update"] + args)
|
428
|
+
unless result.success?
|
429
|
+
raise ::Bundler::InstallError, "Failed to install or update bundle: #{gemfile_path}"
|
430
|
+
end
|
406
431
|
end
|
407
432
|
end
|
408
433
|
end
|