fig 1.1.0 → 1.2.0

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 (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(