fig 0.1.67 → 0.1.69

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,318 @@
1
+ require 'fileutils'
2
+ require 'set'
3
+ require 'socket'
4
+ require 'sys/admin'
5
+ require 'tmpdir'
6
+
7
+ require 'fig'
8
+ require 'fig/at_exit'
9
+ require 'fig/logging'
10
+ require 'fig/not_found_error'
11
+ require 'fig/package_cache'
12
+ require 'fig/package_descriptor'
13
+ require 'fig/parser'
14
+ require 'fig/repository'
15
+ require 'fig/repository_error'
16
+ require 'fig/statement/archive'
17
+ require 'fig/statement/resource'
18
+
19
+ module Fig; end
20
+
21
+ class Fig::RepositoryPackagePublisher
22
+ attr_accessor :operating_system
23
+ attr_accessor :publish_listeners
24
+ attr_accessor :package_statements
25
+ attr_accessor :descriptor
26
+ attr_accessor :source_package
27
+ attr_accessor :was_forced
28
+ attr_accessor :base_temp_dir
29
+ attr_accessor :local_dir_for_package
30
+ attr_accessor :remote_dir_for_package
31
+ attr_accessor :local_fig_file_for_package
32
+ attr_accessor :remote_fig_file_for_package
33
+ attr_accessor :local_only
34
+
35
+ def publish_package()
36
+ derive_publish_metadata()
37
+ validate_asset_names()
38
+
39
+ temp_dir = publish_temp_dir()
40
+ @operating_system.delete_and_recreate_directory(temp_dir)
41
+ @operating_system.delete_and_recreate_directory(@local_dir_for_package)
42
+
43
+ fig_file = File.join(temp_dir, Fig::Repository::PACKAGE_FILE_IN_REPO)
44
+ content = publish_package_content_and_derive_definition_file()
45
+ @operating_system.write(fig_file, content)
46
+
47
+ if not @local_only
48
+ @operating_system.upload(fig_file, remote_fig_file_for_package())
49
+ end
50
+ @operating_system.copy(fig_file, local_fig_file_for_package())
51
+
52
+ notify_listeners
53
+
54
+ FileUtils.rm_rf(temp_dir)
55
+
56
+ return true
57
+ end
58
+
59
+ private
60
+
61
+ def validate_asset_names()
62
+ asset_statements = @package_statements.select { |s| s.is_asset? }
63
+
64
+ asset_names = Set.new()
65
+ asset_statements.each do
66
+ |statement|
67
+
68
+ asset_name = statement.asset_name()
69
+ if not asset_name.nil?
70
+ if asset_name == Fig::Repository::RESOURCES_FILE
71
+ Fig::Logging.fatal \
72
+ %Q<You cannot have an asset with the name "#{Fig::Repository::RESOURCES_FILE}"#{statement.position_string()} due to Fig implementation details.>
73
+ end
74
+
75
+ if asset_names.include?(asset_name)
76
+ Fig::Logging.fatal \
77
+ %Q<Found multiple archives with the name "#{asset_name}"#{statement.position_string()}. If these were allowed, archives would overwrite each other.>
78
+ raise Fig::RepositoryError.new
79
+ else
80
+ asset_names.add(asset_name)
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ def derive_publish_metadata()
87
+ @publish_time = Time.now()
88
+ @publish_login = Sys::Admin.get_login()
89
+ @publish_host = Socket.gethostname()
90
+
91
+ return
92
+ end
93
+
94
+ def publish_package_content_and_derive_definition_file()
95
+ @definition_file_lines = []
96
+
97
+ add_package_metadata_comments()
98
+ publish_package_content()
99
+ add_unparsed_text()
100
+
101
+ @definition_file_lines.flatten!
102
+ file_content = @definition_file_lines.join("\n")
103
+ file_content.gsub!(/\n{3,}/, "\n\n")
104
+
105
+ return file_content.strip() + "\n"
106
+ end
107
+
108
+ def add_package_metadata_comments()
109
+ @definition_file_lines <<
110
+ %Q<# Publishing information for #{@descriptor.to_string()}:>
111
+ @definition_file_lines << %q<#>
112
+
113
+ @definition_file_lines <<
114
+ %Q<# Time: #{@publish_time} (epoch: #{@publish_time.to_i()})>
115
+
116
+ @definition_file_lines << %Q<# User: #{@publish_login}>
117
+ @definition_file_lines << %Q<# Host: #{@publish_host}>
118
+ @definition_file_lines << %Q<# Args: "#{ARGV.join %q[", "]}">
119
+ @definition_file_lines << %Q<# Fig: v#{Fig::VERSION}>
120
+ @definition_file_lines << %q<#>
121
+
122
+ asset_statements =
123
+ @package_statements.select { |statement| statement.is_asset? }
124
+ asset_strings =
125
+ asset_statements.collect { |statement| statement.unparse('# ') }
126
+
127
+ if asset_strings.empty?
128
+ @definition_file_lines <<
129
+ %q<# There were no asset statements in the unpublished package definition.>
130
+ else
131
+ @definition_file_lines << %q<# Original asset statements: >
132
+ @definition_file_lines << %q<#>
133
+ @definition_file_lines << asset_strings
134
+ end
135
+
136
+ @definition_file_lines << %Q<\n>
137
+
138
+ return
139
+ end
140
+
141
+ # Deals with Archive and Resource statements. It downloads any remote
142
+ # files (those where the statement references a URL as opposed to a local
143
+ # file) and then copies all files into the local repository and the remote
144
+ # repository (if not a local-only publish).
145
+ def publish_package_content()
146
+ initialize_statements_to_publish()
147
+ create_resource_archive()
148
+
149
+ @statements_to_publish.each do
150
+ |statement|
151
+
152
+ if statement.is_asset?
153
+ publish_asset(statement)
154
+ else
155
+ @definition_file_lines << statement.unparse('')
156
+ end
157
+ end
158
+
159
+ return
160
+ end
161
+
162
+ def initialize_statements_to_publish()
163
+ @resource_paths = []
164
+
165
+ @statements_to_publish = @package_statements.reject do |statement|
166
+ if (
167
+ statement.is_a?(Fig::Statement::Resource) &&
168
+ ! Fig::Repository.is_url?(statement.url)
169
+ )
170
+ @resource_paths << statement.url
171
+ true
172
+ else
173
+ false
174
+ end
175
+ end
176
+
177
+ return
178
+ end
179
+
180
+ def create_resource_archive()
181
+ if @resource_paths.size > 0
182
+ asset_paths = expand_globs_from(@resource_paths)
183
+ check_asset_paths(asset_paths)
184
+
185
+ file = Fig::Repository::RESOURCES_FILE
186
+ @operating_system.create_archive(file, asset_paths)
187
+ Fig::AtExit.add { File.delete(file) }
188
+
189
+ @statements_to_publish.unshift(
190
+ Fig::Statement::Archive.new(nil, nil, file)
191
+ )
192
+ end
193
+
194
+ return
195
+ end
196
+
197
+ def publish_asset(asset_statement)
198
+ asset_name = asset_statement.asset_name()
199
+ asset_remote = "#{remote_dir_for_package()}/#{asset_name}"
200
+
201
+ if Fig::Repository.is_url?(asset_statement.url)
202
+ asset_local = File.join(publish_temp_dir(), asset_name)
203
+
204
+ begin
205
+ @operating_system.download(asset_statement.url, asset_local)
206
+ rescue Fig::NotFoundError
207
+ Fig::Logging.fatal "Could not download #{asset_statement.url}."
208
+ raise Fig::RepositoryError.new
209
+ end
210
+ else
211
+ asset_local = asset_statement.url
212
+ check_asset_path(asset_local)
213
+ end
214
+
215
+ if not @local_only
216
+ @operating_system.upload(
217
+ asset_local, asset_remote
218
+ )
219
+ end
220
+
221
+ @operating_system.copy(
222
+ asset_local, @local_dir_for_package + '/' + asset_name
223
+ )
224
+ if asset_statement.is_a?(Fig::Statement::Archive)
225
+ @operating_system.unpack_archive(@local_dir_for_package, asset_name)
226
+ end
227
+
228
+ @definition_file_lines <<
229
+ asset_statement.class.new(nil, nil, asset_name).unparse('')
230
+
231
+ return
232
+ end
233
+
234
+ def add_unparsed_text()
235
+ if @source_package && @source_package.unparsed_text
236
+
237
+ @definition_file_lines << ''
238
+ @definition_file_lines << '# Original, unparsed package text:'
239
+ @definition_file_lines << '# '
240
+ @definition_file_lines << @source_package.unparsed_text.gsub(/^/, '# ')
241
+ end
242
+
243
+ return
244
+ end
245
+
246
+ def notify_listeners()
247
+ publish_information = {}
248
+ publish_information[:descriptor] = @descriptor
249
+ publish_information[:time] = @publish_time
250
+ publish_information[:login] = @publish_login
251
+ publish_information[:host] = @publish_host
252
+
253
+ # Ensure that we've really got booleans and not merely true or false
254
+ # values.
255
+ publish_information[:was_forced] = @was_forced ? true : false
256
+ publish_information[:local_only] = @local_only ? true : false
257
+
258
+ publish_information[:local_destination] = @local_dir_for_package
259
+ publish_information[:remote_destination] = @remote_dir_for_package
260
+
261
+ @publish_listeners.each do
262
+ |listener|
263
+
264
+ listener.published(publish_information)
265
+ end
266
+
267
+ return
268
+ end
269
+
270
+ def publish_temp_dir()
271
+ File.join(base_temp_dir(), 'publish')
272
+ end
273
+
274
+ def check_asset_path(asset_path)
275
+ if not File.exist?(asset_path)
276
+ Fig::Logging.fatal "Could not find file #{asset_path}."
277
+ raise Fig::RepositoryError.new
278
+ end
279
+
280
+ return
281
+ end
282
+
283
+ def check_asset_paths(asset_paths)
284
+ non_existing_paths =
285
+ asset_paths.select {|path| ! File.exist?(path) && ! File.symlink?(path) }
286
+
287
+ if not non_existing_paths.empty?
288
+ if non_existing_paths.size > 1
289
+ Fig::Logging.fatal "Could not find files: #{ non_existing_paths.join(', ') }"
290
+ else
291
+ Fig::Logging.fatal "Could not find file #{non_existing_paths[0]}."
292
+ end
293
+
294
+ raise Fig::RepositoryError.new
295
+ end
296
+
297
+ return
298
+ end
299
+
300
+ # 'resources' is an Array of fileglob patterns: ['tmp/foo/file1',
301
+ # 'tmp/foo/*.jar']
302
+ def expand_globs_from(resources)
303
+ expanded_files = []
304
+
305
+ resources.each do
306
+ |path|
307
+
308
+ globbed_files = Dir.glob(path)
309
+ if globbed_files.empty?
310
+ expanded_files << path
311
+ else
312
+ expanded_files.concat(globbed_files)
313
+ end
314
+ end
315
+
316
+ return expanded_files
317
+ end
318
+ end
@@ -0,0 +1,68 @@
1
+ require 'fileutils'
2
+
3
+ require 'fig/user_input_error'
4
+
5
+ module Fig; end
6
+
7
+ class Fig::UpdateLock
8
+ def initialize(lock_directory, response)
9
+ set_up_lock(lock_directory, response)
10
+
11
+ return
12
+ end
13
+
14
+ def close()
15
+ @lock.close
16
+
17
+ return
18
+ end
19
+
20
+ private
21
+
22
+ def set_up_lock(lock_directory, response)
23
+ FileUtils.mkdir_p(lock_directory)
24
+
25
+ # Tried using the directory itself as the lock, but Windows is
26
+ # non-cooperative.
27
+ lock_file = lock_directory + '/lock'
28
+
29
+ # Yes, there's a race condition here, but with the way Windows file locking
30
+ # works, it's better than a boot to the head.
31
+ if ! File.exists? lock_file
32
+ created_file = File.new(lock_file, 'w')
33
+ created_file.close
34
+ end
35
+
36
+ @lock = File.new(lock_file)
37
+
38
+ # *sigh* Ruby 1.8 doesn't support close_on_exec(), but we'll still use it
39
+ # if we can as a better attempt at safety.
40
+ if @lock.respond_to? :close_on_exec=
41
+ @lock.close_on_exec = true
42
+ end
43
+
44
+ if response == :wait
45
+ @lock.flock(File::LOCK_EX)
46
+ else
47
+ if ! @lock.flock(File::LOCK_EX | File::LOCK_NB)
48
+ raise_lock_usage_error(lock_directory)
49
+ end
50
+ end
51
+
52
+ return
53
+ end
54
+
55
+ def raise_lock_usage_error(lock_directory)
56
+ raise Fig::UserInputError.new(<<-END_MESSAGE)
57
+ Cannot update while another instance of Fig is updating #{lock_directory}.
58
+
59
+ You can tell Fig to wait for update with
60
+
61
+ fig --update --update-lock-response wait ...
62
+
63
+ or you can throw caution to the wind with
64
+
65
+ fig --update --update-lock-response ignore ...
66
+ END_MESSAGE
67
+ end
68
+ end
@@ -95,44 +95,78 @@ class Fig::WorkingDirectoryMaintainer
95
95
 
96
96
  def copy(source, relpath)
97
97
  target = File.join(@base_dir, relpath)
98
+
99
+ if source_and_target_are_same?(source, target)
100
+ # Actually happened: Retrieve and "set" both set to ".". Victim's current
101
+ # directory included a ".git" directory. Update was done and then later,
102
+ # an update with different dependencies. Fig proceeded to delete all
103
+ # files that had previously existed in the current directory, including
104
+ # out of the git repo. Whoops.
105
+ Fig::Logging.warn %Q<Skipping copying "#{source}" to itself.>
106
+ return
107
+ end
108
+
98
109
  if File.directory?(source)
99
- FileUtils.mkdir_p(target)
100
- Fig::Logging.debug "Copying directory #{source} to #{target}."
101
- Dir.foreach(source) do |child|
102
- if child != '.' and child != '..'
103
- source_file = File.join(source, child)
104
- target_file = File.join(relpath, child)
105
- copy(source_file, target_file)
106
- end
107
- end
110
+ copy_directory(source, relpath, target)
108
111
  else
109
- if should_copy_file?(source, target)
110
- if Fig::Logging.debug?
111
- Fig::Logging.debug \
112
- "Copying file from #{source} to #{target}."
113
- else
114
- Fig::Logging.info(
115
- Fig::Logging::Colorizable.new(
116
- "+ [#{formatted_meta()}] #{relpath}",
117
- :green,
118
- nil
119
- )
120
- )
121
- end
122
- FileUtils.mkdir_p(File.dirname(target))
112
+ copy_file(source, relpath, target)
113
+ end
123
114
 
124
- # If the source is a dangling symlink, then there's no time, etc. to
125
- # preserve.
126
- preserve = File.exist?(source) && ! File.symlink?(source)
115
+ return
116
+ end
127
117
 
128
- FileUtils.copy_entry(
129
- source, target, preserve, false, :remove_destination
130
- )
118
+ def source_and_target_are_same?(source, target)
119
+ # Ruby 1.8 doesn't have File.absolute_path(), so we have to fall back to
120
+ # .expand_path().
121
+ source_absolute = File.expand_path(source)
122
+ target_absolute = File.expand_path(target)
123
+
124
+ return source_absolute == target_absolute
125
+ end
126
+
127
+ def copy_directory(source, relpath, target)
128
+ FileUtils.mkdir_p(target)
129
+ Fig::Logging.debug "Copying directory #{source} to #{target}."
130
+
131
+ Dir.foreach(source) do |child|
132
+ if child != '.' and child != '..'
133
+ source_file = File.join(source, child)
134
+ target_file = File.join(relpath, child)
135
+ copy(source_file, target_file)
131
136
  end
132
- if @package_meta
133
- @package_meta.add_file(relpath)
134
- @package_meta.mark_as_retrieved()
137
+ end
138
+
139
+ return
140
+ end
141
+
142
+ def copy_file(source, relpath, target)
143
+ if should_copy_file?(source, target)
144
+ if Fig::Logging.debug?
145
+ Fig::Logging.debug \
146
+ "Copying file from #{source} to #{target}."
147
+ else
148
+ Fig::Logging.info(
149
+ Fig::Logging::Colorizable.new(
150
+ "+ [#{formatted_meta()}] #{relpath}",
151
+ :green,
152
+ nil
153
+ )
154
+ )
135
155
  end
156
+ FileUtils.mkdir_p(File.dirname(target))
157
+
158
+ # If the source is a dangling symlink, then there's no time, etc. to
159
+ # preserve.
160
+ preserve = File.exist?(source) && ! File.symlink?(source)
161
+
162
+ FileUtils.copy_entry(
163
+ source, target, preserve, false, :remove_destination
164
+ )
165
+ end
166
+
167
+ if @package_meta
168
+ @package_meta.add_file(relpath)
169
+ @package_meta.mark_as_retrieved()
136
170
  end
137
171
 
138
172
  return