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,12 +1,17 @@
1
+ # typed: false
2
+ require 'sorbet-runtime'
3
+
1
4
  module Spud
2
- module BuildTools
3
- module Spud
5
+ module TaskRunners
6
+ module SpudTaskRunner
4
7
  module Shell
5
8
  class Result < String
9
+ extend T::Sig
10
+
11
+ sig {returns(Process::Status)}
6
12
  attr_reader :status
7
13
 
8
- # @param output [String]
9
- # @param status [Process::Status]
14
+ sig {params(output: String, status: Process::Status).void}
10
15
  def initialize(output, status)
11
16
  super(output)
12
17
  @status = status
@@ -0,0 +1,143 @@
1
+ # typed: true
2
+ require 'sorbet-runtime'
3
+ require 'stringio'
4
+ require 'spud/driver'
5
+ require 'spud/error'
6
+ require 'spud/task_args'
7
+ require 'spud/task_runners/task'
8
+ require 'spud/task_runners/spud_task_runner/dependency'
9
+ require 'spud/task_runners/spud_task_runner/task'
10
+ require 'spud/task_runners/spud_task_runner/file_dsl'
11
+ require 'spud/task_runners/spud_task_runner/task_dsl'
12
+
13
+ module Spud
14
+ module TaskRunners
15
+ module SpudTaskRunner
16
+ class Task < TaskRunners::Task
17
+ extend T::Sig
18
+
19
+ sig {override.returns(String)}
20
+ attr_reader :filename
21
+
22
+ sig {override.returns(String)}
23
+ attr_reader :name
24
+
25
+ sig {override.params(driver: Driver).returns(T::Array[TaskRunners::Task])}
26
+ def self.tasks(driver)
27
+ Dir['**/Spudfile', '**/*.spud'].flat_map { |filename| FileDSL.run(driver, filename) }
28
+ end
29
+
30
+ sig {params(filename: String, name: String).returns(String)}
31
+ def self.qualified_name(filename, name)
32
+ segments = File.dirname(filename)
33
+ .split('/')
34
+ .reject { |segment| segment == '.' }
35
+
36
+ basename = File.basename(filename, '.spud')
37
+ segments << basename unless basename == 'Spudfile'
38
+
39
+ segments << name
40
+ segments.join('.')
41
+ end
42
+
43
+ sig do
44
+ params(
45
+ driver: Driver,
46
+ name: String,
47
+ filename: String,
48
+ dependencies: T::Hash[T.any(String, T::Array[String]), T.any(String, T::Array[String])],
49
+ block: Proc,
50
+ )
51
+ .void
52
+ end
53
+ def initialize(driver:, name:, filename:, dependencies:, &block)
54
+ @driver = driver
55
+ @name = name
56
+ @filename = filename
57
+ @dependencies = dependencies.map { |to, from| Dependency.new(to, from) }
58
+ @block = block
59
+ end
60
+
61
+ sig {override.params(ordered: T::Array[String], named: T::Hash[String, String]).returns(T.untyped)}
62
+ def invoke(ordered, named)
63
+ if up_to_date?
64
+ puts "'#{name}' up to date"
65
+ return nil
66
+ end
67
+
68
+ check_required_args!(ordered)
69
+
70
+ if args.any_named?
71
+ T.unsafe(task_dsl).instance_exec(*ordered, **symbolize_keys(named), &@block)
72
+ else
73
+ T.unsafe(task_dsl).instance_exec(*ordered, &@block)
74
+ end
75
+ rescue ArgumentError => error
76
+ raise Error, "invocation of '#{name}' with #{error.message}"
77
+ end
78
+
79
+ sig {override.returns(TaskArgs)}
80
+ def args
81
+ @args ||= TaskArgs.from_block(filename, &@block)
82
+ end
83
+
84
+ sig {override.returns(String)}
85
+ def details
86
+ filename, line_cursor = @block.source_location
87
+ line_cursor -= 1
88
+
89
+ lines = File.read(filename).split("\n")
90
+ builder = StringIO.new
91
+
92
+ # Move up for comments
93
+ while lines[line_cursor - 1]&.start_with?('#')
94
+ line_cursor -= 1
95
+ end
96
+
97
+ # Capture comments
98
+ while lines[line_cursor]&.start_with?('#')
99
+ builder.puts lines[line_cursor]
100
+ line_cursor += 1
101
+ end
102
+
103
+ # Capture block
104
+ until lines[line_cursor - 1]&.start_with?('end')
105
+ builder.puts lines[line_cursor]
106
+ line_cursor += 1
107
+ end
108
+
109
+ builder.string
110
+ end
111
+
112
+ private
113
+
114
+ sig {params(ordered: T::Array[String]).void}
115
+ def check_required_args!(ordered)
116
+ required_ordered = args.required_ordered
117
+ missing_ordered = required_ordered.length - ordered.length
118
+ if missing_ordered > 0
119
+ arguments = required_ordered.length - missing_ordered > 1 ? 'arguments' : 'argument'
120
+ raise Error, "invocation of '#{name}' missing required #{arguments} #{required_ordered.join(', ')}"
121
+ end
122
+ end
123
+
124
+ sig {returns(T::Boolean)}
125
+ def up_to_date?
126
+ return false if @dependencies.empty?
127
+
128
+ @dependencies.all?(&:up_to_date?)
129
+ end
130
+
131
+ sig {returns(SpudTaskRunner::TaskDSL)}
132
+ def task_dsl
133
+ @task_dsl ||= TaskDSL.new(@driver, @filename)
134
+ end
135
+
136
+ sig {params(hash: T::Hash[T.any(String, Symbol), T.untyped]).returns(T::Hash[Symbol, T.untyped])}
137
+ def symbolize_keys(hash)
138
+ hash.each_with_object({}) { |(key, value), new_hash| new_hash[key.to_sym] = value }
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,82 @@
1
+ # typed: false
2
+ require 'sorbet-runtime'
3
+ require 'spud/error'
4
+ require 'spud/driver'
5
+ require 'spud/task_runners/spud_task_runner/shell/command'
6
+ require 'spud/task_runners/spud_task_runner/shell/result'
7
+
8
+ module Spud
9
+ module TaskRunners
10
+ module SpudTaskRunner
11
+ class TaskDSL
12
+ extend T::Sig
13
+
14
+ sig {params(driver: Driver, filename: String).void}
15
+ def initialize(driver, filename)
16
+ @__filename = filename
17
+ @__driver = driver
18
+ end
19
+
20
+ sig {params(command: String).returns(Shell::Result)}
21
+ def sh(command)
22
+ puts command
23
+ Shell::Command.(@__driver, command)
24
+ end
25
+
26
+ sig {params(command: String).returns(Shell::Result)}
27
+ def shh(command)
28
+ Shell::Command.(@__driver, command)
29
+ end
30
+
31
+ sig {params(command: String).returns(Shell::Result)}
32
+ def shhh(command)
33
+ Shell::Command.(@__driver, command, silent: true)
34
+ end
35
+
36
+ sig {params(command: String).returns(Shell::Result)}
37
+ def sh!(command)
38
+ puts command
39
+ result = Shell::Command.(@__driver, command)
40
+ raise Error, "sh failed for '#{command}'" unless result.success?
41
+ result
42
+ end
43
+
44
+ sig {params(command: String).returns(Shell::Result)}
45
+ def shh!(command)
46
+ result = Shell::Command.(@__driver, command)
47
+ raise Error, "sh failed for '#{command}'" unless result.success?
48
+ result
49
+ end
50
+
51
+ sig {params(command: String).returns(Shell::Result)}
52
+ def shhh!(command)
53
+ result = Shell::Command.(@__driver, command, silent: true)
54
+ raise Error, "sh failed for '#{command}'" unless result.success?
55
+ result
56
+ end
57
+
58
+ sig {params(task: String, ordered: String, named: String).returns(T.untyped)}
59
+ def invoke(task, *ordered, **named)
60
+ task = task.to_s
61
+ task = task.include?('.') ? task : Task.qualified_name(@__filename, task)
62
+ @__driver.invoke(task, ordered, named)
63
+ rescue Error => error
64
+ puts error.message
65
+ end
66
+
67
+ sig {params(task: String, ordered: String, named: String).returns(T.untyped)}
68
+ def invoke!(task, *ordered, **named)
69
+ task = task.to_s
70
+ task = task.include?('.') ? task : Task.qualified_name(@__filename, task)
71
+ @__driver.invoke(task, ordered, named)
72
+ end
73
+
74
+ def method_missing(symbol, *ordered, **named)
75
+ task = symbol.to_s
76
+ task = task.include?('.') ? task : Task.qualified_name(@__filename, task)
77
+ @__driver.invoke(task, ordered, named)
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,43 @@
1
+ # typed: strict
2
+ require 'sorbet-runtime'
3
+ require 'spud/driver'
4
+ require 'spud/task_args'
5
+
6
+ module Spud
7
+ module TaskRunners
8
+ class Task
9
+ extend T::Sig
10
+ extend T::Helpers
11
+ abstract!
12
+
13
+ sig {abstract.params(driver: Driver).returns(T::Array[TaskRunners::Task])}
14
+ def self.tasks(driver); end
15
+
16
+ sig {abstract.returns(String)}
17
+ def name; end
18
+
19
+ sig {abstract.returns(String)}
20
+ def filename; end
21
+
22
+ sig do
23
+ abstract
24
+ .params(
25
+ ordered: T::Array[String],
26
+ named: T::Hash[String, String],
27
+ )
28
+ .returns(T.untyped)
29
+ end
30
+ def invoke(ordered, named); end
31
+
32
+ sig {overridable.returns(TaskArgs)}
33
+ def args
34
+ TaskArgs.new([])
35
+ end
36
+
37
+ sig {overridable.returns(String)}
38
+ def details
39
+ name
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,25 @@
1
+ # typed: strict
2
+ require 'sorbet-runtime'
3
+ require 'spud/task_runners/task'
4
+ require 'spud/task_runners/spud_task_runner/task'
5
+ require 'spud/task_runners/make/task'
6
+ require 'spud/task_runners/package.json/task'
7
+
8
+ module Spud
9
+ module TaskRunners
10
+ extend T::Sig
11
+
12
+ sig {returns(T::Array[T.class_of(Task)])}
13
+ def self.get
14
+ # Ordered by priority
15
+ T.let(
16
+ [
17
+ T.let(SpudTaskRunner::Task, T.class_of(Task)),
18
+ T.let(Make::Task, T.class_of(Task)),
19
+ T.let(PackageJSON::Task, T.class_of(Task)),
20
+ ],
21
+ T::Array[T.class_of(Task)]
22
+ )
23
+ end
24
+ end
25
+ end
@@ -1,3 +1,4 @@
1
+ # typed: strict
1
2
  module Spud
2
- VERSION = '0.2.3'
3
+ VERSION = '0.2.4'
3
4
  end
@@ -0,0 +1,73 @@
1
+ # typed: true
2
+ require 'sorbet-runtime'
3
+ require 'spud/driver'
4
+
5
+ module Spud
6
+ class Watch
7
+ extend T::Sig
8
+
9
+ sig do
10
+ params(
11
+ driver: Driver,
12
+ task: String,
13
+ ordered: T::Array[String],
14
+ named: T::Hash[String, String],
15
+ watches: T::Array[String],
16
+ ).void
17
+ end
18
+ def self.run!(driver:, task:, ordered:, named:, watches:)
19
+ new(driver: driver, task: task, ordered: ordered, named: named, watches: watches).run!
20
+ end
21
+
22
+ sig do
23
+ params(
24
+ driver: Driver,
25
+ task: String,
26
+ ordered: T::Array[String],
27
+ named: T::Hash[String, String],
28
+ watches: T::Array[String],
29
+ ).void
30
+ end
31
+ def initialize(driver:, task:, ordered:, named:, watches:)
32
+ @driver = driver
33
+ @task = task
34
+ @ordered = ordered
35
+ @named = named
36
+ @watches = watches
37
+
38
+ @last_changed = Time.at(0)
39
+ end
40
+
41
+ sig {void}
42
+ def run!
43
+ thread = T.let(nil, T.nilable(Thread))
44
+
45
+ loop do
46
+ if watches_changed?
47
+ thread&.kill
48
+ Process.kill('SIGKILL', T.must(@driver.subprocess_pid)) if @driver.subprocess_pid
49
+
50
+ @last_changed = latest_watch_change
51
+ thread = Thread.new { @driver.invoke(@task, @ordered, @named) }
52
+ end
53
+
54
+ sleep(0.1)
55
+ end
56
+ rescue SystemExit, Interrupt => error
57
+ puts "handled interrupt #{error}" if @driver.debug?
58
+ end
59
+
60
+ sig {returns(T::Boolean)}
61
+ def watches_changed?
62
+ @last_changed < latest_watch_change
63
+ end
64
+
65
+ sig {returns(Time)}
66
+ def latest_watch_change
67
+ T.unsafe(Dir)[*@watches]
68
+ .map(&File.method(:stat))
69
+ .map(&:mtime)
70
+ .max
71
+ end
72
+ end
73
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spud
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Booth
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-11-18 00:00:00.000000000 Z
11
+ date: 2020-11-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -52,6 +52,34 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '11.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sorbet
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.5.6100
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.5.6100
69
+ - !ruby/object:Gem::Dependency
70
+ name: sorbet-runtime
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.5.6100
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.5.6100
55
83
  description:
56
84
  email: andrew@andrewbooth.xyz
57
85
  executables:
@@ -61,27 +89,28 @@ extra_rdoc_files: []
61
89
  files:
62
90
  - bin/spud
63
91
  - lib/spud.rb
64
- - lib/spud/build_tools/build_tools.rb
65
- - lib/spud/build_tools/make/task.rb
66
- - lib/spud/build_tools/package.json/task.rb
67
- - lib/spud/build_tools/spud/block_param_info.rb
68
- - lib/spud/build_tools/spud/dependency.rb
69
- - lib/spud/build_tools/spud/dsl/file.rb
70
- - lib/spud/build_tools/spud/dsl/task.rb
71
- - lib/spud/build_tools/spud/shell/command.rb
72
- - lib/spud/build_tools/spud/shell/result.rb
73
- - lib/spud/build_tools/spud/task.rb
74
- - lib/spud/build_tools/task.rb
92
+ - lib/spud/block_param_info.rb
93
+ - lib/spud/cli/options.rb
75
94
  - lib/spud/cli/parser.rb
76
95
  - lib/spud/cli/results.rb
96
+ - lib/spud/driver.rb
77
97
  - lib/spud/error.rb
78
98
  - lib/spud/help.rb
79
99
  - lib/spud/lister.rb
80
- - lib/spud/options.rb
81
- - lib/spud/runtime.rb
82
100
  - lib/spud/task_arg.rb
83
101
  - lib/spud/task_args.rb
102
+ - lib/spud/task_runners/make/task.rb
103
+ - lib/spud/task_runners/package.json/task.rb
104
+ - lib/spud/task_runners/spud_task_runner/dependency.rb
105
+ - lib/spud/task_runners/spud_task_runner/file_dsl.rb
106
+ - lib/spud/task_runners/spud_task_runner/shell/command.rb
107
+ - lib/spud/task_runners/spud_task_runner/shell/result.rb
108
+ - lib/spud/task_runners/spud_task_runner/task.rb
109
+ - lib/spud/task_runners/spud_task_runner/task_dsl.rb
110
+ - lib/spud/task_runners/task.rb
111
+ - lib/spud/task_runners/task_runners.rb
84
112
  - lib/spud/version.rb
113
+ - lib/spud/watch.rb
85
114
  homepage: https://github.com/broothie/spud#readme
86
115
  licenses:
87
116
  - MIT
@@ -92,9 +121,9 @@ require_paths:
92
121
  - lib
93
122
  required_ruby_version: !ruby/object:Gem::Requirement
94
123
  requirements:
95
- - - ">="
124
+ - - '='
96
125
  - !ruby/object:Gem::Version
97
- version: '0'
126
+ version: 2.3.0
98
127
  required_rubygems_version: !ruby/object:Gem::Requirement
99
128
  requirements:
100
129
  - - ">="