vendorificator 0.5.git.v0.4.0.63.g8e9d54d → 0.5.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 (44) hide show
  1. checksums.yaml +9 -9
  2. data/.travis.yml +2 -2
  3. data/CHANGELOG.md +10 -0
  4. data/Gemfile +5 -12
  5. data/README.md +27 -1
  6. data/Rakefile +2 -8
  7. data/features/chef_cookbook.feature +4 -4
  8. data/features/download.feature +1 -1
  9. data/features/edgecases.feature +15 -0
  10. data/features/environment.feature +3 -2
  11. data/features/fake_mode.feature +15 -0
  12. data/features/git.feature +4 -4
  13. data/features/overlay.feature +99 -0
  14. data/features/step_definitions/basic.rb +22 -0
  15. data/features/step_definitions/git.rb +16 -0
  16. data/features/step_definitions/vendorificator.rb +9 -4
  17. data/features/support/aruba_ext.rb +4 -0
  18. data/features/support/env.rb +3 -0
  19. data/features/tarball.feature +2 -2
  20. data/features/tarball_edit.feature +3 -3
  21. data/features/tool.feature +6 -4
  22. data/features/tool_shortcuts.feature +3 -3
  23. data/features/tool_specs.feature +62 -0
  24. data/features/vendor.feature +4 -3
  25. data/lib/vendorificator.rb +7 -1
  26. data/lib/vendorificator/cli.rb +12 -11
  27. data/lib/vendorificator/config.rb +32 -1
  28. data/lib/vendorificator/environment.rb +23 -27
  29. data/lib/vendorificator/hooks/chef_cookbook.rb +2 -2
  30. data/lib/vendorificator/overlay.rb +17 -0
  31. data/lib/vendorificator/segment.rb +376 -0
  32. data/lib/vendorificator/segment/overlay.rb +114 -0
  33. data/lib/vendorificator/segment/vendor.rb +115 -0
  34. data/lib/vendorificator/vendor.rb +25 -279
  35. data/lib/vendorificator/vendor/tool.rb +40 -23
  36. data/lib/vendorificator/version.rb +1 -1
  37. data/spec/vendorificator/config_spec.rb +66 -0
  38. data/spec/vendorificator/environment_spec.rb +7 -7
  39. data/spec/vendorificator/fixtures/vendorfiles/overlay.rb +4 -0
  40. data/spec/vendorificator/segment/vendor_spec.rb +19 -0
  41. data/spec/vendorificator/segment_spec.rb +106 -0
  42. data/spec/vendorificator/vendor_spec.rb +0 -89
  43. data/vendorificator.gemspec +5 -5
  44. metadata +45 -29
@@ -23,7 +23,7 @@ module Vendorificator::Hooks
23
23
  environment.config[:chef_cookbook_ignore_dependencies]
24
24
 
25
25
  if !ign || ign.respond_to?(:include?)
26
- metadata = File.join(self.work_dir, 'metadata.rb')
26
+ metadata = File.join(work_dir, 'metadata.rb')
27
27
 
28
28
  unless File.exist?(metadata)
29
29
  say_status :quiet, 'WARNING', "Metadata of #{name} does not exist at #{metadata}, could not gather dependencies", :red
@@ -44,7 +44,7 @@ module Vendorificator::Hooks
44
44
  # Reject dependencies that already have a module
45
45
  deps.reject! do |dep|
46
46
  dir = basedir.join(dep).to_s
47
- environment.vendor_instances.any? do |vi|
47
+ environment.segments.any? do |vi|
48
48
  vi.work_dir == dir
49
49
  end
50
50
  end
@@ -0,0 +1,17 @@
1
+ module Vendorificator
2
+ class Overlay
3
+ attr_reader :path, :name, :segments
4
+
5
+ def initialize(options = {})
6
+ @name = strip_leading_slash(options[:name])
7
+ @path = options[:path] ? strip_leading_slash(options[:path]) : @name
8
+ @segments = []
9
+ end
10
+
11
+ private
12
+
13
+ def strip_leading_slash(string)
14
+ (result = string.gsub(/\A\//, '')) != '' ? result : nil
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,376 @@
1
+ module Vendorificator
2
+ class Segment
3
+
4
+ def initialize(args = {})
5
+ @metadata = {}
6
+ end
7
+
8
+ def fast_forward(branch)
9
+ in_branch { |tmpgit| tmpgit.merge({:ff_only => true}, branch) }
10
+ end
11
+
12
+ def status
13
+ # If there's no branch yet, it's a completely new module
14
+ return :new unless head
15
+
16
+ # If there's a branch but no tag, it's a known module that's not
17
+ # been updated for the new definition yet.
18
+ return :outdated unless tagged_sha1
19
+
20
+ # Well, this is awkward: branch is in config and exists, but is
21
+ # not merged into current branch at all.
22
+ return :unmerged unless merged?
23
+
24
+ # Merge base is tagged with our tag. We're good.
25
+ return :up_to_date if tagged_sha1 == merged_base
26
+
27
+ return :unpulled if environment.fast_forwardable?(tagged_sha1, merged_base)
28
+
29
+ return :unknown
30
+ end
31
+
32
+ def run!(options = {})
33
+ say_status :default, :module, name
34
+ indent do
35
+ case status
36
+
37
+ when :up_to_date
38
+ say_status :default, 'up to date', to_s
39
+
40
+ when :unpulled, :unmerged
41
+ say_status :default, 'merging', to_s, :yellow
42
+ merge_back tagged_sha1
43
+
44
+ when :outdated, :new
45
+ say_status :default, 'fetching', to_s, :yellow
46
+ update options
47
+
48
+ else
49
+ say_status :quiet, self.status, "I'm unsure what to do.", :red
50
+ end
51
+ end
52
+ end
53
+
54
+ def pushable_refs
55
+ created_tags.unshift("refs/heads/#{branch_name}")
56
+ end
57
+
58
+ def work_dir(relative = false)
59
+ arr = relative ? [] : [git.git_work_tree]
60
+ arr << environment.relative_root_dir
61
+ arr << work_subdir
62
+
63
+ _join *arr
64
+ end
65
+
66
+ def included_in_list?(module_list)
67
+ modpaths = module_list.map { |m| File.expand_path(m) }
68
+
69
+ module_list.include?(name) ||
70
+ module_list.include?("#{group}/#{name}") ||
71
+ modpaths.include?(File.expand_path(work_dir)) ||
72
+ module_list.include?(merged_base) ||
73
+ module_list.include?(branch_name)
74
+ end
75
+
76
+ def updatable?
77
+ return nil if self.status == :up_to_date
78
+ return false if !head
79
+ return false if head && merged_base == head
80
+ git.describe({:abbrev => 0, :always => true}, branch_name)
81
+ end
82
+
83
+ def to_s
84
+ _join name, version
85
+ end
86
+
87
+ # Public: Get git vendor notes of the merged commit.
88
+ #
89
+ # Returns the Hash of git vendor notes.
90
+ def merged_notes
91
+ Commit.new(merged_base, git).notes?
92
+ end
93
+
94
+ def merged_version
95
+ merged_tag && merged_tag[(1 + tag_name_base.length)..-1]
96
+ end
97
+
98
+ private
99
+
100
+ def config
101
+ environment.config
102
+ end
103
+
104
+ # Private: Commits and annotates the conjured module.
105
+ #
106
+ # environment_metadata - Hash with environment metadata where vendor was run
107
+ #
108
+ # Returns nothing.
109
+ def commit_and_annotate(environment_metadata = {})
110
+ return if config.fake_mode?
111
+
112
+ git.capturing.add work_dir, *@vendor.git_add_extra_paths
113
+ git.capturing.commit :m => @vendor.conjure_commit_message
114
+ git.capturing.notes({:ref => 'vendor'}, 'add', {:m => conjure_note(environment_metadata)}, 'HEAD')
115
+ git.capturing.tag( { :a => true, :m => tag_message }, tag_name )
116
+ say_status :default, :tag, tag_name
117
+ end
118
+
119
+ # Private: Merges all the data we use for the commit note.
120
+ #
121
+ # environment_metadata - Hash with environment metadata where vendor was run
122
+ #
123
+ # Returns: The note in the YAML format.
124
+ def conjure_note(environment_metadata = {})
125
+ config.metadata.
126
+ merge(environment_metadata).
127
+ merge(metadata).
128
+ merge(@vendor.metadata).
129
+ to_yaml
130
+ end
131
+
132
+ def head
133
+ git.capturing.rev_parse({:verify => true, :quiet => true}, "refs/heads/#{branch_name}").strip
134
+ rescue MiniGit::GitError
135
+ nil
136
+ end
137
+
138
+ def in_branch(branch = branch_name, options = {}, &block)
139
+ Dir.mktmpdir do |tmpdir|
140
+ tmpgit = create_temp_git_repo(branch, options, tmpdir)
141
+ fetch_repo_data tmpgit
142
+ repo_cleanup tmpgit if options[:clean] || !branch_exists?(branch)
143
+
144
+ Dir.chdir(tmpdir){ yield tmpgit }
145
+
146
+ propagate_repo_data_to_original branch, tmpdir
147
+ end
148
+ end
149
+
150
+ def create_temp_git_repo(branch, options, dir)
151
+ clone_opts = {shared: true, no_checkout: true}
152
+ clone_opts[:branch] = branch if branch_exists? branch
153
+ say { MiniGit::Capturing.git :clone, clone_opts, git.git_dir, dir }
154
+
155
+ tmpgit = MiniGit.new(dir)
156
+ unless branch_exists? branch
157
+ say { tmpgit.capturing.checkout({orphan: true}, branch) }
158
+ end
159
+
160
+ tmpgit
161
+ end
162
+
163
+ def repo_cleanup(tmpgit)
164
+ tmpgit.rm({ :r => true, :f => true, :q => true, :ignore_unmatch => true }, '.')
165
+ end
166
+
167
+ def fetch_repo_data(tmpgit)
168
+ tmpgit.fetch git.git_dir, "refs/notes/vendor:refs/notes/vendor" if notes_exist?
169
+ end
170
+
171
+ def propagate_repo_data_to_original(branch, clone_dir)
172
+ if config.fake_mode?
173
+ add_path_to_git_exclude
174
+ copy_back_from_temporary_clone(clone_dir)
175
+ else
176
+ fetch_back_from_temporary_clone(branch, clone_dir)
177
+ end
178
+ end
179
+
180
+ # Private: Fetches the branches from the temporary clone in the main repo,
181
+ # to get the conjured data.
182
+ #
183
+ # branch - branch name to fetch
184
+ # clone_dir - path to the local temporary clone
185
+ #
186
+ # Returns nothing.
187
+ def fetch_back_from_temporary_clone(branch, clone_dir)
188
+ git.fetch clone_dir
189
+ git.fetch({tags: true}, clone_dir)
190
+ git.fetch clone_dir,
191
+ "refs/heads/#{branch}:refs/heads/#{branch}",
192
+ "refs/notes/vendor:refs/notes/vendor"
193
+ end
194
+
195
+ # Private: Copies the conjured vendor files back to main repo, instead of
196
+ # just fetching the branches. Used in fake development mode.
197
+ #
198
+ # clone_dir - path to the local temporary clone
199
+ #
200
+ # Returns nothing.
201
+ def copy_back_from_temporary_clone(clone_dir)
202
+ FileUtils.mkdir_p work_dir
203
+ FileUtils.cp_r clone_dir, work_dir
204
+ end
205
+
206
+ # Private: adds conjured directory path to git exclude file. Used in fake
207
+ # mode.
208
+ #
209
+ # Returns nothing.
210
+ def add_path_to_git_exclude
211
+ return if check_if_work_dir_excluded
212
+
213
+ if File.exists? work_dir
214
+ say_status(:quiet, 'FATAL', "Directory #{work_dir(true).inspect} already exists. Aborting.", :red)
215
+ exit
216
+ end
217
+ File.open('.git/info/exclude', 'a') { |f| f.puts work_dir(true) }
218
+ end
219
+
220
+ # Private: Checks if segment work_dir has already been excluded.
221
+ #
222
+ # Returns true/false.
223
+ def check_if_work_dir_excluded
224
+ File.open('.git/info/exclude').each_line.any? do |line|
225
+ line =="#{work_dir(true)}\n"
226
+ end
227
+ end
228
+
229
+ def in_work_dir
230
+ FileUtils::mkdir_p work_dir
231
+
232
+ Dir::chdir work_dir do
233
+ begin
234
+ shell.padding += 1
235
+ yield
236
+ ensure
237
+ shell.padding -= 1
238
+ end
239
+ end
240
+ end
241
+
242
+ def notes_exist?
243
+ git.capturing.rev_parse({verify: true, quiet: true}, 'refs/notes/vendor')
244
+ true
245
+ rescue MiniGit::GitError
246
+ false
247
+ end
248
+
249
+ def metadata
250
+ default = {
251
+ }
252
+ default.merge @metadata
253
+ end
254
+
255
+ def name
256
+ raise NotImplementedError
257
+ end
258
+
259
+ def version
260
+ raise NotImplementedError
261
+ end
262
+
263
+ def tag_message
264
+ @vendor.conjure_commit_message
265
+ end
266
+
267
+ def _join(*parts)
268
+ parts.compact.map(&:to_s).join('/')
269
+ end
270
+
271
+ def git
272
+ @git || environment.git
273
+ end
274
+
275
+ def make_subdir_root(subdir_path)
276
+ curdir = Pathname.pwd
277
+ tmpdir = Pathname.pwd.dirname.join("#{Pathname.pwd.basename}.tmp")
278
+ subdir = Pathname.pwd.join(subdir_path)
279
+
280
+ Dir.chdir('..')
281
+
282
+ subdir.rename(tmpdir.to_s)
283
+ curdir.rmtree
284
+ tmpdir.rename(curdir.to_s)
285
+ ensure
286
+ Dir.chdir(curdir.to_s) if curdir.exist?
287
+ end
288
+
289
+ def created_tags
290
+ git.capturing.show_ref.lines.map{ |line| line.split(' ')[1] }.
291
+ select{ |ref| ref =~ /\Arefs\/tags\/#{tag_name_base}\// }
292
+ end
293
+
294
+ def tagged_sha1
295
+ @tagged_sha1 ||= git.capturing.rev_parse(
296
+ {:verify => true, :quiet => true}, "refs/tags/#{tag_name}^{commit}"
297
+ ).strip
298
+ rescue MiniGit::GitError
299
+ nil
300
+ end
301
+
302
+ def group
303
+ nil
304
+ end
305
+
306
+ def branch_name
307
+ _join(config[:branch_prefix], group, name)
308
+ end
309
+
310
+ def tag_name
311
+ _join(tag_name_base, version)
312
+ end
313
+
314
+ def tag_name_base
315
+ branch_name
316
+ end
317
+
318
+ def merged_base
319
+ return @merged_base if defined? @merged_base
320
+ base = git.capturing.merge_base(head, 'HEAD').strip
321
+ @merged_base = base.empty? ? nil : base
322
+ rescue MiniGit::GitError
323
+ @merged_base = nil
324
+ end
325
+
326
+ def merged?
327
+ !merged_base.nil?
328
+ end
329
+
330
+ def merged_tag
331
+ return @merged_tag if defined? @merged_tag
332
+ @merged_tag = if merged?
333
+ tag = git.capturing.describe( {
334
+ :exact_match => true,
335
+ :match => _join(tag_name_base, '*') },
336
+ merged_base).strip
337
+ tag.empty? ? nil : tag
338
+ else
339
+ nil
340
+ end
341
+ end
342
+
343
+ # Private: Checks whether a particular branch exists.
344
+ #
345
+ # branch - name of the branch to check, default to segment branch
346
+ #
347
+ # Returns true/false.
348
+ def branch_exists?(branch = branch_name)
349
+ git.capturing.rev_parse({:verify => true}, "refs/heads/#{branch}")
350
+ true
351
+ rescue MiniGit::GitError
352
+ false
353
+ end
354
+
355
+ def shell
356
+ environment.shell
357
+ end
358
+
359
+ def say(verb_level= :default, &block)
360
+ output = yield
361
+ environment.say verb_level, output
362
+ end
363
+
364
+ def say_status(*args, &block)
365
+ environment.say_status(*args, &block)
366
+ end
367
+
368
+ def indent(verb_level = :default, *args, &block)
369
+ say_status verb_level, *args unless args.empty?
370
+ shell.padding += 1 if shell
371
+ yield
372
+ ensure
373
+ shell.padding -= 1 if shell
374
+ end
375
+ end
376
+ end
@@ -0,0 +1,114 @@
1
+ module Vendorificator
2
+ class Segment::Overlay < Segment
3
+ attr_reader :overlay, :segments, :environment
4
+
5
+ def initialize(options)
6
+ @overlay = ::Vendorificator::Overlay.new(options[:overlay_opts])
7
+ @environment = options[:environment]
8
+ @segments = []
9
+ super
10
+ end
11
+
12
+ def name
13
+ "Overlay \"#{overlay.path}\""
14
+ end
15
+
16
+ def base_branch_name
17
+ _join config[:branch_prefix], 'overlay', (overlay.name || overlay.path)
18
+ end
19
+
20
+ def branch_name
21
+ _join base_branch_name, 'layer'
22
+ end
23
+
24
+ def merge_branch_name
25
+ _join base_branch_name, 'merged'
26
+ end
27
+
28
+ def compute_dependencies!
29
+ each_segment { |seg| seg.compute_dependencies! }
30
+ end
31
+
32
+ # Public: Goes through all the Vendor instances and runs the block
33
+ #
34
+ # segments - An Array of vendor segments to yield the block for.
35
+ #
36
+ # Returns nothing.
37
+ def each_segment(*segments)
38
+ # We don't use @segments.each here, because Vendor#run! is
39
+ # explicitly allowed to append to instantiate new dependencies, and #each
40
+ # fails to catch up on some Ruby implementations.
41
+ i = 0
42
+ while true
43
+ break if i >= @segments.length
44
+ seg = @segments[i]
45
+ yield seg if segments.empty? || seg.included_in_list?(segments)
46
+ i += 1
47
+ end
48
+ end
49
+
50
+ def group
51
+ nil
52
+ end
53
+
54
+ def version
55
+ nil
56
+ end
57
+
58
+ def path
59
+ _join overlay.path
60
+ end
61
+
62
+ private
63
+
64
+ def merge_back
65
+ unless config.fake_mode?
66
+ in_branch merge_branch_name do |git|
67
+ each_segment do |seg|
68
+ git.capturing.merge({:no_edit => true, :no_ff => true}, seg.branch_name)
69
+ end
70
+ end
71
+ git.capturing.merge({:no_edit => true, :no_ff => true}, merge_branch_name)
72
+ end
73
+
74
+ each_segment do |seg|
75
+ seg.vendor.postprocess! if seg.vendor.respond_to? :postprocess!
76
+ seg.vendor.compute_dependencies!
77
+ end
78
+ end
79
+
80
+ def update(options = {})
81
+ shell.padding += 1
82
+ each_segment do |seg|
83
+ seg.conjure options
84
+ end
85
+
86
+ merge_back
87
+ ensure
88
+ shell.padding -= 1
89
+ end
90
+
91
+ def work_subdir
92
+ _join path
93
+ end
94
+
95
+ def fetch_repo_data(tmpgit)
96
+ super
97
+ each_segment do |seg|
98
+ tmpgit.fetch git.git_dir,
99
+ "refs/heads/#{seg.branch_name}:refs/heads/#{seg.branch_name}"
100
+ end
101
+ end
102
+
103
+ def create_temp_git_repo(branch, options, dir)
104
+ tmpgit = super
105
+ unless branch_exists? branch
106
+ tmpgit.capturing.commit allow_empty: true, message: 'Empty init'
107
+ end
108
+
109
+ tmpgit
110
+ end
111
+
112
+ end
113
+ end
114
+