fig 0.1.62 → 0.1.64

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 +156 -0
  2. data/VERSION +1 -1
  3. data/bin/fig +9 -2
  4. data/bin/fig-debug +9 -2
  5. data/lib/fig/applicationconfiguration.rb +3 -2
  6. data/lib/fig/atexit.rb +37 -0
  7. data/lib/fig/backtrace.rb +23 -6
  8. data/lib/fig/command.rb +131 -31
  9. data/lib/fig/command/coveragesupport.rb +40 -0
  10. data/lib/fig/command/listing.rb +8 -8
  11. data/lib/fig/command/optionerror.rb +8 -0
  12. data/lib/fig/{options.rb → command/options.rb} +248 -144
  13. data/lib/fig/command/packageload.rb +161 -62
  14. data/lib/fig/configfileerror.rb +2 -0
  15. data/lib/fig/environment.rb +350 -246
  16. data/lib/fig/environmentvariables/casesensitive.rb +1 -1
  17. data/lib/fig/figrc.rb +78 -78
  18. data/lib/fig/grammar.treetop +204 -219
  19. data/lib/fig/log4rconfigerror.rb +2 -0
  20. data/lib/fig/operatingsystem.rb +382 -334
  21. data/lib/fig/package.rb +11 -33
  22. data/lib/fig/packagecache.rb +1 -1
  23. data/lib/fig/packagedescriptor.rb +103 -21
  24. data/lib/fig/packagedescriptorparseerror.rb +16 -0
  25. data/lib/fig/parser.rb +36 -19
  26. data/lib/fig/parserpackagebuildstate.rb +56 -0
  27. data/lib/fig/repository.rb +504 -259
  28. data/lib/fig/statement.rb +30 -12
  29. data/lib/fig/statement/archive.rb +8 -5
  30. data/lib/fig/statement/asset.rb +19 -0
  31. data/lib/fig/statement/command.rb +2 -2
  32. data/lib/fig/statement/configuration.rb +20 -20
  33. data/lib/fig/statement/include.rb +13 -34
  34. data/lib/fig/statement/override.rb +21 -7
  35. data/lib/fig/statement/path.rb +22 -2
  36. data/lib/fig/statement/resource.rb +14 -4
  37. data/lib/fig/statement/retrieve.rb +34 -4
  38. data/lib/fig/statement/set.rb +22 -2
  39. data/lib/fig/workingdirectorymaintainer.rb +197 -0
  40. data/lib/fig/workingdirectorymetadata.rb +45 -0
  41. metadata +52 -46
  42. data/lib/fig/retriever.rb +0 -141
  43. data/lib/fig/statement/publish.rb +0 -15
data/Changes CHANGED
@@ -1,3 +1,159 @@
1
+ v0.1.64
2
+
3
+ Backwards incompatibilities:
4
+
5
+ - --set and --append command-line options now take priority over package.fig
6
+ files and --include options are now processed after any "include"
7
+ statements.
8
+
9
+ Previously, "fig foo/1.2.3 --set SET=command-line --append
10
+ APPEND=command-line --include command-line/1.2.3" was equivalent to
11
+
12
+ config default
13
+ set SET=command-line
14
+ add APPEND=command-line
15
+ include command-line/1.2.3
16
+
17
+ include foo/1.2.3
18
+ end
19
+
20
+ Now it is equivalent to
21
+
22
+ config default
23
+ include foo/1.2.3
24
+
25
+ set SET=command-line
26
+ add APPEND=command-line
27
+ include command-line/1.2.3
28
+ end
29
+
30
+ (It really is like that: a package is synthesized with statements
31
+ equivalent to the command-line options and that package is run through the
32
+ whole process.)
33
+
34
+ - Retrieve variable names can no longer contain "@", "/", or ".". Since
35
+ environment variable statements only allow alphanumerics and underscore in
36
+ the variable names, any retrieve variable with those characters in its name
37
+ would never have any effect.
38
+
39
+ - Running fig --publish or --publish-local with --resource or --archive
40
+ options without also specifying a --set or --append option is now an error.
41
+
42
+ What would previously happen is that the --resource and --archive options
43
+ were ignored and package.fig locating would happen and the publish would be
44
+ based only on that. Probably not what was intended.
45
+
46
+ E.g.:
47
+
48
+ fig --publish foo/1 --resource=something.txt
49
+
50
+ would ignore the --resource option, look for a package.fig file and publish
51
+ based upon that.
52
+
53
+ - Now fails if you specify both a descriptor and the --file option and you
54
+ aren't publishing. Previously it would silently ignore the --file option,
55
+ which could be a bit more than a little surprising.
56
+
57
+ New features:
58
+
59
+ - Overrides are now independent statements, no longer attached to includes.
60
+ E.g. this
61
+
62
+ config default
63
+ include A/1 override C/3
64
+ include B/1 override C/3
65
+ end
66
+
67
+ is now equivalent to this
68
+
69
+ config default
70
+ override C/3
71
+ include A/1
72
+ include B/1
73
+ end
74
+
75
+ No changes to existing (functioning) package.fig files will be necessary.
76
+ In fact, this will fix some that didn't work due to versionless includes.
77
+
78
+ - You can now specify overrides on the command-line using "--override".
79
+
80
+ - Now checks repository format version and fails if it's different from what
81
+ it knows about.
82
+
83
+ There are no immediate plans to change the repository format, but
84
+ considering the inability to fix the non-unique archive name issue below,
85
+ it behooves us to allow for a format change in the future. If the layout
86
+ does change, then current code could possibly corrupt the future
87
+ repository.
88
+
89
+ - Warns if you attempt to use a "retrieve" statement to write to an absolute
90
+ path; all retrieve destinations are relative.
91
+
92
+ - Now warns about ineffectual "retrieve" statements.
93
+
94
+ - A lot of existence checks have been added, resulting in nicer error
95
+ messages and fewer stack traces.
96
+
97
+ - README.md has had most of its content ripped out. Documentation has been
98
+ greatly expanded and is available at https://github.com/mfoemmel/fig/wiki.
99
+
100
+ - Includes the Fig version in the comments in published .fig files.
101
+
102
+ Bug fixes:
103
+
104
+ - Better command-line parsing. There were a number of scenarios where
105
+ missing or malformed arguments to options would cause incorrect behavior or
106
+ stack traces.
107
+
108
+ - Handling of symlinks is better.
109
+
110
+ - Now checks that archive base names are unique before allowing publishing.
111
+ Previously, if you had something like
112
+
113
+ archive http://somewhere/archive.tar.gz
114
+ archive foo/archive.tar.gz
115
+
116
+ everything would work locally, but the version of the package published to
117
+ the remote repository was corrupt. Given the current repository layout,
118
+ this is not fixable, so we now disallow publishing packages like this.
119
+
120
+ Similarly, we now complain about archives named "resources.tar.gz" because
121
+ Fig creates an archive by that name to hold resources.
122
+
123
+ - Updates should be more robust in the face of simultaneous runs of fig.
124
+
125
+ - Retrieves:
126
+
127
+ * Now saves retrieve metadata prior to an exec(2). Previously, if you ran
128
+ a command, Fig would lose track of what you had in your current
129
+ directory.
130
+
131
+ * Cleanup of retrieves for no-longer-referenced packages now happens, i.e.,
132
+ if a dependency removes a dependency, files are correctly removed.
133
+
134
+ Note that, depending upon the packages involved, switching between two
135
+ base packages can be slower than it was before. In other words, if
136
+ packages "foo" and "bar" have a lot of retrieves of dependences NOT in
137
+ common, running
138
+
139
+ fig --update-if-missing foo/v1.2.3
140
+ fig --update-if-missing bar/v6.5.4
141
+
142
+ will be slower. On the other hand, you won't have a lot of stuff from
143
+ "foo" hanging around that shouldn't be there.
144
+
145
+ v0.1.63.beta.2
146
+ v0.1.63.beta.1
147
+
148
+ - Test releases
149
+
150
+ v0.1.62
151
+
152
+ Bug fixes:
153
+
154
+ - Fixed stack trace when encountering conflicting dependency package
155
+ versions.
156
+
1
157
  v0.1.61
2
158
 
3
159
  New features:
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.62
1
+ 0.1.64
data/bin/fig CHANGED
@@ -1,10 +1,17 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ if ENV['FIG_COVERAGE']
4
+ require File.expand_path(
5
+ File.join(
6
+ File.dirname(__FILE__), %w< .. lib fig command coveragesupport.rb >
7
+ )
8
+ )
9
+ end
10
+
3
11
  $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), %w< .. lib > ))
4
12
 
5
13
  require 'rubygems'
6
14
 
7
15
  require 'fig/command'
8
16
 
9
- return_code = Fig::Command.new.run_with_exception_handling(ARGV)
10
- exit return_code
17
+ exit Fig::Command.new.run_with_exception_handling ARGV
@@ -1,5 +1,13 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ if ENV['FIG_COVERAGE']
4
+ require File.expand_path(
5
+ File.join(
6
+ File.dirname(__FILE__), %w< .. lib fig command coveragesupport.rb >
7
+ )
8
+ )
9
+ end
10
+
3
11
  $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), %w< .. lib > ))
4
12
 
5
13
  require 'rubygems'
@@ -8,5 +16,4 @@ require 'fig/command'
8
16
 
9
17
  # Identical to regular fig, but doesn't use exception handling so you can see
10
18
  # stack traces.
11
- return_code = Fig::Command.new.run_fig(ARGV)
12
- exit return_code
19
+ exit Fig::Command.new.run_fig ARGV
@@ -1,8 +1,9 @@
1
1
  module Fig; end
2
2
 
3
- # Configuration for the Fig program, as opposed to the configuration in a
4
- # package.
3
+ # Configuration for the Fig program, as opposed to a config in a package.
5
4
  class Fig::ApplicationConfiguration
5
+ attr_reader :remote_repository_url
6
+
6
7
  def initialize(remote_repository_url)
7
8
  @data = []
8
9
  @remote_repository_url = remote_repository_url
@@ -0,0 +1,37 @@
1
+ module Fig; end
2
+
3
+ # This exists because standard Kernel#at_exit blocks don't get run before
4
+ # Kernel#exec.
5
+ class Fig::AtExit
6
+ def self.add(&block)
7
+ EXIT_PROCS << block
8
+
9
+ return
10
+ end
11
+
12
+ def self.execute()
13
+ EXIT_PROCS.each do
14
+ |proc|
15
+
16
+ begin
17
+ proc.call()
18
+ rescue StandardError => exception
19
+ $stderr.puts(
20
+ [
21
+ %q<Got exception from "at exit" processing.>,
22
+ exception.message,
23
+ exception.backtrace
24
+ ].flatten.join("\n")
25
+ )
26
+ end
27
+ end
28
+
29
+ return
30
+ end
31
+
32
+ private
33
+
34
+ EXIT_PROCS = []
35
+
36
+ at_exit { Fig::AtExit.execute() }
37
+ end
@@ -1,7 +1,8 @@
1
+ require 'fig/repositoryerror'
2
+
1
3
  module Fig; end
2
4
 
3
- # Contains traces of file inclusions so that the user can track down which file
4
- # an error occurred in.
5
+ # Keeps track of overrides and can produce package definition stack traces.
5
6
  class Fig::Backtrace
6
7
  attr_reader :overrides
7
8
 
@@ -11,11 +12,21 @@ class Fig::Backtrace
11
12
  @overrides = {}
12
13
  end
13
14
 
14
- def add_override(package_name, version)
15
+ def add_override(statement)
16
+ package_name = statement.package_name
15
17
  # Don't replace an existing override on the stack
16
- return if get_override(package_name)
18
+ return if @parent && @parent.get_override(package_name)
19
+
20
+ new_version = statement.version
21
+ existing_version = @overrides[package_name]
22
+ if existing_version && existing_version != new_version
23
+ stacktrace = dump_to_string()
24
+ raise Fig::RepositoryError.new(
25
+ "Override #{package_name} version conflict (#{existing_version} vs #{new_version})#{statement.position_string}." + ( stacktrace.empty? ? '' : "\n#{stacktrace}" )
26
+ )
27
+ end
17
28
 
18
- @overrides[package_name] = version
29
+ @overrides[package_name] = new_version
19
30
  end
20
31
 
21
32
  # Returns a version.
@@ -31,7 +42,7 @@ class Fig::Backtrace
31
42
  def dump(out)
32
43
  stack = []
33
44
  collect(stack)
34
- i=0
45
+ i = 0
35
46
  for descriptor in stack
36
47
  indent=''
37
48
  i.times { indent += ' ' }
@@ -42,6 +53,12 @@ class Fig::Backtrace
42
53
 
43
54
  protected
44
55
 
56
+ def dump_to_string()
57
+ string_handle = StringIO.new
58
+ dump(string_handle)
59
+ return string_handle.string
60
+ end
61
+
45
62
  def collect(stack)
46
63
  if @parent
47
64
  @parent.collect(stack)
@@ -2,27 +2,28 @@ require 'rubygems'
2
2
  require 'net/ftp'
3
3
  require 'set'
4
4
 
5
+ require 'fig/atexit'
6
+ require 'fig/command/options'
5
7
  require 'fig/environment'
6
8
  require 'fig/figrc'
7
9
  require 'fig/logging'
8
10
  require 'fig/operatingsystem'
9
- require 'fig/options'
10
11
  require 'fig/package'
11
12
  require 'fig/parser'
12
13
  require 'fig/repository'
13
14
  require 'fig/repositoryerror'
14
- require 'fig/retriever'
15
15
  require 'fig/statement/configuration'
16
- require 'fig/statement/publish'
17
16
  require 'fig/userinputerror'
17
+ require 'fig/workingdirectorymaintainer'
18
18
 
19
- # These are a breakout of parts of this class simply to keep the file size down.
19
+ # The following are a break out of parts of this class simply to keep the file
20
+ # size down.
20
21
 
21
22
  # You will need to look in this file for any stuff related to --list-* options.
22
23
  require 'fig/command/listing'
23
24
 
24
25
  # You will need to look in this file for any stuff related to loading the
25
- # primary Package object.
26
+ # base Package object.
26
27
  require 'fig/command/packageload'
27
28
 
28
29
  module Fig; end
@@ -32,17 +33,59 @@ class Fig::Command
32
33
  include Fig::Command::Listing
33
34
  include Fig::Command::PackageLoad
34
35
 
36
+ def self.get_version()
37
+ line = nil
38
+
39
+ begin
40
+ File.open(
41
+ "#{File.expand_path(File.dirname(__FILE__) + '/../../VERSION')}"
42
+ ) do |file|
43
+ line = file.gets
44
+ end
45
+ rescue
46
+ $stderr.puts 'Could not retrieve version number. Something has mucked with your Fig install.'
47
+
48
+ return nil
49
+ end
50
+
51
+ # Note that we accept anything that contains three decimal numbers
52
+ # seperated by periods. This allows for versions like
53
+ # "4.3.2-super-special-version-in-3D".
54
+ if line !~ %r< \b \d+ [.] \d+ [.] \d+ \b >x
55
+ $stderr.puts %Q<"#{line}" does not look like a version number. Something has mucked with your Fig install.>
56
+
57
+ return nil
58
+ end
59
+
60
+ return line
61
+ end
62
+
35
63
  def run_fig(argv)
36
- @options = Fig::Options.new(argv)
64
+ begin
65
+ @options = Fig::Command::Options.new(argv)
66
+ rescue Fig::UserInputError => error
67
+ $stderr.puts error.to_s # Logging isn't set up yet.
68
+ return 1
69
+ end
70
+
37
71
  if not @options.exit_code.nil?
38
72
  return @options.exit_code
39
73
  end
40
74
  @descriptor = @options.descriptor
41
75
 
76
+ if @options.help?
77
+ return @options.help
78
+ end
79
+
80
+ if @options.version?
81
+ return emit_version()
82
+ end
83
+
42
84
  configure()
43
85
 
44
86
  if @options.clean?
45
87
  check_required_package_descriptor('to clean')
88
+ ensure_descriptor_and_file_were_not_both_specified()
46
89
  @repository.clean(@descriptor)
47
90
  return 0
48
91
  end
@@ -55,26 +98,30 @@ class Fig::Command
55
98
  return publish()
56
99
  end
57
100
 
101
+ ensure_descriptor_and_file_were_not_both_specified()
102
+
58
103
  load_package_object()
59
104
 
60
105
  if @options.listing()
61
106
  handle_post_parse_list_options()
62
107
  elsif @options.get()
63
- puts @environment[@options.get()]
108
+ # Ruby v1.8 emits "nil" for nil, whereas ruby v1.9 emits the empty
109
+ # string, so, for consistency, we need to ensure that we always emit the
110
+ # empty string.
111
+ puts @environment[@options.get()] || ''
64
112
  elsif @options.shell_command
65
113
  @environment.execute_shell(@options.shell_command) do
66
114
  |command| @operating_system.shell_exec command
67
115
  end
68
116
  elsif @descriptor
69
- @environment.include_config(@package, @descriptor, {}, nil)
117
+ @environment.include_config(@base_package, @descriptor, nil)
70
118
  @environment.execute_config(
71
- @package,
119
+ @base_package,
72
120
  @descriptor,
73
121
  @options.command_extra_argv || []
74
122
  ) { |cmd| @operating_system.shell_exec cmd }
75
123
  elsif not @repository.updating?
76
- $stderr.puts "Nothing to do.\n"
77
- $stderr.puts Fig::Options::USAGE
124
+ $stderr.puts "Nothing to do.\n\n"
78
125
  $stderr.puts %q<Run "fig --help" for a full list of commands.>
79
126
  return 1
80
127
  end
@@ -95,7 +142,7 @@ class Fig::Command
95
142
  return 1
96
143
  rescue OptionParser::InvalidOption => error
97
144
  $stderr.puts error.to_s
98
- $stderr.puts Fig::Options::USAGE
145
+ $stderr.puts Fig::Command::Options::USAGE
99
146
  return 1
100
147
  rescue Fig::RepositoryError => error
101
148
  log_error_message(error)
@@ -103,6 +150,15 @@ class Fig::Command
103
150
  end
104
151
  end
105
152
 
153
+ def emit_version()
154
+ version = Fig::Command.get_version()
155
+ return 1 if version.nil?
156
+
157
+ puts File.basename($0) + ' v' + version
158
+
159
+ return 0
160
+ end
161
+
106
162
  private
107
163
 
108
164
  def derive_remote_url()
@@ -128,11 +184,10 @@ class Fig::Command
128
184
  def configure()
129
185
  Fig::Logging.initialize_pre_configuration(@options.log_level())
130
186
 
131
- remote_url = derive_remote_url()
132
187
 
133
188
  @configuration = Fig::FigRC.find(
134
189
  @options.figrc(),
135
- remote_url,
190
+ derive_remote_url(),
136
191
  @options.login?,
137
192
  @options.home(),
138
193
  @options.no_figrc?
@@ -146,8 +201,7 @@ class Fig::Command
146
201
  @operating_system = Fig::OperatingSystem.new(@options.login?)
147
202
  @repository = Fig::Repository.new(
148
203
  @operating_system,
149
- File.expand_path(File.join(@options.home(), 'repos')),
150
- remote_url,
204
+ @options.home(),
151
205
  @configuration,
152
206
  nil, # remote_user
153
207
  @options.update?,
@@ -155,15 +209,13 @@ class Fig::Command
155
209
  check_include_statements_versions?
156
210
  )
157
211
 
158
- @retriever = Fig::Retriever.new('.')
159
-
160
- at_exit { @retriever.save_metadata() }
212
+ @working_directory_maintainer = Fig::WorkingDirectoryMaintainer.new('.')
161
213
 
162
- @environment = prepare_environment
163
-
164
- @options.non_command_package_statements().each do |statement|
165
- @environment.apply_config_statement(nil, statement, nil)
214
+ Fig::AtExit.add do
215
+ @working_directory_maintainer.prepare_for_shutdown(@options.updating?)
166
216
  end
217
+
218
+ prepare_environment()
167
219
  end
168
220
 
169
221
  def prepare_environment()
@@ -172,7 +224,18 @@ class Fig::Command
172
224
  environment_variables = Fig::OperatingSystem.get_environment_variables({})
173
225
  end
174
226
 
175
- return Fig::Environment.new(@repository, environment_variables, @retriever)
227
+ @environment = Fig::Environment.new(
228
+ @repository, environment_variables, @working_directory_maintainer
229
+ )
230
+
231
+ Fig::AtExit.add { @environment.check_unused_retrieves() }
232
+
233
+ return
234
+ end
235
+
236
+ def config_was_specified_by_user()
237
+ return ! @options.config().nil? ||
238
+ @descriptor && ! @descriptor.config().nil?
176
239
  end
177
240
 
178
241
  def base_config()
@@ -181,6 +244,29 @@ class Fig::Command
181
244
  Fig::Package::DEFAULT_CONFIG
182
245
  end
183
246
 
247
+ # If the user has specified a descriptor, than any package.fig or --file
248
+ # option is ignored. Thus, in order to avoid confusing the user, we make
249
+ # specifying both an error.
250
+ #
251
+ # The one exception to this rule is when we are publishing, which should
252
+ # already have been invoked by the time this is called.
253
+ def ensure_descriptor_and_file_were_not_both_specified()
254
+ file = @options.package_definition_file()
255
+
256
+ # If the user specified --no-file, even though it's kind of superfluous,
257
+ # we'll let it slide because the user doesn't think that any file will be
258
+ # processed.
259
+ file_specified = ! file.nil? && file != :none
260
+
261
+ if @descriptor and file_specified
262
+ raise Fig::UserInputError.new(
263
+ %Q<Cannot specify both a package descriptor (#{@descriptor.original_string}) and the --file option (#{file}).>
264
+ )
265
+ end
266
+
267
+ return
268
+ end
269
+
184
270
  def check_required_package_descriptor(operation_description)
185
271
  if not @descriptor
186
272
  raise Fig::UserInputError.new(
@@ -209,31 +295,45 @@ class Fig::Command
209
295
  check_required_package_descriptor('to publish')
210
296
 
211
297
  if @descriptor.name.nil? || @descriptor.version.nil?
212
- raise Fig::UserInputError.new('Please specify a package name and a version name.')
298
+ raise Fig::UserInputError.new(
299
+ 'Please specify a package name and a version name.'
300
+ )
301
+ end
302
+ if @descriptor.name == '_meta'
303
+ raise Fig::UserInputError.new(
304
+ %q<Due to implementation issues, cannot create a package named "_meta".>
305
+ )
213
306
  end
214
307
 
215
- if not @options.non_command_package_statements().empty?
308
+ publish_statements = nil
309
+ if not @options.environment_statements().empty?
216
310
  publish_statements =
217
311
  @options.resources() +
218
312
  @options.archives() +
219
313
  [
220
314
  Fig::Statement::Configuration.new(
315
+ nil,
221
316
  nil,
222
317
  Fig::Package::DEFAULT_CONFIG,
223
- @options.non_command_package_statements()
318
+ @options.environment_statements()
224
319
  )
225
320
  ]
226
- publish_statements << Fig::Statement::Publish.new()
321
+ elsif not @options.resources().empty? or not @options.archives().empty?
322
+ raise Fig::UserInputError.new(
323
+ '--resource/--archive options were specified, but no --set/--append option was given. Will not publish.'
324
+ )
227
325
  else
228
- load_package_file()
229
- if not @package.statements.empty?
230
- publish_statements = @package.statements
326
+ load_package_object_from_file()
327
+ if not @base_package.statements.empty?
328
+ publish_statements = @base_package.statements
231
329
  else
232
330
  $stderr.puts 'Nothing to publish.'
233
331
  return 1
234
332
  end
235
333
  end
236
334
 
335
+ apply_base_config_to_environment(:ignore_base_package)
336
+
237
337
  if @options.publish?
238
338
  Fig::Logging.info "Checking status of #{@descriptor.to_string()}..."
239
339