fig 0.1.81 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/Changes +87 -0
  2. data/lib/fig.rb +1 -1
  3. data/lib/fig/command.rb +5 -0
  4. data/lib/fig/command/action/dump_package_definition_for_command_line.rb +62 -0
  5. data/lib/fig/command/action/dump_package_definition_parsed.rb +19 -2
  6. data/lib/fig/command/action/list_local.rb +9 -1
  7. data/lib/fig/command/action/list_remote.rb +9 -1
  8. data/lib/fig/command/action/role/list_variables_in_a_tree.rb +1 -1
  9. data/lib/fig/command/action/run_command_line.rb +1 -1
  10. data/lib/fig/command/action/run_command_statement.rb +4 -2
  11. data/lib/fig/command/options.rb +50 -18
  12. data/lib/fig/command/options/parser.rb +16 -15
  13. data/lib/fig/command/package_applier.rb +5 -3
  14. data/lib/fig/grammar/v0.rb +287 -289
  15. data/lib/fig/grammar/v0.treetop +66 -42
  16. data/lib/fig/grammar/v1.rb +629 -533
  17. data/lib/fig/grammar/v1.treetop +102 -39
  18. data/lib/fig/grammar_monkey_patches.rb +21 -0
  19. data/lib/fig/operating_system.rb +53 -36
  20. data/lib/fig/package_descriptor.rb +1 -12
  21. data/lib/fig/parser.rb +8 -33
  22. data/lib/fig/parser_package_build_state.rb +92 -31
  23. data/lib/fig/repository_package_publisher.rb +2 -2
  24. data/lib/fig/runtime_environment.rb +54 -120
  25. data/lib/fig/statement.rb +6 -6
  26. data/lib/fig/statement/asset.rb +1 -13
  27. data/lib/fig/statement/command.rb +47 -0
  28. data/lib/fig/statement/environment_variable.rb +64 -3
  29. data/lib/fig/statement/grammar_version.rb +4 -0
  30. data/lib/fig/statement/include.rb +4 -0
  31. data/lib/fig/statement/override.rb +4 -0
  32. data/lib/fig/statement/path.rb +40 -16
  33. data/lib/fig/statement/retrieve.rb +61 -5
  34. data/lib/fig/statement/set.rb +16 -19
  35. data/lib/fig/string_tokenizer.rb +63 -25
  36. data/lib/fig/tokenized_string.rb +31 -5
  37. data/lib/fig/tokenized_string/plain_segment.rb +32 -2
  38. data/lib/fig/tokenized_string/token.rb +12 -0
  39. data/lib/fig/unparser.rb +27 -12
  40. data/lib/fig/unparser/v0.rb +4 -5
  41. data/lib/fig/unparser/v1.rb +43 -6
  42. data/lib/fig/url.rb +13 -0
  43. metadata +44 -42
@@ -21,7 +21,11 @@ module Fig
21
21
  include Fig::Grammar::Version
22
22
 
23
23
  rule package
24
- optional_ws_or_comment grammar_version:grammar_version? statements:(package_statement_with_ws*) optional_ws_or_comment {
24
+ optional_ws_or_comment
25
+ grammar_version:grammar_version?
26
+ statements:(package_statement_with_ws*)
27
+ optional_ws_or_comment
28
+ {
25
29
  def to_package(directory, build_state)
26
30
  return build_state.new_package_statement(
27
31
  directory, grammar_version, statements
@@ -45,7 +49,10 @@ module Fig
45
49
  end
46
50
 
47
51
  rule archive
48
- statement_start:'archive' ws_or_comment+ location:asset_location {
52
+ statement_start:'archive'
53
+ ws_or_comment+
54
+ location:quoted_or_bare_string
55
+ {
49
56
  def to_package_statement(build_state)
50
57
  return build_state.new_asset_statement(
51
58
  Statement::Archive, statement_start, location
@@ -55,7 +62,10 @@ module Fig
55
62
  end
56
63
 
57
64
  rule resource
58
- statement_start:'resource' ws_or_comment+ location:asset_location {
65
+ statement_start:'resource'
66
+ ws_or_comment+
67
+ location:quoted_or_bare_string
68
+ {
59
69
  def to_package_statement(build_state)
60
70
  return build_state.new_asset_statement(
61
71
  Statement::Resource, statement_start, location
@@ -64,28 +74,27 @@ module Fig
64
74
  }
65
75
  end
66
76
 
67
- rule asset_location
68
- '"' ( [^"\\] / '\\' . )* '"' /
69
- "'" ( [^'\\] / '\\' . )* "'" /
70
- [^\s#]+
71
- end
72
-
73
77
  rule retrieve
74
- statement_start:'retrieve' ws_or_comment+ var:environment_variable_name '->' path:retrieve_path {
78
+ statement_start:'retrieve'
79
+ ws_or_comment+
80
+ variable:environment_variable_name '->' path:quoted_or_bare_string
81
+ {
75
82
  def to_package_statement(build_state)
76
- return build_state.new_retrieve_statement(statement_start, var, path)
83
+ return build_state.new_retrieve_statement(
84
+ statement_start, variable, path
85
+ )
77
86
  end
78
87
  }
79
88
  end
80
89
 
81
- rule retrieve_path
82
- # TODO: this should match asset location rules, but handling of
83
- # "[package]" substitution is going to be interesting.
84
- [a-zA-Z0-9_/.\[\]-]+
85
- end
86
-
87
90
  rule config
88
- statement_start:'config' ws_or_comment+ config_name ws_or_comment+ statements:config_statement_with_ws* 'end' {
91
+ statement_start:'config'
92
+ ws_or_comment+
93
+ config_name
94
+ ws_or_comment+
95
+ statements:config_statement_with_ws*
96
+ 'end'
97
+ {
89
98
  def to_package_statement(build_state)
90
99
  return build_state.new_configuration_statement(
91
100
  statement_start, config_name, statements
@@ -94,10 +103,6 @@ module Fig
94
103
  }
95
104
  end
96
105
 
97
- rule config_name
98
- [a-zA-Z0-9_.-]+
99
- end
100
-
101
106
  # Shim between "config" and "config_statement" rules to allow the
102
107
  # compiled v0 and v1 grammars to have the same interface.
103
108
  rule config_statement_with_ws
@@ -132,10 +137,6 @@ module Fig
132
137
  }
133
138
  end
134
139
 
135
- rule environment_variable_name
136
- [a-zA-Z0-9_]+
137
- end
138
-
139
140
  rule set
140
141
  statement_start:'set' ws_or_comment+ environment_variable_name_value {
141
142
  def to_config_statement(build_state)
@@ -147,7 +148,10 @@ module Fig
147
148
  end
148
149
 
149
150
  rule path
150
- statement_start:('add' / 'append' / 'path') ws_or_comment+ environment_variable_name_value {
151
+ statement_start:('add' / 'append' / 'path')
152
+ ws_or_comment+
153
+ environment_variable_name_value
154
+ {
151
155
  def to_config_statement(build_state)
152
156
  return build_state.new_environment_variable_statement(
153
157
  Statement::Path, statement_start, environment_variable_name_value
@@ -156,31 +160,90 @@ module Fig
156
160
  }
157
161
  end
158
162
 
159
- rule environment_variable_name_value
160
- # Should result in somewhat reasonable handling by Treetop when
161
- # encountering mis-quoted constructs.
162
- [^\s#\\"]* '"' ( [^"\\] / '\\' . )* '"' /
163
- [^\s#\\']* "'" ( [^'\\] / '\\' . )* "'" /
164
- [^\s#]+
165
- end
166
-
167
163
  rule command
168
- statement_start:'command' ws_or_comment+ command_line {
164
+ statement_start:'command'
165
+ ws_or_comment+
166
+ command_line
167
+ ws_or_comment+
168
+ 'end'
169
+ {
169
170
  def to_config_statement(build_state)
170
- return build_state.new_command_statement(
171
- statement_start, command_line
171
+ return build_state.new_v1_command_statement(
172
+ statement_start, gather_command_argument_nodes(command_line)
172
173
  )
173
174
  end
175
+
176
+ def gather_command_argument_nodes(node, arguments = [])
177
+ if node.respond_to? 'quoted_or_bare_string?'
178
+ arguments << node
179
+ return arguments
180
+ end
181
+
182
+ return arguments if not node.elements
183
+
184
+ node.elements.each do
185
+ |element|
186
+ gather_command_argument_nodes(element, arguments)
187
+ end
188
+
189
+ return arguments
190
+ end
174
191
  }
175
192
  end
176
193
 
177
194
  rule command_line
178
- '"' value:[^"]* '"'
195
+ quoted_or_bare_string
196
+ ! { |sequence| sequence[-1].text_value == 'end' }
197
+ (
198
+ ws_or_comment+
199
+ quoted_or_bare_string
200
+ ! { |sequence| sequence[-1].text_value == 'end' }
201
+ )*
179
202
  end
180
203
 
204
+ # Terminals
205
+
181
206
  rule descriptor_string
182
207
  [^\s#]+
183
208
  end
209
+
210
+ rule config_name
211
+ [a-zA-Z0-9_.-]+
212
+ end
213
+
214
+ rule quoted_or_bare_string
215
+ # In order to deal with the hierarchy of nodes that the command_line
216
+ # rule above generates, we tag each of the expressions here so that
217
+ # they can be found in the syntax tree.
218
+ [^\s#\\"]* '"' ( [^"\\] / '\\' . )* '"'
219
+ { def quoted_or_bare_string?() return true end }
220
+
221
+ /
222
+
223
+ [^\s#\\']* "'" ( [^'\\] / '\\' . )* "'"
224
+ { def quoted_or_bare_string?() return true end }
225
+
226
+ /
227
+
228
+ [^\s#]+
229
+ { def quoted_or_bare_string?() return true end }
230
+ end
231
+
232
+ rule environment_variable_name
233
+ [a-zA-Z0-9_]+
234
+ end
235
+
236
+ rule environment_variable_name_value
237
+ # This is like quoted_or_bare_string, but allows for the unquoted
238
+ # variable name followed by equals sign prior to the quotation marks
239
+ # coming in.
240
+ #
241
+ # Should result in somewhat reasonable handling by Treetop when
242
+ # encountering mis-quoted constructs.
243
+ [^\s#\\'"]* '"' ( [^"\\] / '\\' . )* '"' /
244
+ [^\s#\\'"]* "'" ( [^'\\] / '\\' . )* "'" /
245
+ [^\s#]+
246
+ end
184
247
  end
185
248
  end
186
249
  end
@@ -0,0 +1,21 @@
1
+ # The existence of this &!#%)(*!&% file makes me want to hurl, but it's the
2
+ # simplest way to modify the classes generated by Treetop: the Treetop grammar
3
+ # doesn't allow extensions at the grammar level, only the rule level.
4
+
5
+ require 'fig/grammar/v0'
6
+ require 'fig/grammar/v1'
7
+
8
+ module Fig; end
9
+ module Fig::Grammar; end
10
+
11
+ class Fig::Grammar::V0Parser
12
+ def version()
13
+ return 0
14
+ end
15
+ end
16
+
17
+ class Fig::Grammar::V1Parser
18
+ def version()
19
+ return 1
20
+ end
21
+ end
@@ -10,7 +10,6 @@ require 'net/sftp'
10
10
  require 'net/netrc'
11
11
  require 'rbconfig'
12
12
  require 'tempfile'
13
- require 'uri'
14
13
 
15
14
  require 'highline/import'
16
15
 
@@ -20,6 +19,8 @@ require 'fig/environment_variables/case_sensitive'
20
19
  require 'fig/file_not_found_error'
21
20
  require 'fig/logging'
22
21
  require 'fig/network_error'
22
+ require 'fig/url'
23
+ require 'fig/user_input_error'
23
24
 
24
25
  module Fig; end
25
26
 
@@ -119,40 +120,49 @@ class Fig::OperatingSystem
119
120
 
120
121
  def download_list(url)
121
122
  begin
122
- uri = URI.parse(url)
123
+ uri = Fig::URL.parse(url)
123
124
  rescue
124
125
  Fig::Logging.fatal %Q<Unable to parse url: "#{url}">
125
126
  raise Fig::NetworkError.new
126
127
  end
127
- case uri.scheme
128
- when 'ftp'
129
- ftp = Net::FTP.new(uri.host)
130
- ftp_login(ftp, uri.host)
131
- ftp.chdir(uri.path)
132
- dirs = ftp.nlst
133
- ftp.close
134
128
 
135
- download_ftp_list(uri, dirs)
136
- when 'ssh'
137
- packages = []
138
- Net::SSH.start(uri.host, uri.user) do |ssh|
139
- ls = ssh.exec!("[ -d #{uri.path} ] && find #{uri.path}")
140
- strip_paths_for_list(ls, packages, uri.path)
141
- end
142
- packages
143
- when 'file'
144
- packages = []
145
- unescaped_path = CGI.unescape uri.path
146
- return packages if ! File.exist?(unescaped_path)
129
+ begin
130
+ case uri.scheme
131
+ when 'ftp'
132
+ ftp = Net::FTP.new(uri.host)
133
+ ftp_login(ftp, uri.host)
134
+ ftp.chdir(uri.path)
135
+ dirs = ftp.nlst
136
+ ftp.close
147
137
 
148
- ls = ''
149
- Find.find(unescaped_path) { |file| ls << file.to_s; ls << "\n" }
138
+ download_ftp_list(uri, dirs)
139
+ when 'ssh'
140
+ packages = []
141
+ Net::SSH.start(uri.host, uri.user) do |ssh|
142
+ ls = ssh.exec!("[ -d #{uri.path} ] && find #{uri.path}")
143
+ strip_paths_for_list(ls, packages, uri.path)
144
+ end
145
+ packages
146
+ when 'file'
147
+ packages = []
148
+ unescaped_path = CGI.unescape uri.path
149
+ return packages if ! File.exist?(unescaped_path)
150
150
 
151
- strip_paths_for_list(ls, packages, unescaped_path)
152
- return packages
153
- else
154
- Fig::Logging.fatal "Protocol not supported: #{url}"
155
- raise Fig::NetworkError.new("Protocol not supported: #{url}")
151
+ ls = ''
152
+ Find.find(unescaped_path) { |file| ls << file.to_s; ls << "\n" }
153
+
154
+ strip_paths_for_list(ls, packages, unescaped_path)
155
+ return packages
156
+ else
157
+ Fig::Logging.fatal "Protocol not supported: #{url}"
158
+ raise Fig::NetworkError.new "Protocol not supported: #{url}"
159
+ end
160
+ rescue SocketError => error
161
+ Fig::Logging.debug error.message
162
+ raise Fig::NetworkError.new "#{url}: #{error.message}"
163
+ rescue Errno::ETIMEDOUT => error
164
+ Fig::Logging.debug error.message
165
+ raise Fig::NetworkError.new "#{url}: #{error.message}"
156
166
  end
157
167
  end
158
168
 
@@ -200,7 +210,7 @@ class Fig::OperatingSystem
200
210
  def path_up_to_date?(url, path)
201
211
  return false if ! File.exist? path
202
212
 
203
- uri = URI.parse(url)
213
+ uri = Fig::URL.parse(url)
204
214
  case uri.scheme
205
215
  when 'ftp'
206
216
  begin
@@ -242,7 +252,7 @@ class Fig::OperatingSystem
242
252
  # exists and is already up-to-date.
243
253
  def download(url, path)
244
254
  FileUtils.mkdir_p(File.dirname(path))
245
- uri = URI.parse(url)
255
+ uri = Fig::URL.parse(url)
246
256
  case uri.scheme
247
257
  when 'ftp'
248
258
  begin
@@ -263,6 +273,9 @@ class Fig::OperatingSystem
263
273
  rescue SocketError => error
264
274
  Fig::Logging.debug error.message
265
275
  raise Fig::FileNotFoundError.new error.message, url
276
+ rescue Errno::ETIMEDOUT => error
277
+ Fig::Logging.debug error.message
278
+ raise Fig::FileNotFoundError.new error.message, url
266
279
  end
267
280
  when 'http'
268
281
  log_download(url, path)
@@ -303,7 +316,7 @@ class Fig::OperatingSystem
303
316
  def download_resource(url, download_directory)
304
317
  FileUtils.mkdir_p(download_directory)
305
318
 
306
- basename = CGI.unescape URI.parse(url).path.split('/').last
319
+ basename = CGI.unescape Fig::URL.parse(url).path.split('/').last
307
320
  path = File.join(download_directory, basename)
308
321
 
309
322
  download(url, path)
@@ -333,14 +346,14 @@ class Fig::OperatingSystem
333
346
 
334
347
  def upload(local_file, remote_file)
335
348
  Fig::Logging.debug "Uploading #{local_file} to #{remote_file}."
336
- uri = URI.parse(remote_file)
349
+ uri = Fig::URL.parse(remote_file)
337
350
  case uri.scheme
338
351
  when 'ssh'
339
352
  ssh_upload(uri.user, uri.host, local_file, remote_file)
340
353
  when 'ftp'
341
354
  # fail unless system "curl -T #{local_file} --create-dirs --ftp-create-dirs #{remote_file}"
342
355
  require 'net/ftp'
343
- ftp_uri = URI.parse(ENV['FIG_REMOTE_URL'])
356
+ ftp_uri = Fig::URL.parse(ENV['FIG_REMOTE_URL'])
344
357
  ftp_root_path = ftp_uri.path
345
358
  ftp_root_dirs = ftp_uri.path.split('/')
346
359
  remote_publish_path = uri.path[0, uri.path.rindex('/')]
@@ -465,7 +478,11 @@ class Fig::OperatingSystem
465
478
  SimpleCov.at_exit.call
466
479
  end
467
480
 
468
- Kernel.exec(*command)
481
+ begin
482
+ Kernel.exec(*command)
483
+ rescue SystemCallError => exception
484
+ raise Fig::UserInputError.new exception
485
+ end
469
486
  end
470
487
 
471
488
  # *sigh* Apparently Ruby < v1.9.3 does some wacko thing with single argument
@@ -520,7 +537,7 @@ class Fig::OperatingSystem
520
537
  end
521
538
 
522
539
  def ssh_upload(user, host, local_file, remote_file)
523
- uri = URI.parse(remote_file)
540
+ uri = Fig::URL.parse(remote_file)
524
541
  dir = uri.path[0, uri.path.rindex('/')]
525
542
  Net::SSH.start(host, user) do |ssh|
526
543
  ssh.exec!("mkdir -p #{dir}")
@@ -544,7 +561,7 @@ class Fig::OperatingSystem
544
561
  when Net::HTTPRedirection then
545
562
  location = response['location']
546
563
  Fig::Logging.debug "Redirecting to #{location}."
547
- download_via_http_get(location, file, limit - 1)
564
+ download_via_http_get(location, file, redirection_limit - 1)
548
565
  else
549
566
  Fig::Logging.debug "Download failed: #{response.code} #{response.message}."
550
567
  raise Fig::FileNotFoundError.new(
@@ -1,4 +1,5 @@
1
1
  require 'fig/package_descriptor_parse_error'
2
+ require 'fig/statement'
2
3
 
3
4
  module Fig; end
4
5
 
@@ -64,7 +65,6 @@ class Fig::PackageDescriptor
64
65
  validate_component name, 'name', :name, options
65
66
  validate_component version, 'version', :version, options
66
67
  validate_component config, 'config', :config, options
67
- validate_name options
68
68
  validate_across_components options
69
69
  end
70
70
 
@@ -113,17 +113,6 @@ class Fig::PackageDescriptor
113
113
  )
114
114
  end
115
115
 
116
- def validate_name(options)
117
- return if @name.nil?
118
-
119
- return if ! Fig::Parser.strict_keyword? @name
120
-
121
- raise Fig::PackageDescriptorParseError.new(
122
- %Q<Invalid package name; "#{@name}" is a keyword#{standard_exception_suffix(options)}>,
123
- @original_string
124
- )
125
- end
126
-
127
116
  def throw_presence_exception(name, presence_requirement_symbol, options)
128
117
  presence = options[presence_requirement_symbol]
129
118
  raise Fig::PackageDescriptorParseError.new(
data/lib/fig/parser.rb CHANGED
@@ -3,6 +3,7 @@ require 'set'
3
3
  require 'fig/grammar/version_identification'
4
4
  require 'fig/grammar/v0'
5
5
  require 'fig/grammar/v1'
6
+ require 'fig/grammar_monkey_patches'
6
7
  require 'fig/logging'
7
8
  require 'fig/package_parse_error'
8
9
  require 'fig/parser_package_build_state'
@@ -16,19 +17,6 @@ module Fig; end
16
17
  # Parses .fig files (wrapping the Treetop-generated parser object) and deals
17
18
  # with a few restrictions on them.
18
19
  class Fig::Parser
19
- # Keywords that we really want to lock down.
20
- def self.strict_keyword?(string)
21
- # "config" is considered too useful for users, so we allow that where we
22
- # restrict other keywords.
23
- return false if string == 'config'
24
-
25
- return keyword? string
26
- end
27
-
28
- def self.keyword?(string)
29
- return KEYWORDS.include? string
30
- end
31
-
32
20
  def initialize(application_config, check_include_versions)
33
21
  @application_config = application_config
34
22
  @check_include_versions = check_include_versions
@@ -48,21 +36,6 @@ class Fig::Parser
48
36
 
49
37
  private
50
38
 
51
- KEYWORDS = Set.new
52
- KEYWORDS << 'add'
53
- KEYWORDS << 'append'
54
- KEYWORDS << 'archive'
55
- KEYWORDS << 'command'
56
- KEYWORDS << 'config'
57
- KEYWORDS << 'end'
58
- KEYWORDS << 'include'
59
- KEYWORDS << 'override'
60
- KEYWORDS << 'path'
61
- KEYWORDS << 'resource'
62
- KEYWORDS << 'retrieve'
63
- KEYWORDS << 'set'
64
-
65
-
66
39
  def get_grammar_version(
67
40
  descriptor, directory, source_description, unparsed_text
68
41
  )
@@ -77,7 +50,7 @@ class Fig::Parser
77
50
  end
78
51
 
79
52
  statement = result.get_grammar_version(
80
- Fig::ParserPackageBuildState.new(descriptor, extended_description)
53
+ Fig::ParserPackageBuildState.new(nil, descriptor, extended_description)
81
54
  )
82
55
  return 0 if not statement
83
56
 
@@ -96,7 +69,7 @@ class Fig::Parser
96
69
 
97
70
  v0_parser = Fig::Grammar::V0Parser.new
98
71
 
99
- return drive_parser(
72
+ return drive_treetop_parser(
100
73
  v0_parser,
101
74
  descriptor,
102
75
  directory,
@@ -109,7 +82,7 @@ class Fig::Parser
109
82
  def parse_v1(descriptor, directory, source_description, unparsed_text)
110
83
  v1_parser = Fig::Grammar::V1Parser.new
111
84
 
112
- return drive_parser(
85
+ return drive_treetop_parser(
113
86
  v1_parser,
114
87
  descriptor,
115
88
  directory,
@@ -119,7 +92,7 @@ class Fig::Parser
119
92
  )
120
93
  end
121
94
 
122
- def drive_parser(
95
+ def drive_treetop_parser(
123
96
  parser,
124
97
  descriptor,
125
98
  directory,
@@ -140,7 +113,9 @@ class Fig::Parser
140
113
 
141
114
  package = result.to_package(
142
115
  directory,
143
- Fig::ParserPackageBuildState.new(descriptor, extended_description)
116
+ Fig::ParserPackageBuildState.new(
117
+ parser.version, descriptor, extended_description
118
+ )
144
119
  )
145
120
  package.unparsed_text = unparsed_text
146
121