spud 0.2.0 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/lib/spud.rb +3 -2
  3. data/lib/spud/block_param_info.rb +80 -0
  4. data/lib/spud/cli/options.rb +35 -0
  5. data/lib/spud/cli/parser.rb +23 -19
  6. data/lib/spud/cli/results.rb +18 -11
  7. data/lib/spud/driver.rb +135 -0
  8. data/lib/spud/error.rb +1 -0
  9. data/lib/spud/help.rb +13 -13
  10. data/lib/spud/lister.rb +27 -22
  11. data/lib/spud/shell/command.rb +75 -0
  12. data/lib/spud/shell/result.rb +33 -0
  13. data/lib/spud/task_arg.rb +24 -12
  14. data/lib/spud/task_args.rb +20 -18
  15. data/lib/spud/task_runners/make/task.rb +67 -0
  16. data/lib/spud/task_runners/package.json/task.rb +71 -0
  17. data/lib/spud/task_runners/spud_task_runner/dependency.rb +49 -0
  18. data/lib/spud/task_runners/spud_task_runner/file_dsl.rb +57 -0
  19. data/lib/spud/task_runners/spud_task_runner/task.rb +148 -0
  20. data/lib/spud/task_runners/spud_task_runner/task_dsl.rb +90 -0
  21. data/lib/spud/task_runners/task.rb +40 -0
  22. data/lib/spud/task_runners/task_runners.rb +25 -0
  23. data/lib/spud/version.rb +2 -1
  24. data/lib/spud/watch.rb +73 -0
  25. metadata +46 -16
  26. data/lib/spud/build_tools/build_tools.rb +0 -13
  27. data/lib/spud/build_tools/make/task.rb +0 -27
  28. data/lib/spud/build_tools/package.json/task.rb +0 -47
  29. data/lib/spud/build_tools/spud/block_param_info.rb +0 -77
  30. data/lib/spud/build_tools/spud/dsl/file.rb +0 -23
  31. data/lib/spud/build_tools/spud/dsl/task.rb +0 -63
  32. data/lib/spud/build_tools/spud/shell/command.rb +0 -52
  33. data/lib/spud/build_tools/spud/shell/result.rb +0 -26
  34. data/lib/spud/build_tools/spud/task.rb +0 -125
  35. data/lib/spud/build_tools/task.rb +0 -33
  36. data/lib/spud/options.rb +0 -14
  37. data/lib/spud/runtime.rb +0 -104
@@ -1,3 +1,4 @@
1
+ # typed: true
1
2
  require 'stringio'
2
3
  require 'spud/version'
3
4
 
@@ -5,19 +6,18 @@ module Spud
5
6
  module Help
6
7
  # @return [void]
7
8
  def self.print!
8
- help = StringIO.new
9
-
10
- help.puts "spud #{VERSION}"
11
- help.puts
12
- help.puts 'usage:'
13
- help.puts ' spud [options] <task> [args]'
14
- help.puts
15
- help.puts 'options:'
16
- help.puts ' -h, --help show this help dialog'
17
- help.puts ' -f, --files list parsed files'
18
- help.puts ' --debug run in debug mode'
19
-
20
- puts help.string
9
+ puts <<~HELP
10
+ spud #{VERSION}
11
+
12
+ usage:
13
+ spud [options] <task> [args]
14
+
15
+ options:
16
+ -h, --help show this help dialog
17
+ -w, --watch <glob> re-run task any time glob changes
18
+ -f, --files list parsed files
19
+ -i, --inspect show details about a task
20
+ HELP
21
21
  end
22
22
  end
23
23
  end
@@ -1,22 +1,27 @@
1
+ # typed: true
2
+ require 'sorbet-runtime'
1
3
  require 'stringio'
4
+ require 'spud/task_runners/task'
2
5
 
3
6
  module Spud
4
7
  class Lister
5
- # @param tasks [Array<BuildTools::Task>]
8
+ extend T::Sig
9
+
10
+ sig {params(tasks: T::Array[TaskRunners::Task]).void}
6
11
  def initialize(tasks)
7
12
  @tasks = tasks
8
13
  end
9
14
 
10
- # @return [void]
11
- def list!
15
+ sig {void}
16
+ def list_tasks!
12
17
  builder = StringIO.new
13
18
 
14
19
  @tasks.each do |task|
15
20
  builder.write task.name.ljust(max_task_length)
16
21
 
17
- if show_positional_args?
22
+ if show_ordered_args?
18
23
  builder.write ' '
19
- builder.write task.args.positional.join(' ').ljust(max_positional_string_length)
24
+ builder.write task.args.ordered.join(' ').ljust(max_ordered_string_length)
20
25
  end
21
26
 
22
27
  if show_named_args?
@@ -35,32 +40,37 @@ module Spud
35
40
  puts builder.string
36
41
  end
37
42
 
38
- # @return [void]
43
+ sig {void}
39
44
  def list_filenames!
40
45
  puts filenames.join("\n")
41
46
  end
42
47
 
48
+ sig {returns(T::Array[String])}
49
+ def filenames
50
+ @filenames ||= @tasks.map(&:filename).uniq
51
+ end
52
+
43
53
  private
44
54
 
45
- # @return [Integer]
55
+ sig {returns(Integer)}
46
56
  def max_task_length
47
57
  @max_task_length ||= @tasks.map { |task| task.name.length }.max
48
58
  end
49
59
 
50
- # @return [Integer]
51
- def max_positional_string_length
52
- @max_positional_string_length ||= @tasks
53
- .map { |task| task.args.positional.join(' ') }
60
+ sig {returns(Integer)}
61
+ def max_ordered_string_length
62
+ @max_ordered_string_length ||= @tasks
63
+ .map { |task| task.args.ordered.join(' ') }
54
64
  .map(&:length)
55
65
  .max
56
66
  end
57
67
 
58
- # @return [Boolean]
59
- def show_positional_args?
60
- @show_positional_args ||= @tasks.any? { |task| task.args.any_positional? }
68
+ sig {returns(T::Boolean)}
69
+ def show_ordered_args?
70
+ @show_ordered_args ||= @tasks.any? { |task| task.args.any_ordered? }
61
71
  end
62
72
 
63
- # @return [Integer]
73
+ sig {returns(Integer)}
64
74
  def max_named_string_length
65
75
  @max_named_string_length ||= @tasks
66
76
  .map { |task| task.args.named.join(' ') }
@@ -68,17 +78,12 @@ module Spud
68
78
  .max
69
79
  end
70
80
 
71
- # @return [Boolean]
81
+ sig {returns(T::Boolean)}
72
82
  def show_named_args?
73
83
  @show_named_args ||= @tasks.any? { |task| task.args.any_named? }
74
84
  end
75
85
 
76
- # @return [Array<String>]
77
- def filenames
78
- @filenames ||= @tasks.map(&:filename).uniq
79
- end
80
-
81
- # @return [Boolean]
86
+ sig {returns(T::Boolean)}
82
87
  def show_filenames?
83
88
  filenames.length > 1
84
89
  end
@@ -0,0 +1,75 @@
1
+ # typed: true
2
+ require 'sorbet-runtime'
3
+ require 'stringio'
4
+ require 'open3'
5
+ require 'spud/driver'
6
+ require 'spud/shell/result'
7
+
8
+ module Spud
9
+ module Shell
10
+ class Command
11
+ extend T::Sig
12
+
13
+ attr_reader :result
14
+
15
+ Handle = T.type_alias {T.any(IO, StringIO)}
16
+
17
+ sig {params(command: String, driver: T.nilable(Driver), silent: T::Boolean, handle: Handle).returns(Result)}
18
+ def self.call(command, driver: nil, silent: false, handle: STDOUT)
19
+ cmd = new(command, driver: driver, silent: silent, handle: handle)
20
+ cmd.issue!
21
+ cmd.result
22
+ end
23
+
24
+ sig {params(driver: Driver).returns(Commander)}
25
+ def self.commander(driver)
26
+ Commander.new(driver)
27
+ end
28
+
29
+ sig {params(command: String, driver: T.nilable(Driver), silent: T::Boolean, handle: Handle).void}
30
+ def initialize(command, driver: nil, silent: false, handle: STDOUT)
31
+ @command = command
32
+ @driver = driver
33
+ @silent = silent
34
+ @handle = handle
35
+
36
+ @result = T.let(nil, T.nilable(Result))
37
+ end
38
+
39
+ sig {void}
40
+ def issue!
41
+ capturer = StringIO.new
42
+
43
+ Open3.popen2e(@command) do |_, out, thread|
44
+ @driver&.register_subprocess(thread.pid)
45
+
46
+ loop do
47
+ line = out.gets
48
+ break unless line
49
+
50
+ @handle.write line unless @silent
51
+ capturer.puts line
52
+ end
53
+
54
+ @result = Result.new(capturer.string, T.cast(thread.value, Process::Status))
55
+ end
56
+
57
+ @driver&.register_subprocess(nil)
58
+ end
59
+
60
+ class Commander
61
+ extend T::Sig
62
+
63
+ sig {params(driver: Driver).void}
64
+ def initialize(driver)
65
+ @driver = driver
66
+ end
67
+
68
+ sig {params(command: String, silent: T::Boolean, handle: Handle).returns(Result)}
69
+ def call(command, silent: false, handle: STDOUT)
70
+ Command.(command, driver: @driver, silent: silent, handle: handle)
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,33 @@
1
+ # typed: strict
2
+ require 'sorbet-runtime'
3
+ require 'forwardable'
4
+
5
+ module Spud
6
+ module Shell
7
+ class Result < String
8
+ extend T::Sig
9
+ extend Forwardable
10
+
11
+ sig {returns(Process::Status)}
12
+ attr_reader :status
13
+
14
+ def_delegators :status,
15
+ :coredump?,
16
+ :exited?,
17
+ :exitstatus,
18
+ :pid,
19
+ :signaled?,
20
+ :stopped?,
21
+ :stopsig,
22
+ :success?,
23
+ :termsig,
24
+ :to_i
25
+
26
+ sig {params(output: String, status: Process::Status).void}
27
+ def initialize(output, status)
28
+ super(output)
29
+ @status = status
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,39 +1,51 @@
1
+ # typed: strict
2
+ require 'sorbet-runtime'
3
+
1
4
  module Spud
2
5
  class TaskArg
3
- # @param name [String]
4
- # @param type [String]
5
- # @param default [String]
6
+ extend T::Sig
7
+
8
+ sig {returns(String)}
9
+ attr_reader :name
10
+
11
+ sig {returns(String)}
12
+ attr_reader :type
13
+
14
+ sig {returns(T.nilable(String))}
15
+ attr_reader :default
16
+
17
+ sig {params(name: String, type: String, default: T.nilable(String)).void}
6
18
  def initialize(name, type, default: nil)
7
- raise 'must be of type "positional" or "named"' unless %w[positional named].include?(type)
19
+ raise 'must be of type "ordered" or "named"' unless %w[ordered named].include?(type)
8
20
 
9
21
  @name = name
10
22
  @type = type
11
23
  @default = default
12
24
  end
13
25
 
14
- # @return [Boolean]
26
+ sig {returns(T::Boolean)}
15
27
  def required?
16
28
  !has_default?
17
29
  end
18
30
 
19
- # @return [Boolean]
31
+ sig {returns(T::Boolean)}
20
32
  def has_default?
21
33
  !!@default
22
34
  end
23
35
 
24
- # @return [Boolean]
25
- def positional?
26
- @type == 'positional'
36
+ sig {returns(T::Boolean)}
37
+ def ordered?
38
+ @type == 'ordered'
27
39
  end
28
40
 
29
- # @return [Boolean]
41
+ sig {returns(T::Boolean)}
30
42
  def named?
31
43
  @type == 'named'
32
44
  end
33
45
 
34
- # @return [String]
46
+ sig {returns(String)}
35
47
  def to_s
36
- if positional?
48
+ if ordered?
37
49
  if has_default?
38
50
  "<#{@name}=#{@default}>"
39
51
  else
@@ -1,47 +1,49 @@
1
+ # typed: true
2
+ require 'sorbet-runtime'
1
3
  require 'spud/task_arg'
2
- require 'spud/build_tools/spud/block_param_info'
4
+ require 'spud/block_param_info'
3
5
 
4
6
  module Spud
5
7
  class TaskArgs < Array
6
- # @param filename [String]
7
- # @param block [Proc]
8
- # @return [Spud::TaskArgs]
8
+ extend T::Sig
9
+
10
+ sig {params(filename: String, block: Proc).returns(T.attached_class)}
9
11
  def self.from_block(filename, &block)
10
- info = BuildTools::Spud::BlockParamInfo.new(filename, &block)
12
+ info = BlockParamInfo.new(filename, &block)
11
13
  new(info.task_args)
12
14
  end
13
15
 
14
- # @param task_args [Array<Spud::TaskArg>]
16
+ sig {params(task_args: T::Array[TaskArg]).void}
15
17
  def initialize(task_args)
16
18
  super(task_args)
17
19
  end
18
20
 
19
- # @return [Array<Spud::TaskArg>]
20
- def positional
21
- @positional ||= select(&:positional?)
21
+ sig {returns(T::Array[TaskArg])}
22
+ def ordered
23
+ @ordered ||= select(&:ordered?)
22
24
  end
23
25
 
24
- # @return [Array<Spud::TaskArg>]
25
- def required_positional
26
- @required_positional ||= positional.select(&:required?)
26
+ sig {returns(T::Array[TaskArg])}
27
+ def required_ordered
28
+ @required_ordered ||= ordered.select(&:required?)
27
29
  end
28
30
 
29
- # @return [Boolean]
30
- def any_positional?
31
- !positional.empty?
31
+ sig {returns(T::Boolean)}
32
+ def any_ordered?
33
+ !ordered.empty?
32
34
  end
33
35
 
34
- # @return [Array<Spud::TaskArg>]
36
+ sig {returns(T::Array[TaskArg])}
35
37
  def named
36
38
  @named ||= select(&:named?)
37
39
  end
38
40
 
39
- # @return [Array<Spud::TaskArg>]
41
+ sig {returns(T::Array[TaskArg])}
40
42
  def required_named
41
43
  @required_named ||= named.select(&:required?)
42
44
  end
43
45
 
44
- # @return [Boolean]
46
+ sig {returns(T::Boolean)}
45
47
  def any_named?
46
48
  !named.empty?
47
49
  end
@@ -0,0 +1,67 @@
1
+ # typed: strict
2
+ require 'sorbet-runtime'
3
+ require 'stringio'
4
+ require 'spud/driver'
5
+ require 'spud/shell/command'
6
+ require 'spud/task_runners/task'
7
+
8
+ module Spud
9
+ module TaskRunners
10
+ module Make
11
+ class Task < TaskRunners::Task
12
+ extend T::Sig
13
+
14
+ sig {override.returns(String)}
15
+ attr_reader :name
16
+
17
+ sig {override.params(driver: Driver).returns(T::Array[TaskRunners::Task])}
18
+ def self.tasks(driver)
19
+ return [] unless File.exist?('Makefile')
20
+
21
+ if `command -v make`.empty?
22
+ puts 'Makefile detected, but no installation of `make` exists. Skipping make...'
23
+ return []
24
+ end
25
+
26
+ source = File.read('Makefile')
27
+ T.unsafe(source.scan(/^(\S+):.*/)).map(&:first).map do |name|
28
+ new(driver, name)
29
+ end
30
+ end
31
+
32
+ sig {params(driver: Driver, name: String).void}
33
+ def initialize(driver, name)
34
+ @driver = driver
35
+ @name = name
36
+ end
37
+
38
+ sig {override.params(ordered: T::Array[String], named: T::Hash[String, String]).returns(T.untyped)}
39
+ def invoke(ordered, named)
40
+ Shell::Command.("make #{name}", driver: @driver)
41
+ end
42
+
43
+ sig {override.returns(String)}
44
+ def filename
45
+ 'Makefile'
46
+ end
47
+
48
+ sig {override.returns(String)}
49
+ def details
50
+ source = File.read('Makefile')
51
+ lines = source.split("\n")
52
+ cursor = 0
53
+
54
+ cursor += 1 until lines[cursor]&.start_with?(name)
55
+
56
+ builder = StringIO.new
57
+ while lines[cursor] && !lines[cursor]&.empty?
58
+ builder.puts lines[cursor]
59
+ cursor += 1
60
+ end
61
+
62
+ builder.string
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,71 @@
1
+ # typed: strict
2
+ require 'sorbet-runtime'
3
+ require 'json'
4
+ require 'spud/driver'
5
+ require 'spud/task_args'
6
+ require 'spud/shell/command'
7
+ require 'spud/task_runners/task'
8
+
9
+ module Spud
10
+ module TaskRunners
11
+ module PackageJSON
12
+ class Task < TaskRunners::Task
13
+ extend T::Sig
14
+
15
+ sig {override.returns(String)}
16
+ attr_reader :name
17
+
18
+ sig {override.params(driver: Driver).returns(T::Array[TaskRunners::Task])}
19
+ def self.tasks(driver)
20
+ if File.exist?('package.lock')
21
+ if `command -v npm`.empty?
22
+ puts 'package.json detected, but no installation of `npm` exists. Skipping npm...'
23
+ return []
24
+ else
25
+ command = 'npm'
26
+ end
27
+ elsif File.exist?('yarn.lock')
28
+ if `command -v yarn`.empty?
29
+ puts 'package.json detected, but no installation of `yarn` exists. Skipping yarn...'
30
+ return []
31
+ else
32
+ command = 'yarn'
33
+ end
34
+ else
35
+ return []
36
+ end
37
+
38
+ source = File.read('package.json')
39
+ json = JSON.parse(source)
40
+ scripts = json['scripts']
41
+ return [] unless scripts
42
+
43
+ scripts.keys.map { |name| new(driver, name, command, scripts) }
44
+ end
45
+
46
+ sig {params(driver: Driver, name: String, command: String, scripts: T::Hash[String, String]).void}
47
+ def initialize(driver, name, command, scripts)
48
+ @driver = driver
49
+ @name = name
50
+ @command = command
51
+ @scripts = scripts
52
+ end
53
+
54
+ sig {override.params(ordered: T::Array[String], named: T::Hash[String, String]).returns(T.untyped)}
55
+ def invoke(ordered, named)
56
+ Shell::Command.("#{@command} run #{name}", driver: @driver)
57
+ end
58
+
59
+ sig {override.returns(String)}
60
+ def filename
61
+ 'package.json'
62
+ end
63
+
64
+ sig {override.returns(String)}
65
+ def details
66
+ %({ "#{name}": "#{@scripts[name]}" })
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end