fig 0.1.81 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Changes +87 -0
- data/lib/fig.rb +1 -1
- data/lib/fig/command.rb +5 -0
- data/lib/fig/command/action/dump_package_definition_for_command_line.rb +62 -0
- data/lib/fig/command/action/dump_package_definition_parsed.rb +19 -2
- data/lib/fig/command/action/list_local.rb +9 -1
- data/lib/fig/command/action/list_remote.rb +9 -1
- data/lib/fig/command/action/role/list_variables_in_a_tree.rb +1 -1
- data/lib/fig/command/action/run_command_line.rb +1 -1
- data/lib/fig/command/action/run_command_statement.rb +4 -2
- data/lib/fig/command/options.rb +50 -18
- data/lib/fig/command/options/parser.rb +16 -15
- data/lib/fig/command/package_applier.rb +5 -3
- data/lib/fig/grammar/v0.rb +287 -289
- data/lib/fig/grammar/v0.treetop +66 -42
- data/lib/fig/grammar/v1.rb +629 -533
- data/lib/fig/grammar/v1.treetop +102 -39
- data/lib/fig/grammar_monkey_patches.rb +21 -0
- data/lib/fig/operating_system.rb +53 -36
- data/lib/fig/package_descriptor.rb +1 -12
- data/lib/fig/parser.rb +8 -33
- data/lib/fig/parser_package_build_state.rb +92 -31
- data/lib/fig/repository_package_publisher.rb +2 -2
- data/lib/fig/runtime_environment.rb +54 -120
- data/lib/fig/statement.rb +6 -6
- data/lib/fig/statement/asset.rb +1 -13
- data/lib/fig/statement/command.rb +47 -0
- data/lib/fig/statement/environment_variable.rb +64 -3
- data/lib/fig/statement/grammar_version.rb +4 -0
- data/lib/fig/statement/include.rb +4 -0
- data/lib/fig/statement/override.rb +4 -0
- data/lib/fig/statement/path.rb +40 -16
- data/lib/fig/statement/retrieve.rb +61 -5
- data/lib/fig/statement/set.rb +16 -19
- data/lib/fig/string_tokenizer.rb +63 -25
- data/lib/fig/tokenized_string.rb +31 -5
- data/lib/fig/tokenized_string/plain_segment.rb +32 -2
- data/lib/fig/tokenized_string/token.rb +12 -0
- data/lib/fig/unparser.rb +27 -12
- data/lib/fig/unparser/v0.rb +4 -5
- data/lib/fig/unparser/v1.rb +43 -6
- data/lib/fig/url.rb +13 -0
- metadata +44 -42
data/lib/fig/string_tokenizer.rb
CHANGED
@@ -1,11 +1,33 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
1
3
|
require 'fig/tokenized_string'
|
2
4
|
require 'fig/tokenized_string/plain_segment'
|
3
5
|
|
4
6
|
module Fig; end
|
5
7
|
|
6
8
|
class Fig::StringTokenizer
|
7
|
-
|
9
|
+
# subexpression_matchers is an array of hashes. Each hash is expected to
|
10
|
+
# contain two keys: :pattern and :action.
|
11
|
+
#
|
12
|
+
# The :pattern value needs to be a regular expression for the substring that
|
13
|
+
# needs special handling.
|
14
|
+
#
|
15
|
+
# The :action value needs to be a block that takes two parameters.
|
16
|
+
#
|
17
|
+
# The first parameter is the text that was matched and the second is the
|
18
|
+
# error block passed to #tokenize().
|
19
|
+
#
|
20
|
+
# On success the block returns either a String containing replacement text or
|
21
|
+
# a Fig::TokenizedString::Token representing the special handling of the
|
22
|
+
# consumed text. If there was a problem, then the error block should have
|
23
|
+
# been invoked and the block should return nil.
|
24
|
+
#
|
25
|
+
#
|
26
|
+
# metacharacters is a regular expression character class for characters that
|
27
|
+
# need to be escaped when un-single quoting a string.
|
28
|
+
def initialize(subexpression_matchers = [], metacharacters = '')
|
8
29
|
@subexpression_matchers = subexpression_matchers
|
30
|
+
@metacharacters = metacharacters
|
9
31
|
|
10
32
|
return
|
11
33
|
end
|
@@ -28,15 +50,11 @@ class Fig::StringTokenizer
|
|
28
50
|
|
29
51
|
return if @segments.empty?
|
30
52
|
|
31
|
-
return Fig::TokenizedString.new(@segments, @single_quoted)
|
53
|
+
return Fig::TokenizedString.new(@segments, @single_quoted, @metacharacters)
|
32
54
|
end
|
33
55
|
|
34
56
|
private
|
35
57
|
|
36
|
-
DEFAULT_SUBEXPRESSION_MATCHER = [
|
37
|
-
{ :pattern => %r<\@>, :action => lambda {|character| character} }
|
38
|
-
]
|
39
|
-
|
40
58
|
def strip_quotes_and_process_escapes()
|
41
59
|
if @string.length == 0
|
42
60
|
@single_quoted = false
|
@@ -60,13 +78,15 @@ class Fig::StringTokenizer
|
|
60
78
|
|
61
79
|
def strip_single_quotes_and_process_escapes()
|
62
80
|
return false if @string[0..0] != %q<'> && @string[-1..-1] != %q<'>
|
63
|
-
return false if @string =~ %r<
|
81
|
+
return false if @string =~ %r< # «\'» is legal
|
82
|
+
\A ( [^\\']* (?: \\{2} )* \\ ' )* \z
|
83
|
+
>x
|
64
84
|
|
65
85
|
if (
|
66
|
-
@string.length == 1
|
67
|
-
@string[0..0] != %q<'>
|
68
|
-
@string[-1..-1] != %q<'>
|
69
|
-
@string =~ %r< [^\\] (?: \\{2} )* \\ ' \z >x
|
86
|
+
@string.length == 1 ||
|
87
|
+
@string[0..0] != %q<'> ||
|
88
|
+
@string[-1..-1] != %q<'> ||
|
89
|
+
@string =~ %r< [^\\] (?: \\{2} )* (?: \\ | ' .* ) ' \z >x
|
70
90
|
)
|
71
91
|
@error_block.call 'has unbalanced single quotes.'
|
72
92
|
return
|
@@ -85,7 +105,8 @@ class Fig::StringTokenizer
|
|
85
105
|
end
|
86
106
|
|
87
107
|
def strip_double_quotes_and_process_escapes()
|
88
|
-
|
108
|
+
was_quoted = check_and_strip_double_quotes
|
109
|
+
return if was_quoted.nil?
|
89
110
|
|
90
111
|
if @string == %q<\\'>
|
91
112
|
@segments << Fig::TokenizedString::PlainSegment.new(%q<'>)
|
@@ -93,7 +114,7 @@ class Fig::StringTokenizer
|
|
93
114
|
return
|
94
115
|
end
|
95
116
|
|
96
|
-
generate_segments
|
117
|
+
generate_segments was_quoted
|
97
118
|
|
98
119
|
return
|
99
120
|
end
|
@@ -101,7 +122,7 @@ class Fig::StringTokenizer
|
|
101
122
|
def check_and_strip_double_quotes()
|
102
123
|
# We accept any unquoted single character at this point. Later validation
|
103
124
|
# will catch bad characters.
|
104
|
-
return
|
125
|
+
return false if @string =~ %r< \A \\ . \z >xm
|
105
126
|
|
106
127
|
if @string[0..0] == %q<">
|
107
128
|
if @string.length == 1 || @string[-1..-1] != %q<">
|
@@ -115,16 +136,18 @@ class Fig::StringTokenizer
|
|
115
136
|
end
|
116
137
|
|
117
138
|
@string.sub!( %r< \A " (.*) " \z >xm, '\1' )
|
139
|
+
|
140
|
+
return true
|
118
141
|
elsif @string =~ %r< (?: \A | [^\\] ) (?: \\{2} )* " \z >xm
|
119
142
|
@error_block.call \
|
120
143
|
%q<has unbalanced double quotes; it ends in a double quote when it didn't start with one.>
|
121
144
|
return
|
122
145
|
end
|
123
146
|
|
124
|
-
return
|
147
|
+
return false
|
125
148
|
end
|
126
149
|
|
127
|
-
def generate_segments()
|
150
|
+
def generate_segments(was_quoted)
|
128
151
|
plain_string = nil
|
129
152
|
|
130
153
|
while ! @string.empty?
|
@@ -135,13 +158,20 @@ class Fig::StringTokenizer
|
|
135
158
|
@error_block.call 'ends in an incomplete escape.'
|
136
159
|
return
|
137
160
|
end
|
138
|
-
|
161
|
+
subexpression_matched = subexpression_match(remainder)
|
162
|
+
return if subexpression_matched.nil?
|
163
|
+
if (
|
164
|
+
subexpression_matched ||
|
165
|
+
remainder[0..0] == %q<"> ||
|
166
|
+
! was_quoted && remainder[0..0] == %q<'>
|
167
|
+
)
|
139
168
|
plain_string ||= ''
|
140
169
|
plain_string << slashes
|
141
170
|
plain_string << remainder[0..0]
|
142
171
|
@string = remainder[1..-1] || ''
|
143
172
|
else
|
144
|
-
@error_block.call
|
173
|
+
@error_block.call \
|
174
|
+
"contains a bad escape sequence (\\#{remainder[0..0]})."
|
145
175
|
return
|
146
176
|
end
|
147
177
|
else
|
@@ -151,17 +181,23 @@ class Fig::StringTokenizer
|
|
151
181
|
end
|
152
182
|
else
|
153
183
|
replacement, remainder = subexpression_match @string
|
184
|
+
return if replacement.nil?
|
154
185
|
if replacement
|
155
186
|
if replacement.is_a? String
|
156
187
|
plain_string << replacement
|
157
188
|
else
|
158
|
-
|
159
|
-
|
189
|
+
if ! plain_string.nil?
|
190
|
+
@segments << Fig::TokenizedString::PlainSegment.new(plain_string)
|
191
|
+
plain_string = nil
|
192
|
+
end
|
193
|
+
@segments << replacement
|
160
194
|
end
|
161
195
|
@string = remainder
|
162
|
-
elsif @string =~ %r< \A
|
163
|
-
|
164
|
-
|
196
|
+
elsif @string =~ %r< \A " >xm
|
197
|
+
@error_block.call 'contains an unescaped double quote.'
|
198
|
+
return
|
199
|
+
elsif ! was_quoted && @string =~ %r< \A ' >xm
|
200
|
+
@error_block.call 'contains an unescaped single quote.'
|
165
201
|
return
|
166
202
|
else
|
167
203
|
plain_string ||= ''
|
@@ -185,11 +221,13 @@ class Fig::StringTokenizer
|
|
185
221
|
pattern = matcher[:pattern]
|
186
222
|
if sub_string =~ %r< \A ( #{pattern} ) >x
|
187
223
|
subexpression, remainder = $1, $'
|
188
|
-
replacement = matcher[:action].call subexpression
|
224
|
+
replacement = matcher[:action].call subexpression, @error_block
|
225
|
+
|
226
|
+
return if ! replacement
|
189
227
|
return [replacement, remainder]
|
190
228
|
end
|
191
229
|
end
|
192
230
|
|
193
|
-
return
|
231
|
+
return false
|
194
232
|
end
|
195
233
|
end
|
data/lib/fig/tokenized_string.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
module Fig; end
|
2
2
|
|
3
3
|
class Fig::TokenizedString
|
4
|
-
def initialize(segments, single_quoted)
|
5
|
-
@segments
|
6
|
-
@single_quoted
|
4
|
+
def initialize(segments, single_quoted, metacharacters)
|
5
|
+
@segments = segments
|
6
|
+
@single_quoted = single_quoted
|
7
|
+
@metacharacters = metacharacters
|
7
8
|
|
8
9
|
return
|
9
10
|
end
|
@@ -12,11 +13,36 @@ class Fig::TokenizedString
|
|
12
13
|
return @single_quoted
|
13
14
|
end
|
14
15
|
|
15
|
-
def
|
16
|
-
return
|
16
|
+
def can_be_single_quoted?()
|
17
|
+
return true if single_quoted?
|
18
|
+
return @segments.all? {|segment| segment.type.nil?}
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_expanded_string(&block)
|
22
|
+
return (
|
23
|
+
@segments.collect { |segment| segment.to_expanded_string(&block) }
|
24
|
+
).join ''
|
17
25
|
end
|
18
26
|
|
19
27
|
def to_escaped_string()
|
20
28
|
return ( @segments.collect {|segment| segment.to_escaped_string} ).join ''
|
21
29
|
end
|
30
|
+
|
31
|
+
def to_single_quoted_string()
|
32
|
+
return to_escaped_string if single_quoted?
|
33
|
+
|
34
|
+
return (
|
35
|
+
@segments.collect {|segment| segment.to_single_quoted_string}
|
36
|
+
).join ''
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_double_quotable_string()
|
40
|
+
return to_escaped_string if ! single_quoted?
|
41
|
+
|
42
|
+
return (
|
43
|
+
@segments.collect {
|
44
|
+
|segment| segment.to_double_quotable_string @metacharacters
|
45
|
+
}
|
46
|
+
).join ''
|
47
|
+
end
|
22
48
|
end
|
@@ -11,14 +11,44 @@ class Fig::TokenizedString::PlainSegment
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def type
|
14
|
-
return
|
14
|
+
return nil
|
15
15
|
end
|
16
16
|
|
17
|
-
def to_expanded_string()
|
17
|
+
def to_expanded_string(&block)
|
18
18
|
return @raw_value.gsub(%r< \\ (.) >xm, '\1')
|
19
19
|
end
|
20
20
|
|
21
21
|
def to_escaped_string()
|
22
22
|
return @raw_value
|
23
23
|
end
|
24
|
+
|
25
|
+
# Should not be invoked if original string was single quoted.
|
26
|
+
def to_single_quoted_string()
|
27
|
+
# Raw value will have come from a non-single quoted string, so we unescape
|
28
|
+
# everything (including backslashes) and then escape backslashes and single
|
29
|
+
# quotes (which cannot be escaped outside of single quotes).
|
30
|
+
return \
|
31
|
+
@raw_value.gsub(%r< \\ (.) >xm, '\1').gsub(%r< ([\\']) >xm, '\\\\\1')
|
32
|
+
end
|
33
|
+
|
34
|
+
# Should not be invoked if original string was not single quoted.
|
35
|
+
def to_double_quotable_string(metacharacters)
|
36
|
+
quoted_value = @raw_value.gsub %r< ( ["#{metacharacters}] ) >xm, '\\\\\1'
|
37
|
+
|
38
|
+
quoted_value.gsub!(
|
39
|
+
%r<
|
40
|
+
(
|
41
|
+
(?: ^ | [^\\] ) # New line or non-backslash
|
42
|
+
(\\{2})* # Even number of backslashes
|
43
|
+
)
|
44
|
+
|
45
|
+
# All single quotes must have been escaped. The important bit is to
|
46
|
+
# not lose escaped backslashes.
|
47
|
+
\\'
|
48
|
+
>xm,
|
49
|
+
%q<\1'>
|
50
|
+
)
|
51
|
+
|
52
|
+
return quoted_value
|
53
|
+
end
|
24
54
|
end
|
@@ -12,7 +12,19 @@ class Fig::TokenizedString::Token
|
|
12
12
|
return
|
13
13
|
end
|
14
14
|
|
15
|
+
def to_expanded_string(&block)
|
16
|
+
return block.call self
|
17
|
+
end
|
18
|
+
|
15
19
|
def to_escaped_string()
|
16
20
|
return raw_value
|
17
21
|
end
|
22
|
+
|
23
|
+
def to_double_quotable_string(metacharacters)
|
24
|
+
return raw_value
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_single_quoted_string()
|
28
|
+
raise NotImplementedError.new 'Cannot single-quote a token.'
|
29
|
+
end
|
18
30
|
end
|
data/lib/fig/unparser.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
# Note: we very specifically do not require the files containing the
|
2
|
+
# Unparser classes in order to avoid circular dependencies.
|
3
|
+
|
1
4
|
module Fig; end
|
2
5
|
|
3
6
|
module Fig::Unparser
|
@@ -7,8 +10,6 @@ module Fig::Unparser
|
|
7
10
|
def self.class_for_statements(
|
8
11
|
statements, emit_as_input_or_to_be_published_values
|
9
12
|
)
|
10
|
-
# Note: we very specifically do not require the files containing the
|
11
|
-
# Unparser classes in order to avoid circular dependencies.
|
12
13
|
statements = [statements].flatten
|
13
14
|
|
14
15
|
versions =
|
@@ -21,12 +22,6 @@ module Fig::Unparser
|
|
21
22
|
return Fig::Unparser::V0, explanations
|
22
23
|
end
|
23
24
|
|
24
|
-
# TODO: Until v1 grammar handling is done, ensure we don't emit anything
|
25
|
-
# old fig versions cannot handle.
|
26
|
-
if ! ENV['FIG_ALLOW_NON_V0_GRAMMAR']
|
27
|
-
raise 'Reached a point where something could not be represented by the v0 grammar. Bailing out.'
|
28
|
-
end
|
29
|
-
|
30
25
|
return Fig::Unparser::V1, explanations
|
31
26
|
end
|
32
27
|
|
@@ -44,8 +39,11 @@ module Fig::Unparser
|
|
44
39
|
private
|
45
40
|
|
46
41
|
def self.gather_versions(statements, emit_as_input_or_to_be_published_values)
|
42
|
+
all_statements = gather_all_statements statements
|
43
|
+
|
47
44
|
if emit_as_input_or_to_be_published_values == :emit_as_input
|
48
|
-
|
45
|
+
versions = []
|
46
|
+
return all_statements.map {
|
49
47
|
|statement|
|
50
48
|
|
51
49
|
self.expand_version_and_explanation(
|
@@ -54,7 +52,7 @@ module Fig::Unparser
|
|
54
52
|
}
|
55
53
|
end
|
56
54
|
|
57
|
-
return
|
55
|
+
return all_statements.map {
|
58
56
|
|statement|
|
59
57
|
|
60
58
|
self.expand_version_and_explanation(
|
@@ -63,6 +61,21 @@ module Fig::Unparser
|
|
63
61
|
}
|
64
62
|
end
|
65
63
|
|
64
|
+
def self.gather_all_statements(statements)
|
65
|
+
all_statements = []
|
66
|
+
statements.each do
|
67
|
+
|statement|
|
68
|
+
|
69
|
+
all_statements << statement
|
70
|
+
|
71
|
+
if statement.is_a? Fig::Statement::Configuration
|
72
|
+
all_statements << statement.statements
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
return all_statements.flatten
|
77
|
+
end
|
78
|
+
|
66
79
|
def self.expand_version_and_explanation(statement, version_info)
|
67
80
|
version, explanation = *version_info
|
68
81
|
if explanation.nil?
|
@@ -159,6 +172,8 @@ module Fig::Unparser
|
|
159
172
|
end
|
160
173
|
|
161
174
|
def path(statement)
|
175
|
+
# I'd really like to change this to "add", but because the command-line
|
176
|
+
# option is "--append", I'm not going to.
|
162
177
|
environment_variable(statement, 'append')
|
163
178
|
|
164
179
|
return
|
@@ -202,8 +217,8 @@ module Fig::Unparser
|
|
202
217
|
raise NotImplementedError
|
203
218
|
end
|
204
219
|
|
205
|
-
def add_indent()
|
206
|
-
@text << @indent_string *
|
220
|
+
def add_indent(indent_level = @indent_level)
|
221
|
+
@text << @indent_string * indent_level
|
207
222
|
|
208
223
|
return
|
209
224
|
end
|
data/lib/fig/unparser/v0.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'fig/package_descriptor'
|
2
1
|
require 'fig/unparser'
|
3
2
|
|
4
3
|
module Fig; end
|
@@ -25,7 +24,7 @@ class Fig::Unparser::V0
|
|
25
24
|
add_indent
|
26
25
|
|
27
26
|
@text << %q<command ">
|
28
|
-
@text << statement.command
|
27
|
+
@text << statement.command.first.to_double_quotable_string
|
29
28
|
@text << %Q<"\n>
|
30
29
|
|
31
30
|
return
|
@@ -44,9 +43,9 @@ class Fig::Unparser::V0
|
|
44
43
|
add_indent
|
45
44
|
|
46
45
|
@text << 'retrieve '
|
47
|
-
@text << statement.
|
46
|
+
@text << statement.variable
|
48
47
|
@text << '->'
|
49
|
-
@text << statement.
|
48
|
+
@text << statement.tokenized_path.to_double_quotable_string
|
50
49
|
@text << "\n"
|
51
50
|
|
52
51
|
return
|
@@ -80,7 +79,7 @@ class Fig::Unparser::V0
|
|
80
79
|
@text << ' '
|
81
80
|
@text << statement.name
|
82
81
|
@text << '='
|
83
|
-
@text << statement.
|
82
|
+
@text << statement.tokenized_value.to_double_quotable_string
|
84
83
|
@text << "\n"
|
85
84
|
|
86
85
|
return
|
data/lib/fig/unparser/v1.rb
CHANGED
@@ -22,10 +22,19 @@ class Fig::Unparser::V1
|
|
22
22
|
|
23
23
|
def command(statement)
|
24
24
|
add_indent
|
25
|
+
@text << %Q<command\n>
|
25
26
|
|
26
|
-
@
|
27
|
-
|
28
|
-
|
27
|
+
add_indent(@indent_level + 1)
|
28
|
+
statement.command.each do
|
29
|
+
|argument|
|
30
|
+
|
31
|
+
emit_tokenized_value argument
|
32
|
+
@text << ' '
|
33
|
+
end
|
34
|
+
|
35
|
+
@text << %Q<\n>
|
36
|
+
add_indent
|
37
|
+
@text << %Q<end\n>
|
29
38
|
|
30
39
|
return
|
31
40
|
end
|
@@ -38,6 +47,20 @@ class Fig::Unparser::V1
|
|
38
47
|
return
|
39
48
|
end
|
40
49
|
|
50
|
+
def retrieve(statement)
|
51
|
+
add_indent
|
52
|
+
|
53
|
+
@text << 'retrieve '
|
54
|
+
@text << statement.variable
|
55
|
+
@text << '->'
|
56
|
+
|
57
|
+
emit_tokenized_value statement.tokenized_path
|
58
|
+
|
59
|
+
@text << "\n"
|
60
|
+
|
61
|
+
return
|
62
|
+
end
|
63
|
+
|
41
64
|
def grammar_description()
|
42
65
|
return 'v1'
|
43
66
|
end
|
@@ -61,17 +84,31 @@ class Fig::Unparser::V1
|
|
61
84
|
end
|
62
85
|
|
63
86
|
def environment_variable(statement, keyword)
|
64
|
-
# TODO: temporarily hack v0 grammar in here so we can test asset
|
65
|
-
# statements; proper implementation once asset statements are done.
|
66
87
|
add_indent
|
67
88
|
|
68
89
|
@text << keyword
|
69
90
|
@text << ' '
|
70
91
|
@text << statement.name
|
71
92
|
@text << '='
|
72
|
-
|
93
|
+
|
94
|
+
emit_tokenized_value statement.tokenized_value
|
95
|
+
|
73
96
|
@text << "\n"
|
74
97
|
|
75
98
|
return
|
76
99
|
end
|
100
|
+
|
101
|
+
def emit_tokenized_value(tokenized_value)
|
102
|
+
if tokenized_value.can_be_single_quoted?
|
103
|
+
@text << %q<'>
|
104
|
+
@text << tokenized_value.to_single_quoted_string
|
105
|
+
@text << %q<'>
|
106
|
+
else
|
107
|
+
@text << %q<">
|
108
|
+
@text << tokenized_value.to_escaped_string
|
109
|
+
@text << %q<">
|
110
|
+
end
|
111
|
+
|
112
|
+
return
|
113
|
+
end
|
77
114
|
end
|