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.
- checksums.yaml +9 -9
- data/.travis.yml +2 -2
- data/CHANGELOG.md +10 -0
- data/Gemfile +5 -12
- data/README.md +27 -1
- data/Rakefile +2 -8
- data/features/chef_cookbook.feature +4 -4
- data/features/download.feature +1 -1
- data/features/edgecases.feature +15 -0
- data/features/environment.feature +3 -2
- data/features/fake_mode.feature +15 -0
- data/features/git.feature +4 -4
- data/features/overlay.feature +99 -0
- data/features/step_definitions/basic.rb +22 -0
- data/features/step_definitions/git.rb +16 -0
- data/features/step_definitions/vendorificator.rb +9 -4
- data/features/support/aruba_ext.rb +4 -0
- data/features/support/env.rb +3 -0
- data/features/tarball.feature +2 -2
- data/features/tarball_edit.feature +3 -3
- data/features/tool.feature +6 -4
- data/features/tool_shortcuts.feature +3 -3
- data/features/tool_specs.feature +62 -0
- data/features/vendor.feature +4 -3
- data/lib/vendorificator.rb +7 -1
- data/lib/vendorificator/cli.rb +12 -11
- data/lib/vendorificator/config.rb +32 -1
- data/lib/vendorificator/environment.rb +23 -27
- data/lib/vendorificator/hooks/chef_cookbook.rb +2 -2
- data/lib/vendorificator/overlay.rb +17 -0
- data/lib/vendorificator/segment.rb +376 -0
- data/lib/vendorificator/segment/overlay.rb +114 -0
- data/lib/vendorificator/segment/vendor.rb +115 -0
- data/lib/vendorificator/vendor.rb +25 -279
- data/lib/vendorificator/vendor/tool.rb +40 -23
- data/lib/vendorificator/version.rb +1 -1
- data/spec/vendorificator/config_spec.rb +66 -0
- data/spec/vendorificator/environment_spec.rb +7 -7
- data/spec/vendorificator/fixtures/vendorfiles/overlay.rb +4 -0
- data/spec/vendorificator/segment/vendor_spec.rb +19 -0
- data/spec/vendorificator/segment_spec.rb +106 -0
- data/spec/vendorificator/vendor_spec.rb +0 -89
- data/vendorificator.gemspec +5 -5
- 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(
|
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.
|
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
|
+
|