fig 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/Changes +94 -0
  2. data/lib/fig.rb +1 -1
  3. data/lib/fig/command.rb +18 -7
  4. data/lib/fig/command/action/dump_package_definition_for_command_line.rb +2 -2
  5. data/lib/fig/command/action/dump_package_definition_parsed.rb +2 -2
  6. data/lib/fig/command/action/dump_package_definition_text.rb +2 -2
  7. data/lib/fig/command/action/source_package.rb +65 -0
  8. data/lib/fig/command/options.rb +64 -15
  9. data/lib/fig/command/options/parser.rb +24 -14
  10. data/lib/fig/command/package_applier.rb +32 -7
  11. data/lib/fig/command/package_loader.rb +16 -7
  12. data/lib/fig/external_program.rb +72 -0
  13. data/lib/fig/figrc.rb +1 -1
  14. data/lib/fig/grammar/v0.rb +2 -2
  15. data/lib/fig/grammar/v0.treetop +2 -2
  16. data/lib/fig/grammar/v1.rb +17 -1737
  17. data/lib/fig/grammar/v1.treetop +6 -217
  18. data/lib/fig/grammar/v1_base.rb +1750 -0
  19. data/lib/fig/grammar/v1_base.treetop +229 -0
  20. data/lib/fig/grammar/v2.rb +508 -0
  21. data/lib/fig/grammar/v2.treetop +65 -0
  22. data/lib/fig/grammar_monkey_patches.rb +7 -0
  23. data/lib/fig/no_such_package_config_error.rb +3 -1
  24. data/lib/fig/not_yet_parsed_package.rb +27 -0
  25. data/lib/fig/operating_system.rb +5 -5
  26. data/lib/fig/package.rb +20 -2
  27. data/lib/fig/package_definition_text_assembler.rb +2 -1
  28. data/lib/fig/package_descriptor.rb +11 -4
  29. data/lib/fig/parser.rb +44 -58
  30. data/lib/fig/parser_package_build_state.rb +39 -4
  31. data/lib/fig/protocol/file.rb +2 -2
  32. data/lib/fig/protocol/ftp.rb +15 -10
  33. data/lib/fig/protocol/http.rb +1 -1
  34. data/lib/fig/protocol/netrc_enabled.rb +29 -16
  35. data/lib/fig/protocol/sftp.rb +19 -12
  36. data/lib/fig/repository.rb +33 -21
  37. data/lib/fig/repository_package_publisher.rb +129 -8
  38. data/lib/fig/runtime_environment.rb +114 -28
  39. data/lib/fig/statement/include.rb +21 -4
  40. data/lib/fig/statement/include_file.rb +94 -0
  41. data/lib/fig/unparser.rb +15 -7
  42. data/lib/fig/unparser/v1.rb +2 -80
  43. data/lib/fig/unparser/v1_base.rb +85 -0
  44. data/lib/fig/unparser/v2.rb +55 -0
  45. data/lib/fig/working_directory_maintainer.rb +12 -0
  46. metadata +61 -51
@@ -0,0 +1,65 @@
1
+ # Treetop (http://treetop.rubyforge.org/) grammar for package definitions in v2
2
+ # format.
3
+
4
+ require 'treetop'
5
+
6
+ require 'fig/grammar/base'
7
+ require 'fig/grammar/v1_base'
8
+ require 'fig/grammar/version'
9
+
10
+ module Fig
11
+ module Grammar
12
+ grammar V2
13
+ include Fig::Grammar::Base
14
+ include Fig::Grammar::Version
15
+ include Fig::Grammar::V1Base
16
+
17
+ # It would nice to be able to put this into Fig::Grammar::V1Base, but it
18
+ # looks like the root has to be declared in the concrete grammar.
19
+ rule package
20
+ optional_ws_or_comment
21
+ grammar_version:grammar_version?
22
+ statements:(package_statement_with_ws*)
23
+ optional_ws_or_comment
24
+ {
25
+ def to_package(unparsed_package, build_state)
26
+ return build_state.new_package_statement(
27
+ unparsed_package, grammar_version, statements
28
+ )
29
+ end
30
+ }
31
+ end
32
+
33
+ rule config_statement
34
+ override / include / include_file / command / path / set
35
+ end
36
+
37
+ rule include_file
38
+ statement_start:'include-file'
39
+ ws_or_comment+
40
+ path:file_path
41
+ config:(':' config_name:config_name)?
42
+ {
43
+ def to_config_statement(build_state)
44
+ config_name = nil
45
+ if config.respond_to? :config_name
46
+ config_name = config.config_name
47
+ end
48
+
49
+ return build_state.new_include_file_statement(
50
+ statement_start, path, config_name
51
+ )
52
+ end
53
+ }
54
+ end
55
+
56
+ rule file_path
57
+ # This is like quoted_or_bare_string, but disallows unquoted colons so
58
+ # that we can differentiate config names.
59
+ '"' ( [^"\\] / '\\' . )* '"' /
60
+ "'" ( [^'\\] / '\\' . )* "'" /
61
+ [^\s#:]+
62
+ end
63
+ end
64
+ end
65
+ end
@@ -4,6 +4,7 @@
4
4
 
5
5
  require 'fig/grammar/v0'
6
6
  require 'fig/grammar/v1'
7
+ require 'fig/grammar/v2'
7
8
 
8
9
  module Fig; end
9
10
  module Fig::Grammar; end
@@ -19,3 +20,9 @@ class Fig::Grammar::V1Parser
19
20
  return 1
20
21
  end
21
22
  end
23
+
24
+ class Fig::Grammar::V2Parser
25
+ def version()
26
+ return 2
27
+ end
28
+ end
@@ -5,11 +5,13 @@ module Fig
5
5
  # User specified a configuration for a Package that does not exist.
6
6
  class NoSuchPackageConfigError < UserInputError
7
7
  attr_reader :descriptor
8
+ attr_reader :package
8
9
 
9
- def initialize(message, descriptor)
10
+ def initialize(message, descriptor, package)
10
11
  super(message)
11
12
 
12
13
  @descriptor = descriptor
14
+ @package = package
13
15
  end
14
16
  end
15
17
  end
@@ -0,0 +1,27 @@
1
+ module Fig; end
2
+
3
+ # Metadata about a package definition file that hasn't been read yet.
4
+ class Fig::NotYetParsedPackage
5
+ attr_accessor :descriptor
6
+ attr_accessor :working_directory
7
+ attr_accessor :base_directory
8
+ attr_accessor :source_description
9
+ attr_accessor :unparsed_text
10
+
11
+ def extended_source_description()
12
+ if source_description
13
+ if source_description.start_with? working_directory
14
+ return source_description
15
+ end
16
+
17
+ extended = source_description
18
+ if working_directory != '.'
19
+ extended << " (#{working_directory})"
20
+ end
21
+
22
+ return extended
23
+ end
24
+
25
+ return working_directory
26
+ end
27
+ end
@@ -94,21 +94,21 @@ class Fig::OperatingSystem
94
94
 
95
95
  # Determine whether we need to update something. Returns nil to indicate
96
96
  # "don't know".
97
- def path_up_to_date?(url, path)
97
+ def path_up_to_date?(url, path, prompt_for_login)
98
98
  return false if ! File.exist? path
99
99
 
100
100
  protocol, uri = decode_protocol url
101
- return protocol.path_up_to_date? uri, path
101
+ return protocol.path_up_to_date? uri, path, prompt_for_login
102
102
  end
103
103
 
104
104
  # Returns whether the file was not downloaded because the file already
105
105
  # exists and is already up-to-date.
106
- def download(url, path)
106
+ def download(url, path, prompt_for_login)
107
107
  protocol, uri = decode_protocol url
108
108
 
109
109
  FileUtils.mkdir_p(File.dirname path)
110
110
 
111
- return protocol.download uri, path
111
+ return protocol.download uri, path, prompt_for_login
112
112
  end
113
113
 
114
114
  # Returns the basename and full path to the download.
@@ -118,7 +118,7 @@ class Fig::OperatingSystem
118
118
  basename = CGI.unescape Fig::URL.parse(url).path.split('/').last
119
119
  path = File.join(download_directory, basename)
120
120
 
121
- download(url, path)
121
+ download(url, path, false)
122
122
 
123
123
  return basename, path
124
124
  end
data/lib/fig/package.rb CHANGED
@@ -22,20 +22,38 @@ class Fig::Package
22
22
  attr_reader :version
23
23
  attr_reader :description
24
24
  attr_reader :runtime_directory
25
+ attr_reader :base_directory
25
26
  attr_reader :statements
26
27
  attr_accessor :backtrace
27
28
  attr_accessor :unparsed_text
28
29
 
29
- def initialize(name, version, description, runtime_directory, statements)
30
+ def initialize(
31
+ name,
32
+ version,
33
+ description,
34
+ runtime_directory,
35
+ base_directory,
36
+ statements,
37
+ synthetic
38
+ )
30
39
  @name = name
31
40
  @version = version
32
41
  @description = description
33
42
  @runtime_directory = runtime_directory
43
+ @base_directory = base_directory
34
44
  @statements = statements
45
+ @synthetic = synthetic
35
46
  @applied_config_names = []
36
47
  @backtrace = nil
37
48
  end
38
49
 
50
+ # Was this package (supposedly) created from something other than usual
51
+ # parsing? (Note that some tests artificially create "non-synthetic"
52
+ # instances.)
53
+ def synthetic?
54
+ return @synthetic
55
+ end
56
+
39
57
  # Is this the base package?
40
58
  def base?()
41
59
  return @base
@@ -64,7 +82,7 @@ class Fig::Package
64
82
 
65
83
  message = %Q<There is no "#{config_description}" config.>
66
84
 
67
- raise Fig::NoSuchPackageConfigError.new(message, descriptor)
85
+ raise Fig::NoSuchPackageConfigError.new(message, descriptor, self)
68
86
  end
69
87
 
70
88
  def <=>(other)
@@ -2,6 +2,7 @@ require 'fig/statement/grammar_version'
2
2
  require 'fig/unparser'
3
3
  require 'fig/unparser/v0'
4
4
  require 'fig/unparser/v1'
5
+ require 'fig/unparser/v2'
5
6
 
6
7
  module Fig; end
7
8
 
@@ -34,7 +35,7 @@ class Fig::PackageDefinitionTextAssembler
34
35
  def add_output(*statements)
35
36
  # Version gets determined by other statements, not by existing grammar.
36
37
  @output_statements <<
37
- statements.reject { |s| s.is_a? Fig::Statement::GrammarVersion }
38
+ statements.flatten.reject { |s| s.is_a? Fig::Statement::GrammarVersion }
38
39
 
39
40
  @output_statements.flatten!
40
41
 
@@ -6,7 +6,8 @@ module Fig; end
6
6
  class Fig::PackageDescriptor
7
7
  include Comparable
8
8
 
9
- COMPONENT_PATTERN = / \A (?! [.]{1,2} $) [a-zA-Z0-9_.-]+ \z /x
9
+ UNBRACKETED_COMPONENT_PATTERN = / (?! [.]{1,2} $) [a-zA-Z0-9_.-]+ /x
10
+ COMPONENT_PATTERN = / \A #{UNBRACKETED_COMPONENT_PATTERN} \z /x
10
11
 
11
12
  attr_reader :name, :version, :config, :original_string, :description
12
13
 
@@ -65,9 +66,9 @@ class Fig::PackageDescriptor
65
66
  # most likely the result of invoking
66
67
  # Fig::Statement.position_description().
67
68
  def initialize(name, version, config, options = {})
68
- @name = name
69
- @version = version
70
- @config = config
69
+ @name = translate_component(name)
70
+ @version = translate_component(version)
71
+ @config = translate_component(config)
71
72
  @original_string = options[:original_string]
72
73
  @description = options[:description]
73
74
 
@@ -94,6 +95,12 @@ class Fig::PackageDescriptor
94
95
 
95
96
  private
96
97
 
98
+ def translate_component(value)
99
+ return if value.nil?
100
+ return if value.empty?
101
+ return value
102
+ end
103
+
97
104
  def validate_component(
98
105
  value, name, presence_requirement_symbol, options
99
106
  )
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/v2'
6
7
  require 'fig/grammar_monkey_patches'
7
8
  require 'fig/logging'
8
9
  require 'fig/package_parse_error'
@@ -22,123 +23,108 @@ class Fig::Parser
22
23
  @check_include_versions = check_include_versions
23
24
  end
24
25
 
25
- def parse_package(descriptor, directory, source_description, unparsed_text)
26
- version = get_grammar_version(
27
- descriptor, directory, source_description, unparsed_text
28
- )
26
+ def parse_package(unparsed_package)
27
+ version = get_grammar_version unparsed_package
29
28
 
30
29
  if version == 0
31
- return parse_v0(descriptor, directory, source_description, unparsed_text)
30
+ return parse_v0 unparsed_package
32
31
  end
33
32
 
34
- return parse_v1(descriptor, directory, source_description, unparsed_text)
33
+ return parse_v1_or_later version, unparsed_package
35
34
  end
36
35
 
37
36
  private
38
37
 
39
- def get_grammar_version(
40
- descriptor, directory, source_description, unparsed_text
41
- )
38
+ PARSER_CLASS = {
39
+ 1 => Fig::Grammar::V1Parser,
40
+ 2 => Fig::Grammar::V2Parser,
41
+ }
42
+
43
+ # TODO: Remove this once stablized.
44
+ @@seen_v2 = false
45
+
46
+ def get_grammar_version(unparsed_package)
42
47
  version_parser = Fig::Grammar::VersionIdentificationParser.new()
43
48
 
44
- extended_description =
45
- extend_source_description(directory, source_description)
49
+ extended_description = unparsed_package.extended_source_description
46
50
 
47
- result = version_parser.parse(unparsed_text)
51
+ result = version_parser.parse(unparsed_package.unparsed_text)
48
52
  if result.nil?
49
- raise_parse_error(version_parser, unparsed_text, extended_description)
53
+ raise_parse_error(
54
+ version_parser,
55
+ unparsed_package.unparsed_text,
56
+ extended_description
57
+ )
50
58
  end
51
59
 
52
60
  statement = result.get_grammar_version(
53
- Fig::ParserPackageBuildState.new(nil, descriptor, extended_description)
61
+ Fig::ParserPackageBuildState.new(
62
+ nil, unparsed_package.descriptor, extended_description
63
+ )
54
64
  )
55
65
  return 0 if not statement
56
66
 
57
67
  version = statement.version
58
- if version > 1
68
+ if version > 2
59
69
  raise Fig::PackageParseError.new(
60
70
  %Q<Don't know how to parse grammar version #{version}#{statement.position_string()}.>
61
71
  )
62
72
  end
73
+ if version == 2 && ! @@seen_v2
74
+ @@seen_v2 = true
75
+ Fig::Logging.info(
76
+ 'Encountered v2 grammar. This is experimental and subject to change without notice.'
77
+ )
78
+ end
63
79
 
64
80
  return version
65
81
  end
66
82
 
67
- def parse_v0(descriptor, directory, source_description, unparsed_text)
68
- stripped_text = unparsed_text.gsub(/#.*$/, '') # Blech.
83
+ def parse_v0(unparsed_package)
84
+ stripped_text = unparsed_package.unparsed_text.gsub(/#.*$/, '') # Blech.
69
85
 
70
86
  v0_parser = Fig::Grammar::V0Parser.new
71
87
 
72
- return drive_treetop_parser(
73
- v0_parser,
74
- descriptor,
75
- directory,
76
- source_description,
77
- unparsed_text,
78
- stripped_text
79
- )
88
+ return drive_treetop_parser(v0_parser, unparsed_package, stripped_text)
80
89
  end
81
90
 
82
- def parse_v1(descriptor, directory, source_description, unparsed_text)
83
- v1_parser = Fig::Grammar::V1Parser.new
91
+ def parse_v1_or_later(version, unparsed_package)
92
+ parser = PARSER_CLASS[version].new
84
93
 
85
94
  return drive_treetop_parser(
86
- v1_parser,
87
- descriptor,
88
- directory,
89
- source_description,
90
- unparsed_text,
91
- unparsed_text
95
+ parser,
96
+ unparsed_package,
97
+ unparsed_package.unparsed_text
92
98
  )
93
99
  end
94
100
 
95
101
  def drive_treetop_parser(
96
- parser,
97
- descriptor,
98
- directory,
99
- source_description,
100
- unparsed_text, # Ugh. V0 strips comments via regex.
101
- cleaned_text
102
+ parser, unparsed_package, cleaned_text # Ugh. V0 strips comments via regex.
102
103
  )
103
104
  # Extra space at the end because most of the rules in the grammar require
104
105
  # trailing whitespace.
105
106
  result = parser.parse(cleaned_text + ' ')
106
107
 
107
- extended_description =
108
- extend_source_description(directory, source_description)
108
+ extended_description = unparsed_package.extended_source_description
109
109
 
110
110
  if result.nil?
111
111
  raise_parse_error(parser, cleaned_text, extended_description)
112
112
  end
113
113
 
114
114
  package = result.to_package(
115
- directory,
115
+ unparsed_package,
116
116
  Fig::ParserPackageBuildState.new(
117
- parser.version, descriptor, extended_description
117
+ parser.version, unparsed_package.descriptor, extended_description
118
118
  )
119
119
  )
120
- package.unparsed_text = unparsed_text
121
120
 
122
- check_for_bad_urls(package, descriptor)
121
+ check_for_bad_urls(package, unparsed_package.descriptor)
123
122
  check_for_multiple_command_statements(package)
124
123
  check_for_missing_versions_on_include_statements(package)
125
124
 
126
125
  return package
127
126
  end
128
127
 
129
- def extend_source_description(directory, original_description)
130
- if original_description
131
- extended = original_description
132
- if directory != '.'
133
- extended << " (#{directory})"
134
- end
135
-
136
- return extended
137
- end
138
-
139
- return directory
140
- end
141
-
142
128
  def raise_parse_error(treetop_parser, text, extended_description)
143
129
  message = extended_description
144
130
 
@@ -6,6 +6,7 @@ require 'fig/statement/command'
6
6
  require 'fig/statement/configuration'
7
7
  require 'fig/statement/grammar_version'
8
8
  require 'fig/statement/include'
9
+ require 'fig/statement/include_file'
9
10
  require 'fig/statement/override'
10
11
  require 'fig/statement/path'
11
12
  require 'fig/statement/resource'
@@ -43,7 +44,7 @@ class Fig::ParserPackageBuildState
43
44
  )
44
45
  end
45
46
 
46
- def new_package_statement(directory, grammar_node, statement_nodes)
47
+ def new_package_statement(unparsed_package, grammar_node, statement_nodes)
47
48
  grammar_statement = nil
48
49
  if grammar_node && ! grammar_node.empty?
49
50
  grammar_statement = grammar_node.to_package_statement(self)
@@ -62,13 +63,18 @@ class Fig::ParserPackageBuildState
62
63
  statement_objects << node.to_package_statement(self)
63
64
  end
64
65
 
65
- return Fig::Package.new(
66
+ package = Fig::Package.new(
66
67
  @descriptor.name,
67
68
  @descriptor.version,
68
69
  @descriptor.description,
69
- directory,
70
- statement_objects
70
+ unparsed_package.working_directory,
71
+ unparsed_package.base_directory,
72
+ statement_objects,
73
+ false,
71
74
  )
75
+ package.unparsed_text = unparsed_package.unparsed_text
76
+
77
+ return package
72
78
  end
73
79
 
74
80
  def new_grammar_version_statement(keyword_node, version_node)
@@ -143,10 +149,39 @@ class Fig::ParserPackageBuildState
143
149
  node_location(keyword_node),
144
150
  @source_description,
145
151
  include_descriptor,
152
+ nil,
146
153
  @descriptor
147
154
  )
148
155
  end
149
156
 
157
+ def new_include_file_statement(keyword_node, path_node, config_name_node)
158
+ path, config_name =
159
+ Fig::Statement::IncludeFile.validate_and_process_raw_path_and_config_name(
160
+ path_node.text_value,
161
+ config_name_node.nil? ? nil : config_name_node.text_value,
162
+ ) do
163
+ |description|
164
+
165
+ value_text = path_node.text_value
166
+ if ! config_name_node.nil?
167
+ value_text << ':'
168
+ value_text << config_name_node.text_value
169
+ end
170
+
171
+ raise Fig::PackageParseError.new(
172
+ %Q<Invalid include-file statement: "#{value_text}" #{description}#{node_location_description(path_node)}>
173
+ )
174
+ end
175
+
176
+ return Fig::Statement::IncludeFile.new(
177
+ node_location(keyword_node),
178
+ @source_description,
179
+ path,
180
+ config_name,
181
+ @descriptor,
182
+ )
183
+ end
184
+
150
185
  def new_override_statement(keyword_node, descriptor_node)
151
186
  override_descriptor =
152
187
  Fig::Statement::Override.parse_descriptor(