bake 0.11.0 → 0.14.1
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.
- checksums.yaml +4 -4
- data/README.md +10 -87
- data/bake.gemspec +1 -4
- data/bake.rb +0 -4
- data/gems.rb +6 -0
- data/guides/command-line-interface/README.md +55 -0
- data/guides/gem-integration/README.md +69 -0
- data/guides/getting-started/README.md +109 -0
- data/guides/links.yaml +8 -0
- data/guides/project-integration/README.md +64 -0
- data/lib/bake/base.rb +19 -0
- data/lib/bake/command.rb +1 -0
- data/lib/bake/command/call.rb +1 -0
- data/lib/bake/command/list.rb +43 -19
- data/lib/bake/command/top.rb +3 -2
- data/lib/bake/context.rb +20 -3
- data/lib/bake/documentation.rb +100 -0
- data/lib/bake/loader.rb +9 -12
- data/lib/bake/loaders.rb +15 -18
- data/lib/bake/recipe.rb +43 -22
- data/lib/bake/scope.rb +11 -4
- data/lib/bake/types.rb +2 -0
- data/lib/bake/types/any.rb +35 -8
- data/lib/bake/types/boolean.rb +4 -0
- data/lib/bake/types/input.rb +42 -0
- data/lib/bake/types/nil.rb +1 -1
- data/lib/bake/types/output.rb +42 -0
- data/lib/bake/version.rb +1 -1
- metadata +14 -35
- data/example.png +0 -0
data/lib/bake/base.rb
CHANGED
@@ -22,7 +22,10 @@ require_relative 'recipe'
|
|
22
22
|
require_relative 'scope'
|
23
23
|
|
24
24
|
module Bake
|
25
|
+
# The base class for including {Scope} instances which define {Recipe} instances.
|
25
26
|
class Base < Struct.new(:context)
|
27
|
+
# Generate a base class for the specified path.
|
28
|
+
# @parameter path [Array(String)] The command path.
|
26
29
|
def self.derive(path = [])
|
27
30
|
klass = Class.new(self)
|
28
31
|
|
@@ -31,6 +34,8 @@ module Bake
|
|
31
34
|
return klass
|
32
35
|
end
|
33
36
|
|
37
|
+
# Format the class as a command.
|
38
|
+
# @returns [String]
|
34
39
|
def self.to_s
|
35
40
|
if path = self.path
|
36
41
|
path.join(':')
|
@@ -47,20 +52,31 @@ module Bake
|
|
47
52
|
end
|
48
53
|
end
|
49
54
|
|
55
|
+
# The path of this derived base class.
|
56
|
+
# @returns [Array(String)]
|
50
57
|
def self.path
|
51
58
|
self.const_get(:PATH)
|
52
59
|
rescue
|
53
60
|
nil
|
54
61
|
end
|
55
62
|
|
63
|
+
# The path for this derived base class.
|
64
|
+
# @returns [Array(String)]
|
56
65
|
def path
|
57
66
|
self.class.path
|
58
67
|
end
|
59
68
|
|
69
|
+
# Proxy a method call using command line arguments through to the {Context} instance.
|
70
|
+
# @parameter arguments [Array(String)]
|
60
71
|
def call(*arguments)
|
61
72
|
self.context.call(*arguments)
|
62
73
|
end
|
63
74
|
|
75
|
+
# Recipes defined in this scope.
|
76
|
+
#
|
77
|
+
# @yields {|recipe| ...}
|
78
|
+
# @parameter recipe [Recipe]
|
79
|
+
# @returns [Enumerable]
|
64
80
|
def recipes
|
65
81
|
return to_enum(:recipes) unless block_given?
|
66
82
|
|
@@ -71,6 +87,9 @@ module Bake
|
|
71
87
|
end
|
72
88
|
end
|
73
89
|
|
90
|
+
# Look up a recipe with a specific name.
|
91
|
+
#
|
92
|
+
# @parameter name [String] The instance method to look up.
|
74
93
|
def recipe_for(name)
|
75
94
|
Recipe.new(self, name)
|
76
95
|
end
|
data/lib/bake/command.rb
CHANGED
data/lib/bake/command/call.rb
CHANGED
data/lib/bake/command/list.rb
CHANGED
@@ -23,8 +23,11 @@ require 'set'
|
|
23
23
|
|
24
24
|
module Bake
|
25
25
|
module Command
|
26
|
+
# List all available commands.
|
26
27
|
class List < Samovar::Command
|
27
|
-
|
28
|
+
self.description = "List all available commands."
|
29
|
+
|
30
|
+
one :pattern, "The pattern to filter tasks by."
|
28
31
|
|
29
32
|
def format_parameters(parameters, terminal)
|
30
33
|
parameters.each do |type, name|
|
@@ -52,25 +55,39 @@ module Bake
|
|
52
55
|
end
|
53
56
|
end
|
54
57
|
|
55
|
-
def print_scope(terminal, scope)
|
58
|
+
def print_scope(terminal, scope, printed: false)
|
56
59
|
format_recipe = self.method(:format_recipe).curry
|
57
60
|
|
58
61
|
scope.recipes.sort.each do |recipe|
|
62
|
+
if @pattern and !recipe.command.include?(pattern)
|
63
|
+
next
|
64
|
+
end
|
65
|
+
|
66
|
+
unless printed
|
67
|
+
yield
|
68
|
+
|
69
|
+
printed = true
|
70
|
+
end
|
71
|
+
|
59
72
|
terminal.print_line
|
60
73
|
terminal.print_line("\t", format_recipe[recipe])
|
61
74
|
|
62
|
-
recipe.
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
75
|
+
documentation = recipe.documentation
|
76
|
+
|
77
|
+
documentation.description do |line|
|
78
|
+
terminal.print_line("\t\t", :description, line)
|
79
|
+
end
|
80
|
+
|
81
|
+
documentation.parameters do |parameter|
|
82
|
+
terminal.print_line("\t\t",
|
83
|
+
:parameter, parameter[:name], :reset, " [",
|
84
|
+
:type, parameter[:type], :reset, "] ",
|
85
|
+
:description, parameter[:details]
|
86
|
+
)
|
72
87
|
end
|
73
88
|
end
|
89
|
+
|
90
|
+
return printed
|
74
91
|
end
|
75
92
|
|
76
93
|
def call
|
@@ -79,23 +96,30 @@ module Bake
|
|
79
96
|
context = @parent.context
|
80
97
|
|
81
98
|
if scope = context.scope
|
82
|
-
terminal
|
83
|
-
|
84
|
-
|
99
|
+
printed = print_scope(terminal, context.scope) do
|
100
|
+
terminal.print_line(:context, context)
|
101
|
+
end
|
85
102
|
|
86
|
-
|
103
|
+
if printed
|
104
|
+
terminal.print_line
|
105
|
+
end
|
87
106
|
end
|
88
107
|
|
89
108
|
context.loaders.each do |loader|
|
90
|
-
|
109
|
+
printed = false
|
91
110
|
|
92
111
|
loader.each do |path|
|
93
112
|
if scope = loader.scope_for(path)
|
94
|
-
print_scope(terminal, scope)
|
113
|
+
print_scope(terminal, scope, printed: printed) do
|
114
|
+
terminal.print_line(:loader, loader)
|
115
|
+
printed = true
|
116
|
+
end
|
95
117
|
end
|
96
118
|
end
|
97
119
|
|
98
|
-
|
120
|
+
if printed
|
121
|
+
terminal.print_line
|
122
|
+
end
|
99
123
|
end
|
100
124
|
end
|
101
125
|
end
|
data/lib/bake/command/top.rb
CHANGED
@@ -26,6 +26,7 @@ require_relative 'list'
|
|
26
26
|
|
27
27
|
module Bake
|
28
28
|
module Command
|
29
|
+
# The top level command line application.
|
29
30
|
class Top < Samovar::Command
|
30
31
|
self.description = "Execute tasks using Ruby."
|
31
32
|
|
@@ -56,12 +57,12 @@ module Bake
|
|
56
57
|
return terminal
|
57
58
|
end
|
58
59
|
|
59
|
-
def
|
60
|
+
def bakefile_path
|
60
61
|
@options[:bakefile] || Dir.pwd
|
61
62
|
end
|
62
63
|
|
63
64
|
def context
|
64
|
-
Context.load(self.
|
65
|
+
Context.load(self.bakefile_path)
|
65
66
|
end
|
66
67
|
|
67
68
|
def call
|
data/lib/bake/context.rb
CHANGED
@@ -21,11 +21,14 @@
|
|
21
21
|
require_relative 'base'
|
22
22
|
|
23
23
|
module Bake
|
24
|
+
# The default file name for the top level bakefile.
|
24
25
|
BAKEFILE = "bake.rb"
|
25
26
|
|
27
|
+
# Represents a context of task execution, containing all relevant state.
|
26
28
|
class Context
|
29
|
+
# Search upwards from the specified path for a {BAKEFILE}.
|
27
30
|
# If path points to a file, assume it's a `bake.rb` file. Otherwise, recursively search up the directory tree starting from `path` to find the specified bakefile.
|
28
|
-
# @
|
31
|
+
# @returns [String | Nil] The path to the bakefile if it could be found.
|
29
32
|
def self.bakefile_path(path, bakefile: BAKEFILE)
|
30
33
|
if File.file?(path)
|
31
34
|
return path
|
@@ -52,6 +55,8 @@ module Bake
|
|
52
55
|
return nil
|
53
56
|
end
|
54
57
|
|
58
|
+
# Load a context from the specified path.
|
59
|
+
# @path [String] A file-system path.
|
55
60
|
def self.load(path = Dir.pwd)
|
56
61
|
if bakefile_path = self.bakefile_path(path)
|
57
62
|
scope = Scope.load(bakefile_path)
|
@@ -68,6 +73,8 @@ module Bake
|
|
68
73
|
return self.new(loaders, scope, working_directory)
|
69
74
|
end
|
70
75
|
|
76
|
+
# Initialize the context with the specified loaders.
|
77
|
+
# @parameter loaders [Loaders]
|
71
78
|
def initialize(loaders, scope = nil, root = nil)
|
72
79
|
@loaders = loaders
|
73
80
|
|
@@ -92,10 +99,18 @@ module Bake
|
|
92
99
|
end
|
93
100
|
end
|
94
101
|
|
102
|
+
# The loaders which will be used to resolve recipes in this context.
|
103
|
+
attr :loaders
|
104
|
+
|
105
|
+
# The scope for the root {BAKEFILE}.
|
95
106
|
attr :scope
|
107
|
+
|
108
|
+
# The root path of this context.
|
109
|
+
# @returns [String | Nil]
|
96
110
|
attr :root
|
97
|
-
attr :loaders
|
98
111
|
|
112
|
+
# Invoke recipes on the context using command line arguments.
|
113
|
+
# @parameter commands [Array(String)]
|
99
114
|
def call(*commands)
|
100
115
|
last_result = nil
|
101
116
|
|
@@ -111,6 +126,8 @@ module Bake
|
|
111
126
|
return last_result
|
112
127
|
end
|
113
128
|
|
129
|
+
# Lookup a recipe for the given command name.
|
130
|
+
# @parameter command [String] The command name, e.g. `bundler:release`.
|
114
131
|
def lookup(command)
|
115
132
|
@recipes[command]
|
116
133
|
end
|
@@ -147,7 +164,7 @@ module Bake
|
|
147
164
|
end
|
148
165
|
end
|
149
166
|
|
150
|
-
# @
|
167
|
+
# @parameter path [Array<String>] the path for the scope.
|
151
168
|
def base_for(path)
|
152
169
|
base = nil
|
153
170
|
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require_relative 'scope'
|
22
|
+
|
23
|
+
module Bake
|
24
|
+
# Structured access to a set of comment lines.
|
25
|
+
class Documentation
|
26
|
+
# Initialize the documentation with an array of comments.
|
27
|
+
#
|
28
|
+
# @parameter comments [Array(String)] An array of comment lines.
|
29
|
+
def initialize(comments)
|
30
|
+
@comments = comments
|
31
|
+
end
|
32
|
+
|
33
|
+
DESCRIPTION = /\A\s*([^@\s].*)?\z/
|
34
|
+
|
35
|
+
# The text-only lines of the comment block.
|
36
|
+
#
|
37
|
+
# @yields {|match| ...}
|
38
|
+
# @parameter match [MatchData] The regular expression match for each line of documentation.
|
39
|
+
# @returns [Enumerable] If no block given.
|
40
|
+
def description
|
41
|
+
return to_enum(:description) unless block_given?
|
42
|
+
|
43
|
+
# We track empty lines and only yield a single empty line when there is another line of text:
|
44
|
+
gap = false
|
45
|
+
|
46
|
+
@comments.each do |comment|
|
47
|
+
if match = comment.match(DESCRIPTION)
|
48
|
+
if match[1]
|
49
|
+
if gap
|
50
|
+
yield ""
|
51
|
+
gap = false
|
52
|
+
end
|
53
|
+
|
54
|
+
yield match[1]
|
55
|
+
else
|
56
|
+
gap = true
|
57
|
+
end
|
58
|
+
else
|
59
|
+
break
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
ATTRIBUTE = /\A\s*@(?<name>.*?)\s+(?<value>.*?)\z/
|
65
|
+
|
66
|
+
# The attribute lines of the comment block.
|
67
|
+
# e.g. `@returns [String]`.
|
68
|
+
#
|
69
|
+
# @yields {|match| ...}
|
70
|
+
# @parameter match [MatchData] The regular expression match with `name` and `value` keys.
|
71
|
+
# @returns [Enumerable] If no block given.
|
72
|
+
def attributes
|
73
|
+
return to_enum(:attributes) unless block_given?
|
74
|
+
|
75
|
+
@comments.each do |comment|
|
76
|
+
if match = comment.match(ATTRIBUTE)
|
77
|
+
yield match
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
PARAMETER = /\A\s*@param(eter)?\s+(?<name>.*?)\s+\[(?<type>.*?)\](\s+(?<details>.*?))?\z/
|
83
|
+
|
84
|
+
# The parameter lines of the comment block.
|
85
|
+
# e.g. `@parameter value [String] The value.`
|
86
|
+
#
|
87
|
+
# @yields {|match| ...}
|
88
|
+
# @parameter match [MatchData] The regular expression match with `name`, `type` and `details` keys.
|
89
|
+
# @returns [Enumerable] If no block given.
|
90
|
+
def parameters
|
91
|
+
return to_enum(:parameters) unless block_given?
|
92
|
+
|
93
|
+
@comments.each do |comment|
|
94
|
+
if match = comment.match(PARAMETER)
|
95
|
+
yield match
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
data/lib/bake/loader.rb
CHANGED
@@ -21,7 +21,10 @@
|
|
21
21
|
require_relative 'scope'
|
22
22
|
|
23
23
|
module Bake
|
24
|
+
# Represents a directory which contains bakefiles.
|
24
25
|
class Loader
|
26
|
+
# Initialize the loader with the specified root path.
|
27
|
+
# @parameter root [String] A file-system path.
|
25
28
|
def initialize(root, name: nil)
|
26
29
|
@root = root
|
27
30
|
@name = name
|
@@ -31,8 +34,12 @@ module Bake
|
|
31
34
|
"#{self.class} #{@name || @root}"
|
32
35
|
end
|
33
36
|
|
37
|
+
# The root path for this loader.
|
34
38
|
attr :root
|
35
39
|
|
40
|
+
# Enumerate all bakefiles within the loaders root directory.
|
41
|
+
# @yields {|path| ...}
|
42
|
+
# @parameter path [String] The Ruby source file path.
|
36
43
|
def each
|
37
44
|
return to_enum unless block_given?
|
38
45
|
|
@@ -41,18 +48,8 @@ module Bake
|
|
41
48
|
end
|
42
49
|
end
|
43
50
|
|
44
|
-
|
45
|
-
|
46
|
-
return book.lookup(path.last)
|
47
|
-
else
|
48
|
-
*path, name = *path
|
49
|
-
|
50
|
-
if book = lookup(path)
|
51
|
-
return book.lookup(name)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
51
|
+
# Load the {Scope} for the specified relative path within this loader, if it exists.
|
52
|
+
# @parameter path [Array(String)] A relative path.
|
56
53
|
def scope_for(path)
|
57
54
|
*directory, file = *path
|
58
55
|
|
data/lib/bake/loaders.rb
CHANGED
@@ -23,9 +23,12 @@ require 'console'
|
|
23
23
|
require_relative 'loader'
|
24
24
|
|
25
25
|
module Bake
|
26
|
+
# Structured access to the working directory and loaded gems for loading bakefiles.
|
26
27
|
class Loaders
|
27
28
|
include Enumerable
|
28
29
|
|
30
|
+
# Create a loader using the specified working directory.
|
31
|
+
# @parameter working_directory [String]
|
29
32
|
def self.default(working_directory)
|
30
33
|
loaders = self.new
|
31
34
|
|
@@ -34,15 +37,20 @@ module Bake
|
|
34
37
|
return loaders
|
35
38
|
end
|
36
39
|
|
40
|
+
# Initialize an empty array of loaders.
|
37
41
|
def initialize
|
38
42
|
@roots = {}
|
39
43
|
@ordered = Array.new
|
40
44
|
end
|
41
45
|
|
46
|
+
# Whether any loaders are defined.
|
47
|
+
# @returns [Boolean]
|
42
48
|
def empty?
|
43
49
|
@ordered.empty?
|
44
50
|
end
|
45
51
|
|
52
|
+
# Add loaders according to the current working directory and loaded gems.
|
53
|
+
# @parameter working_directory [String]
|
46
54
|
def append_defaults(working_directory)
|
47
55
|
# Load recipes from working directory:
|
48
56
|
self.append_path(working_directory)
|
@@ -51,32 +59,26 @@ module Bake
|
|
51
59
|
self.append_from_gems
|
52
60
|
end
|
53
61
|
|
62
|
+
# Enumerate the loaders in order.
|
54
63
|
def each(&block)
|
55
|
-
return to_enum unless block_given?
|
56
|
-
|
57
64
|
@ordered.each(&block)
|
58
65
|
end
|
59
66
|
|
67
|
+
# Append a specific project path to the search path for recipes.
|
68
|
+
# The computed path will have `bake` appended to it.
|
69
|
+
# @parameter current [String] The path to add.
|
60
70
|
def append_path(current = Dir.pwd, **options)
|
61
|
-
recipes_path = File.join(current, "recipes")
|
62
71
|
bake_path = File.join(current, "bake")
|
63
72
|
|
64
73
|
if File.directory?(bake_path)
|
65
74
|
return insert(bake_path, **options)
|
66
75
|
end
|
67
76
|
|
68
|
-
if File.directory?(recipes_path)
|
69
|
-
Console.logger.warn(self) do |buffer|
|
70
|
-
buffer.puts "Recipes path: #{recipes_path.inspect} is deprecated."
|
71
|
-
buffer.puts "Please use #{bake_path.inspect} instead."
|
72
|
-
end
|
73
|
-
|
74
|
-
return insert(recipes_path, **options)
|
75
|
-
end
|
76
|
-
|
77
77
|
return false
|
78
78
|
end
|
79
79
|
|
80
|
+
# Search from the current working directory until a suitable bakefile is found and add it.
|
81
|
+
# @parameter current [String] The path to start searching from.
|
80
82
|
def append_from_root(current = Dir.pwd, **options)
|
81
83
|
while current
|
82
84
|
append_path(current, **options)
|
@@ -91,6 +93,7 @@ module Bake
|
|
91
93
|
end
|
92
94
|
end
|
93
95
|
|
96
|
+
# Enumerate all loaded gems and add them.
|
94
97
|
def append_from_gems
|
95
98
|
Gem.loaded_specs.each do |name, spec|
|
96
99
|
Console.logger.debug(self) {"Checking gem #{name}: #{spec.full_gem_path}..."}
|
@@ -101,12 +104,6 @@ module Bake
|
|
101
104
|
end
|
102
105
|
end
|
103
106
|
|
104
|
-
def append_from_paths(paths, **options)
|
105
|
-
paths.each do |path|
|
106
|
-
append_path(path, **options)
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
107
|
protected
|
111
108
|
|
112
109
|
def insert(directory, **options)
|