vanagon 0.7.1 → 0.8.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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/lib/git/basic_submodules.rb +53 -0
  3. data/lib/vanagon/component.rb +19 -13
  4. data/lib/vanagon/component/dsl.rb +6 -5
  5. data/lib/vanagon/component/source.rb +80 -53
  6. data/lib/vanagon/component/source/git.rb +129 -20
  7. data/lib/vanagon/component/source/http.rb +41 -73
  8. data/lib/vanagon/component/source/local.rb +105 -62
  9. data/lib/vanagon/platform/deb.rb +8 -0
  10. data/lib/vanagon/platform/rpm.rb +4 -0
  11. data/lib/vanagon/project/dsl.rb +3 -1
  12. data/lib/vanagon/utilities.rb +11 -54
  13. data/spec/fixtures/files/fake_file_ext.7z +0 -0
  14. data/spec/fixtures/files/fake_file_ext.bz +0 -0
  15. data/spec/fixtures/files/fake_file_ext.bz2 +0 -0
  16. data/spec/fixtures/files/fake_file_ext.cpio +0 -0
  17. data/spec/fixtures/files/fake_file_ext.gz +0 -0
  18. data/spec/fixtures/files/fake_file_ext.rar +0 -0
  19. data/spec/fixtures/files/fake_file_ext.tar +0 -0
  20. data/spec/fixtures/files/fake_file_ext.tar.bz2 +0 -0
  21. data/spec/fixtures/files/fake_file_ext.tar.xz +0 -0
  22. data/spec/fixtures/files/fake_file_ext.tbz +0 -0
  23. data/spec/fixtures/files/fake_file_ext.tbz2 +0 -0
  24. data/spec/fixtures/files/fake_file_ext.txz +0 -0
  25. data/spec/fixtures/files/fake_file_ext.xz +0 -0
  26. data/spec/fixtures/files/fake_file_ext.z +0 -0
  27. data/spec/lib/vanagon/component/source/git_spec.rb +25 -17
  28. data/spec/lib/vanagon/component/source/http_spec.rb +2 -24
  29. data/spec/lib/vanagon/component/source/{localsource_spec.rb → local_spec.rb} +8 -8
  30. data/spec/lib/vanagon/component/source_spec.rb +150 -67
  31. data/spec/lib/vanagon/platform_spec.rb +40 -21
  32. data/spec/lib/vanagon/project/dsl_spec.rb +28 -3
  33. data/spec/lib/vanagon/utilities_spec.rb +0 -44
  34. metadata +28 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 02183e0db1fb5c4fa7a207d9a24c9c3f2e9c95c6
4
- data.tar.gz: 948b629fcfbf13083f409eacc6681fe7004eee29
3
+ metadata.gz: 9b41f01dda75f8d40767ea3c7e290cf11462ff94
4
+ data.tar.gz: 7c9b7ce98ff34e60371dbf087366cb0fd488c53d
5
5
  SHA512:
6
- metadata.gz: e283e4e4ae27a37619e59aa43c50037e2daae3263143e740885c247e10cb8fae7452f57132edb21b6fc718cc0172ceb02c718807767079618e343205afbdb208
7
- data.tar.gz: dd1da7f1081686fd628e518c258189239335c78f86cf8daf7ac50ad4e944135e6661f4faa61296435cc5efcc39b1625e3d8ce4223b2ea4811569779fadcf499a
6
+ metadata.gz: 5dda1a3fde0dfb600e38da84061bfd3f006b4f8a199e04900d067a4a96408affb102e916622d88b8fa0e04f6773f686bf5e2ef13aef108c5560d2c42e009df54
7
+ data.tar.gz: 4263fedb95452fefc118f7a199d70756455cb02c0fadd1c36b9ad630cdfd8d67d647c7a358c683295ca5cc0d2c53f2eca1a5a1ee4efde71b9d91f02a864118f5
@@ -0,0 +1,53 @@
1
+ require 'git'
2
+
3
+ module BasicSubmodulePrimitives
4
+ # Extend Git::Lib to support shotgunning submodules. This command
5
+ # is not smart, and it has very poor support for submodule options.
6
+ # For example, you can't pass any arguments to the handful of submodule
7
+ # options that accept them (like --depth). We may extend it later,
8
+ # but for rev. 0001, simply initializing them will suffice
9
+ def update_submodules(**options)
10
+ arr_opts = ['update']
11
+ options.each_pair do |k, v|
12
+ arr_opts << "--#{k}" if v
13
+ end
14
+ Dir.chdir(@git_work_dir) do
15
+ command('submodule', arr_opts)
16
+ end
17
+ end
18
+ end
19
+
20
+ module BasicSubmodules
21
+ # @example Initialize all git submodules
22
+ # >> repo = Git.clone("git@github.com:puppetlabs/facter.git", "facter", path: Dir.mktmpdir)
23
+ # => <Git::Base>
24
+ # >> repo.checkout "3.1.3
25
+ # => <String>
26
+ # >> repo.update_submodules(init: true)
27
+ # => <String>
28
+ # @param [Hash] options any options to pass to `git submodule update`
29
+ # @option options [Boolean] :init whether to initialize submodules when updating them
30
+ # @option options [Boolean] :use the submodule's remote-tracking branch instead of superproject's SHA1 sum
31
+ # @option options [Boolean] :no-fetch don't fetch new objects from the remote site.
32
+ # @option options [Boolean] :force remove submodule's working tree even if modified
33
+ # @option options [Boolean] :checkout checkout submodules in detached HEAD state
34
+ # @option options [Boolean] :merge merge recorded commit for submodule into the current branch of the submodule
35
+ # @option options [Boolean] :rebase rebase current branch of submodule onto the commit recorded in the superproject
36
+ # @option options [Boolean] :recursive recurse into nested submodules
37
+ # @return options [String] any output produced by `git` when submodules are initialized
38
+ def update_submodules(**options)
39
+ self.lib.update_submodules(options)
40
+ end
41
+ end
42
+
43
+ module Git
44
+ class Lib
45
+ include BasicSubmodulePrimitives
46
+ end
47
+ end
48
+
49
+ module Git
50
+ class Base
51
+ include BasicSubmodules
52
+ end
53
+ end
@@ -105,17 +105,20 @@ class Vanagon
105
105
  # makefile template
106
106
  #
107
107
  # @param workdir [String] working directory to put the source into
108
- def get_source(workdir)
109
- if @url
110
- @source = Vanagon::Component::Source.source(@url, @options, workdir)
111
- @source.fetch
112
- @source.verify
113
- @extract_with = @source.respond_to?(:extract) ? @source.extract(@platform.tar) : ':'
114
- @cleanup_source = @source.cleanup if @source.respond_to?(:cleanup)
115
- @dirname = @source.dirname
108
+ def get_source(workdir) # rubocop:disable Metrics/AbcSize
109
+ opts = options.merge({ workdir: workdir })
110
+ if url
111
+ @source = Vanagon::Component::Source.source(url, opts)
112
+ source.fetch
113
+ source.verify
114
+ @extract_with = source.respond_to?(:extract) ? source.extract(platform.tar) : ':'
115
+ @cleanup_source = source.cleanup if source.respond_to?(:cleanup)
116
+ @dirname = source.dirname
116
117
 
117
118
  # Git based sources probably won't set the version, so we load it if it hasn't been already set
118
- @version ||= @source.version
119
+ if source.respond_to?(:version)
120
+ @version ||= source.version
121
+ end
119
122
  else
120
123
  warn "No source given for component '#{@name}'"
121
124
 
@@ -140,10 +143,13 @@ class Vanagon
140
143
  #
141
144
  # @param workdir [String] working directory to put the source into
142
145
  def get_sources(workdir)
143
- @sources.each do |source|
144
- cur_source = Vanagon::Component::Source.source(source.url, { :ref => source.ref, :sum => source.sum }, workdir)
145
- cur_source.fetch
146
- cur_source.verify
146
+ sources.each do |source|
147
+ src = Vanagon::Component::Source.source source.url,
148
+ workdir: workdir,
149
+ ref: source.ref,
150
+ sum: source.sum
151
+ src.fetch
152
+ src.verify
147
153
  end
148
154
  end
149
155
 
@@ -310,11 +310,12 @@ class Vanagon
310
310
 
311
311
  # This will add a source to the project and put it in the workdir alongside the other sources
312
312
  #
313
- # @param url [String] url of the source
314
- # @param ref [String] Used for git sources, must be a git ref of some sort
315
- # @param sum [String] sum used to validate http and file sources
316
- def add_source(url, ref: nil, sum: nil)
317
- @component.sources << OpenStruct.new(:url => url, :ref => ref, :sum => sum)
313
+ # @param uri [String] uri of the source
314
+ # @param [Hash] options optional keyword arguments used to instatiate a new source
315
+ # @option opts [String] :sum
316
+ # @option opts [String] :ref
317
+ def add_source(uri, **options)
318
+ @component.sources << OpenStruct.new(options.merge({ url: uri }))
318
319
  end
319
320
 
320
321
  # Adds a directory to the list of directories provided by the project, to be included in any packages of the project
@@ -1,3 +1,4 @@
1
+ require 'fustigit'
1
2
  require 'vanagon/component/source/http'
2
3
  require 'vanagon/component/source/git'
3
4
  require 'vanagon/component/source/local'
@@ -5,74 +6,100 @@ require 'vanagon/component/source/local'
5
6
  class Vanagon
6
7
  class Component
7
8
  class Source
8
- SUPPORTED_PROTOCOLS = ['file', 'http', 'git'].freeze
9
- @@rewrite_rule = {}
9
+ SUPPORTED_PROTOCOLS = %w(file http https git).freeze
10
+ @rewrite_rules = {}
10
11
 
11
- def self.register_rewrite_rule(protocol, rule)
12
- if rule.is_a?(String) or rule.is_a?(Proc)
13
- if SUPPORTED_PROTOCOLS.include?(protocol)
14
- @@rewrite_rule[protocol] = rule
12
+ class << self
13
+ attr_reader :rewrite_rules
14
+
15
+ def register_rewrite_rule(protocol, rule)
16
+ if rule.is_a?(String) || rule.is_a?(Proc)
17
+ if SUPPORTED_PROTOCOLS.include?(protocol)
18
+ @rewrite_rules[protocol] = rule
19
+ else
20
+ raise Vanagon::Error, "#{protocol} is not a supported protocol for rewriting"
21
+ end
15
22
  else
16
- raise Vanagon::Error, "#{protocol} is not a supported protocol for rewriting"
23
+ raise Vanagon::Error, "String or Proc is required as a rewrite_rule."
17
24
  end
18
- else
19
- raise Vanagon::Error, "String or Proc is required as a rewrite_rule."
20
25
  end
21
- end
22
26
 
23
- def self.rewrite(url, protocol)
24
- rule = @@rewrite_rule[protocol]
27
+ def rewrite(url, protocol)
28
+ # Vanagon did not originally distinguish between http and https
29
+ # when looking up rewrite rules; this is no longer true, but it
30
+ # means that we should try to preserve old, dumb behavior until
31
+ # the rewrite engine is removed.
32
+ return rewrite(url, "http") if protocol == "https"
25
33
 
26
- if rule
27
- if rule.is_a?(Proc)
28
- return proc_rewrite(rule, url)
29
- elsif rule.is_a?(String)
30
- return string_rewrite(rule, url)
34
+ rule = @rewrite_rules[protocol]
35
+ if rule
36
+ if rule.is_a?(Proc)
37
+ return proc_rewrite(rule, url)
38
+ elsif rule.is_a?(String)
39
+ return string_rewrite(rule, url)
40
+ end
31
41
  end
42
+
43
+ return url
32
44
  end
33
45
 
34
- return url
35
- end
46
+ def proc_rewrite(rule, url)
47
+ if rule.arity == 1
48
+ rule.call(url)
49
+ else
50
+ raise Vanagon::Error, "Unable to use provided rewrite rule. Expected Proc with one argument, Proc has #{rule.arity} arguments"
51
+ end
52
+ end
36
53
 
37
- def self.proc_rewrite(rule, url)
38
- if rule.arity == 1
39
- rule.call(url)
40
- else
41
- raise Vanagon::Error, "Unable to use provided rewrite rule. Expected Proc with one argument, Proc has #{rule.arity} arguments"
54
+ def string_rewrite(rule, original_url)
55
+ url = original_url.to_s
56
+ target_match = url.match(/.*\/([^\/]*)$/)
57
+ if target_match
58
+ target = target_match[1]
59
+ return File.join(rule, target)
60
+ else
61
+ raise Vanagon::Error, "Unable to apply url rewrite to '#{url}', expected to find at least one '/' in the url."
62
+ end
42
63
  end
43
- end
44
64
 
45
- def self.string_rewrite(rule, url)
46
- target_match = url.match(/.*\/([^\/]*)$/)
47
- if target_match
48
- target = target_match[1]
49
- return File.join(rule, target)
50
- else
51
- raise Vanagon::Error, "Unable to apply url rewrite to '#{url}', expected to find at least one '/' in the url."
65
+ def parse_and_rewrite(uri)
66
+ url = URI.parse(uri)
67
+ return url unless url.scheme
68
+ rewrite(url.to_s, url.scheme)
52
69
  end
53
- end
54
70
 
55
- # Basic factory to hand back the correct {Vanagon::Component::Source} subtype to the component
56
- #
57
- # @param url [String] URL to the source (includes git@... style links)
58
- # @param options [Hash] hash of the options needed for the subtype
59
- # @param workdir [String] working directory to fetch the source into
60
- # @return [Vanagon::Component::Source] the correct subtype for the given source
61
- def self.source(url, options, workdir)
62
- url_match = url.match(/^(.*)(@|:\/\/)(.*)$/)
63
- uri_scheme = url_match[1] if url_match
64
- local_source = case uri_scheme
65
- when /^http/
66
- Vanagon::Component::Source::Http.new(self.rewrite(url, 'http'), options[:sum], workdir)
67
- when /^file/
68
- Vanagon::Component::Source::Local.new(self.rewrite(url, 'file'), workdir)
69
- when /^git/
70
- Vanagon::Component::Source::Git.new(self.rewrite(url, 'git'), options[:ref], workdir)
71
- else
72
- fail "Don't know how to handle source of type '#{uri_scheme}' from url: '#{url}'"
73
- end
71
+ # Basic factory to hand back the correct {Vanagon::Component::Source} subtype to the component
72
+ #
73
+ # @param url [String] URI of the source file (includes git@... style links)
74
+ # @param options [Hash] hash of the options needed for the subtype
75
+ # @param workdir [String] working directory to fetch the source into
76
+ # @return [Vanagon::Component::Source] the correct subtype for the given source
77
+ def source(uri, **options) # rubocop:disable Metrics/AbcSize
78
+ # First we try git
79
+ if Vanagon::Component::Source::Git.valid_remote?(parse_and_rewrite(uri))
80
+ return Vanagon::Component::Source::Git.new parse_and_rewrite(uri),
81
+ sum: options[:sum],
82
+ ref: options[:ref],
83
+ workdir: options[:workdir]
84
+ end
85
+
86
+ # Then we try HTTP
87
+ if Vanagon::Component::Source::Http.valid_url?(parse_and_rewrite(uri))
88
+ return Vanagon::Component::Source::Http.new parse_and_rewrite(uri),
89
+ sum: options[:sum],
90
+ workdir: options[:workdir]
91
+ end
92
+
93
+ # Then we try local
94
+ if Vanagon::Component::Source::Local.valid_file?(uri)
95
+ return Vanagon::Component::Source::Local.new uri,
96
+ workdir: options[:workdir]
97
+ end
74
98
 
75
- return local_source
99
+ # Failing all of that, we give up
100
+ raise Vanagon::Error,
101
+ "Unknown file type: '#{uri}'; cannot continue"
102
+ end
76
103
  end
77
104
  end
78
105
  end
@@ -1,39 +1,69 @@
1
1
  require 'vanagon/utilities'
2
+ require 'vanagon/errors'
3
+ # This stupid library requires a capital 'E' in its name
4
+ # but it provides a wealth of useful constants
5
+ require 'English'
6
+ require 'fustigit'
7
+ require 'git/basic_submodules'
8
+ require 'logger'
2
9
 
3
10
  class Vanagon
4
11
  class Component
5
12
  class Source
6
13
  class Git
7
- include Vanagon::Utilities
8
- attr_accessor :url, :ref, :workdir, :version, :cleanup
14
+ attr_accessor :url, :ref, :workdir
15
+ attr_reader :version, :default_options, :repo
16
+
17
+ class << self
18
+ # Attempt to connect to whatever URL is provided and
19
+ # return True or False depending on whether or not
20
+ # `git` thinks it's a valid Git repo.
21
+ #
22
+ # @return [Boolean] whether #url is a valid Git repo or not
23
+ def valid_remote?(url)
24
+ !!::Git.ls_remote(url)
25
+ rescue ::Git::GitExecuteError
26
+ false
27
+ end
28
+ end
29
+
30
+ # Default options used when cloning; this may expand
31
+ # or change over time.
32
+ def default_options
33
+ @default_options ||= { ref: "refs/heads/master" }
34
+ end
35
+ private :default_options
9
36
 
10
37
  # Constructor for the Git source type
11
38
  #
12
39
  # @param url [String] url of git repo to use as source
13
40
  # @param ref [String] ref to checkout from git repo
14
41
  # @param workdir [String] working directory to clone into
15
- def initialize(url, ref, workdir)
16
- unless ref
17
- fail "ref parameter is required for the git source"
18
- end
19
- @url = url
20
- @ref = ref
42
+ def initialize(url, workdir:, **options)
43
+ opts = default_options.merge(options)
44
+
45
+ # Ensure that #url returns a URI object
46
+ @url = URI.parse(url.to_s)
47
+ @ref = opts[:ref]
21
48
  @workdir = workdir
49
+ @ref_name, @ref_type, = @ref.split('/', 3).reverse
50
+
51
+ # We can test for Repo existence without cloning
52
+ raise Vanagon::InvalidRepo, "#{url} not a valid Git repo" unless valid_remote?
22
53
  end
23
54
 
24
55
  # Fetch the source. In this case, clone the repository into the workdir
25
56
  # and check out the ref. Also sets the version if there is a git tag as
26
57
  # a side effect.
27
58
  def fetch
28
- puts "Cloning ref '#{@ref}' from url '#{@url}'"
29
- Dir.chdir(@workdir) do
30
- git("clone #{@url}", true)
31
- Dir.chdir(dirname) do
32
- git("checkout #{@ref}", true)
33
- git("submodule update --init --recursive", true)
34
- @version = git_version
35
- end
36
- end
59
+ clone!
60
+ checkout!
61
+ version
62
+ update_submodules
63
+ end
64
+
65
+ def ref
66
+ @ref_name || @ref
37
67
  end
38
68
 
39
69
  # Return the correct incantation to cleanup the source directory for a given source
@@ -43,18 +73,97 @@ class Vanagon
43
73
  "rm -rf #{dirname}"
44
74
  end
45
75
 
46
- # There is no md5 to manually verify here, so it is a noop.
76
+ # There is no md5 to manually verify here, so this is a noop.
47
77
  def verify
48
- # nothing to do here, so just return
78
+ # nothing to do here, so just tell users that and return
79
+ puts "Nothing to verify for '#{dirname}' (using Git reference '#{ref}')"
49
80
  end
50
81
 
51
82
  # The dirname to reference when building from the repo
52
83
  #
53
84
  # @return [String] the directory where the repo was cloned
54
85
  def dirname
55
- File.basename(@url).sub(/\.git/, '')
86
+ File.basename(url.path, ".git")
87
+ end
88
+
89
+ # Use `git describe` to lazy-load a version for this component
90
+ def version
91
+ @version ||= describe
92
+ end
93
+
94
+ # Perform a git clone of @url as a lazy-loaded
95
+ # accessor for @clone
96
+ def clone
97
+ @clone ||= ::Git.clone(url, dirname, path: workdir)
98
+ end
99
+
100
+ # Attempt to connect to whatever URL is provided and
101
+ # return True or False depending on whether or not
102
+ # `git` thinks it's a valid Git repo.
103
+ #
104
+ # @return [Boolean] whether #url is a valid Git repo or not
105
+ def valid_remote?
106
+ self.class.valid_remote? url
107
+ end
108
+ private :valid_remote?
109
+
110
+ # Provide a list of remote refs (branches and tags)
111
+ def remote_refs
112
+ (remote['tags'].keys + remote['branches'].keys).uniq
113
+ end
114
+ private :remote_refs
115
+
116
+ # Provide a list of local refs (branches and tags)
117
+ def refs
118
+ (clone.tags.map(&:name) + clone.branches.map(&:name)).uniq
119
+ end
120
+ private :refs
121
+
122
+ # Clone a remote repo, make noise about it, and fail entirely
123
+ # if we're unable to retrieve the remote repo
124
+ def clone!
125
+ puts "Cloning Git repo '#{url}'"
126
+ puts "Successfully cloned '#{dirname}'" if clone
127
+ rescue Git::GitExecuteError
128
+ raise Vanagon::InvalidRepo, "Unable to clone from '#{url}'"
56
129
  end
130
+ private :clone!
131
+
132
+ # Checkout desired ref/sha, make noise about it, and fail
133
+ # entirely if we're unable to checkout that given ref/sha
134
+ def checkout!
135
+ puts "Checking out '#{ref}'' from Git repo '#{dirname}'"
136
+ clone.checkout(ref)
137
+ rescue ::Git::GitExecuteError
138
+ raise Vanagon::CheckoutFailed, "unable to checkout #{ref} from '#{url}'"
139
+ end
140
+ private :checkout!
141
+
142
+ # Attempt to update submodules, and do not panic
143
+ # if there are no submodules to initialize
144
+ def update_submodules
145
+ puts "Attempting to update submodules for repo '#{dirname}'"
146
+ clone.update_submodules(init: true)
147
+ end
148
+ private :update_submodules
149
+
150
+ # Determines a version for the given directory based on the git describe
151
+ # for the repository
152
+ #
153
+ # @return [String] The version of the directory according to git describe
154
+ def describe
155
+ clone.describe(ref, tags: true)
156
+ rescue ::Git::GitExecuteError
157
+ warn "Directory '#{dirname}' cannot be versioned by Git. Maybe it hasn't been tagged yet?"
158
+ end
159
+ private :describe
57
160
  end
58
161
  end
59
162
  end
163
+
164
+ class GitError < Error; end
165
+ # Raised when a URI is not a valid Source Control repo
166
+ class InvalidRepo < GitError; end
167
+ # Raised when checking out a given ref from a Git Repo fails
168
+ class CheckoutFailed < GitError; end
60
169
  end