spud 0.2.3 → 0.2.4

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 (36) 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 +22 -19
  6. data/lib/spud/cli/results.rb +18 -11
  7. data/lib/spud/driver.rb +134 -0
  8. data/lib/spud/error.rb +1 -0
  9. data/lib/spud/help.rb +13 -14
  10. data/lib/spud/lister.rb +27 -22
  11. data/lib/spud/task_arg.rb +15 -12
  12. data/lib/spud/task_args.rb +20 -18
  13. data/lib/spud/task_runners/make/task.rb +66 -0
  14. data/lib/spud/task_runners/package.json/task.rb +70 -0
  15. data/lib/spud/{build_tools/spud → task_runners/spud_task_runner}/dependency.rb +3 -2
  16. data/lib/spud/task_runners/spud_task_runner/file_dsl.rb +56 -0
  17. data/lib/spud/task_runners/spud_task_runner/shell/command.rb +58 -0
  18. data/lib/spud/{build_tools/spud → task_runners/spud_task_runner}/shell/result.rb +9 -4
  19. data/lib/spud/task_runners/spud_task_runner/task.rb +143 -0
  20. data/lib/spud/task_runners/spud_task_runner/task_dsl.rb +82 -0
  21. data/lib/spud/task_runners/task.rb +43 -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 -17
  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/task.rb +0 -177
  34. data/lib/spud/build_tools/task.rb +0 -41
  35. data/lib/spud/options.rb +0 -14
  36. data/lib/spud/runtime.rb +0 -114
@@ -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
@@ -1,39 +1,42 @@
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 {params(name: String, type: String, default: T.nilable(String)).void}
6
9
  def initialize(name, type, default: nil)
7
- raise 'must be of type "positional" or "named"' unless %w[positional named].include?(type)
10
+ raise 'must be of type "ordered" or "named"' unless %w[ordered named].include?(type)
8
11
 
9
12
  @name = name
10
13
  @type = type
11
14
  @default = default
12
15
  end
13
16
 
14
- # @return [Boolean]
17
+ sig {returns(T::Boolean)}
15
18
  def required?
16
19
  !has_default?
17
20
  end
18
21
 
19
- # @return [Boolean]
22
+ sig {returns(T::Boolean)}
20
23
  def has_default?
21
24
  !!@default
22
25
  end
23
26
 
24
- # @return [Boolean]
25
- def positional?
26
- @type == 'positional'
27
+ sig {returns(T::Boolean)}
28
+ def ordered?
29
+ @type == 'ordered'
27
30
  end
28
31
 
29
- # @return [Boolean]
32
+ sig {returns(T::Boolean)}
30
33
  def named?
31
34
  @type == 'named'
32
35
  end
33
36
 
34
- # @return [String]
37
+ sig {returns(String)}
35
38
  def to_s
36
- if positional?
39
+ if ordered?
37
40
  if has_default?
38
41
  "<#{@name}=#{@default}>"
39
42
  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,66 @@
1
+ # typed: strict
2
+ require 'sorbet-runtime'
3
+ require 'stringio'
4
+ require 'spud/driver'
5
+ require 'spud/task_runners/task'
6
+
7
+ module Spud
8
+ module TaskRunners
9
+ module Make
10
+ class Task < TaskRunners::Task
11
+ extend T::Sig
12
+
13
+ sig {override.returns(String)}
14
+ attr_reader :name
15
+
16
+ sig {override.params(driver: Driver).returns(T::Array[TaskRunners::Task])}
17
+ def self.tasks(driver)
18
+ return [] unless File.exist?('Makefile')
19
+
20
+ if `command -v make`.empty?
21
+ puts 'Makefile detected, but no installation of `make` exists. Skipping make...'
22
+ return []
23
+ end
24
+
25
+ source = File.read('Makefile')
26
+ T.unsafe(source.scan(/^(\S+):.*/)).map(&:first).map do |name|
27
+ new(driver, name)
28
+ end
29
+ end
30
+
31
+ sig {params(driver: Driver, name: String).void}
32
+ def initialize(driver, name)
33
+ @driver = driver
34
+ @name = name
35
+ end
36
+
37
+ sig {override.params(ordered: T::Array[String], named: T::Hash[String, String]).returns(T.untyped)}
38
+ def invoke(ordered, named)
39
+ system('make', name)
40
+ end
41
+
42
+ sig {override.returns(String)}
43
+ def filename
44
+ 'Makefile'
45
+ end
46
+
47
+ sig {override.returns(String)}
48
+ def details
49
+ source = File.read('Makefile')
50
+ lines = source.split("\n")
51
+ cursor = 0
52
+
53
+ cursor += 1 until lines[cursor]&.start_with?(name)
54
+
55
+ builder = StringIO.new
56
+ until lines[cursor]&.empty?
57
+ builder.puts lines[cursor]
58
+ cursor += 1
59
+ end
60
+
61
+ builder.string
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,70 @@
1
+ # typed: strict
2
+ require 'sorbet-runtime'
3
+ require 'json'
4
+ require 'spud/driver'
5
+ require 'spud/task_args'
6
+ require 'spud/task_runners/task'
7
+
8
+ module Spud
9
+ module TaskRunners
10
+ module PackageJSON
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
+ if File.exist?('package.lock')
20
+ if `command -v npm`.empty?
21
+ puts 'package.json detected, but no installation of `npm` exists. Skipping npm...'
22
+ return []
23
+ else
24
+ command = 'npm'
25
+ end
26
+ elsif File.exist?('yarn.lock')
27
+ if `command -v yarn`.empty?
28
+ puts 'package.json detected, but no installation of `yarn` exists. Skipping yarn...'
29
+ return []
30
+ else
31
+ command = 'yarn'
32
+ end
33
+ else
34
+ return []
35
+ end
36
+
37
+ source = File.read('package.json')
38
+ json = JSON.parse(source)
39
+ scripts = json['scripts']
40
+ return [] unless scripts
41
+
42
+ scripts.keys.map { |name| new(driver, name, command, scripts) }
43
+ end
44
+
45
+ sig {params(driver: Driver, name: String, command: String, scripts: T::Hash[String, String]).void}
46
+ def initialize(driver, name, command, scripts)
47
+ @driver = driver
48
+ @name = name
49
+ @command = command
50
+ @scripts = scripts
51
+ end
52
+
53
+ sig {override.params(ordered: T::Array[String], named: T::Hash[String, String]).returns(T.untyped)}
54
+ def invoke(ordered, named)
55
+ system("#{@command} run #{name}")
56
+ end
57
+
58
+ sig {override.returns(String)}
59
+ def filename
60
+ 'package.json'
61
+ end
62
+
63
+ sig {override.returns(String)}
64
+ def details
65
+ %({ "#{name}": "#{@scripts[name]}" })
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -1,6 +1,7 @@
1
+ # typed: true
1
2
  module Spud
2
- module BuildTools
3
- module Spud
3
+ module TaskRunners
4
+ module SpudTaskRunner
4
5
  class Dependency
5
6
  def initialize(source, target)
6
7
  @sources = [source].flatten
@@ -0,0 +1,56 @@
1
+ # typed: true
2
+ require 'sorbet-runtime'
3
+ require 'spud/driver'
4
+ require 'spud/task_runners/spud_task_runner/task'
5
+
6
+ module Spud
7
+ module TaskRunners
8
+ module SpudTaskRunner
9
+ class FileDSL
10
+ extend T::Sig
11
+
12
+ sig {params(driver: Driver, filename: String).returns(T::Array[SpudTaskRunner::Task])}
13
+ def self.run(driver, filename)
14
+ dsl = new(driver, filename)
15
+ dsl.instance_eval(::File.read(filename), filename)
16
+
17
+ dsl.instance_variable_get(:@__tasks)
18
+ end
19
+
20
+ sig {params(driver: Driver, filename: String).void}
21
+ def initialize(driver, filename)
22
+ @__driver = driver
23
+ @__filename = filename
24
+
25
+ @__tasks = T.let([], T::Array[Task])
26
+ end
27
+
28
+ sig {params(name: String).returns(T::Boolean)}
29
+ def require_relative(name)
30
+ require("./#{name}")
31
+ end
32
+
33
+ sig do
34
+ params(
35
+ name: T.any(String, Symbol),
36
+ dependencies: T::Hash[T.any(String, T::Array[String]), T.any(String, T::Array[String])],
37
+ block: Proc,
38
+ ).void
39
+ end
40
+ def task(name, dependencies = {}, &block)
41
+ @__tasks << Task.new(
42
+ driver: @__driver,
43
+ name: Task.qualified_name(@__filename, name.to_s),
44
+ filename: @__filename,
45
+ dependencies: dependencies,
46
+ &block
47
+ )
48
+ end
49
+
50
+ def method_missing(name, *args, &block)
51
+ T.unsafe(self).task(name, *args, &block)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,58 @@
1
+ # typed: true
2
+ require 'sorbet-runtime'
3
+ require 'stringio'
4
+ require 'open3'
5
+ require 'spud/driver'
6
+ require 'spud/task_runners/spud_task_runner/shell/result'
7
+
8
+ module Spud
9
+ module TaskRunners
10
+ module SpudTaskRunner
11
+ module Shell
12
+ class Command
13
+ extend T::Sig
14
+
15
+ attr_reader :result
16
+
17
+ sig {params(driver: Driver, command: String, silent: T::Boolean, handle: T.any(IO, StringIO)).returns(Result)}
18
+ def self.call(driver, command, silent: false, handle: STDOUT)
19
+ cmd = new(driver, command, silent: silent, handle: handle)
20
+ cmd.issue!
21
+ cmd.result
22
+ end
23
+
24
+ sig {params(driver: Driver, command: String, silent: T::Boolean, handle: T.any(IO, StringIO)).void}
25
+ def initialize(driver, command, silent: false, handle: STDOUT)
26
+ @command = command
27
+ @driver = driver
28
+ @silent = silent
29
+ @handle = handle
30
+
31
+ @result = T.let(nil, T.nilable(Result))
32
+ end
33
+
34
+ sig {void}
35
+ def issue!
36
+ capturer = StringIO.new
37
+
38
+ Open3.popen2e(@command) do |_stdin, stdout, thread|
39
+ @driver.register_subprocess(thread.pid)
40
+
41
+ loop do
42
+ line = stdout.gets
43
+ break unless line
44
+
45
+ @handle.write line unless @silent
46
+ capturer.puts line
47
+ end
48
+
49
+ @result = Result.new(capturer.string, T.cast(thread.value, Process::Status))
50
+ end
51
+
52
+ @driver.register_subprocess(nil)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end