fpm-fry 0.2.2 → 0.4.6

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.
@@ -4,32 +4,67 @@ require 'net/http'
4
4
  require 'forwardable'
5
5
  require 'zlib'
6
6
  require 'fpm/fry/source'
7
+ require 'fpm/fry/exec'
7
8
  require 'cabin'
9
+ require 'cabin/channel'
10
+
8
11
  module FPM; module Fry ; module Source
9
- class Package
12
+ # Used to build from an archive.
13
+ #
14
+ # @example in a recipe
15
+ # source 'http://curl.haxx.se/download/curl-7.36.0.tar.gz',
16
+ # checksum: '33015795d5650a2bfdd9a4a28ce4317cef944722a5cfca0d1563db8479840e90'
17
+ #
18
+ # It is highly advised to supply a checksum ( althought it's not mandatory ).
19
+ # This checksum will be used to test for cache validity and data integrity. The
20
+ # checksum algorithm is automatically guessed based on the length of the checksum.
21
+ #
22
+ # - 40 characters = sha1
23
+ # - 64 characters = sha256
24
+ #
25
+ # Let's be honest: all other checksum algorithms aren't or shouldn't be in use anyway.
26
+ class Archive
10
27
 
11
28
  REGEX = %r!\Ahttps?:!
12
29
 
30
+ # @return [:archive]
13
31
  def self.name
14
32
  :package
15
33
  end
16
34
 
17
35
  def self.aliases
18
- [:http]
36
+ [:package,:http]
19
37
  end
20
38
 
39
+ # Guesses if the given url is an archive.
40
+ #
41
+ # @example not an archive
42
+ # FPM::Fry::Source::Archive.guess("bzr://something") # => nil
43
+ #
44
+ # @example an archive
45
+ # FPM::Fry::Source::Archive.guess("https://some/thing.tar.gz") #=> 6
46
+ #
47
+ # @return [nil] when it's not an archive
48
+ # @return [Numeric] number of characters used
21
49
  def self.guess( url )
22
50
  Source::guess_regex(REGEX, url)
23
51
  end
24
52
 
53
+ # Raised when too many redirects happened.
25
54
  class RedirectError < CacheFailed
26
55
  end
27
56
 
57
+ # Raised when the archive type is not known.
58
+ class UnknownArchiveType < StandardError
59
+ include WithData
60
+ end
61
+
28
62
  class Cache < Struct.new(:package,:tempdir)
29
63
  extend Forwardable
30
64
 
31
- def_delegators :package, :url, :checksum, :checksum_algorithm, :agent, :logger, :file_map
65
+ def_delegators :package, :url, :checksum, :checksum_algorithm, :logger, :file_map, :to
32
66
 
67
+ # @return [String] cachekey which is equal to the checksum
33
68
  def cachekey
34
69
  @observed_checksum || checksum
35
70
  end
@@ -92,7 +127,7 @@ module FPM; module Fry ; module Source
92
127
  case(resp)
93
128
  when Net::HTTPRedirection
94
129
  if redirs == 0
95
- raise RedirectError, "Too many redirects"
130
+ raise RedirectError.new("Too many redirects", url: url.to_s, location: resp['location'])
96
131
  end
97
132
  logger.debug("Following redirect", url: url.to_s , location: resp['location'])
98
133
  return fetch_url( resp['location'], redirs - 1, &block)
@@ -119,9 +154,33 @@ module FPM; module Fry ; module Source
119
154
 
120
155
  def copy_to(dst)
121
156
  update!
122
- cmd = ['tar','-xf',tempfile,'-C',dst]
123
- logger.debug("Running tar",cmd: cmd)
124
- system(*cmd)
157
+ Exec['tar','-xf',tempfile,'-C',dst, logger: logger]
158
+ end
159
+
160
+ def prefix
161
+ update!
162
+ @prefix ||= prefix!
163
+ end
164
+
165
+ def prefix!
166
+ longest = nil
167
+ Exec.popen('tar','-tf',tempfile, logger: logger).each_line.map do |line|
168
+ line = line.chomp
169
+ parts = line.split('/')
170
+ parts.pop unless line[-1] == '/'
171
+ if longest.nil?
172
+ longest = parts
173
+ else
174
+ longest.each_with_index do | e, i |
175
+ if parts[i] != e
176
+ longest = longest[0...i]
177
+ break
178
+ end
179
+ end
180
+ break if longest.none?
181
+ end
182
+ end
183
+ return Array(longest).join('/')
125
184
  end
126
185
 
127
186
  protected
@@ -142,9 +201,7 @@ module FPM; module Fry ; module Source
142
201
 
143
202
  def tar_io
144
203
  update!
145
- cmd = ['bzcat',tempfile]
146
- logger.debug("Running bzcat",cmd: cmd)
147
- return IO.popen(cmd)
204
+ return Exec::popen('bzcat', tempfile, logger: logger)
148
205
  end
149
206
 
150
207
  end
@@ -152,6 +209,22 @@ module FPM; module Fry ; module Source
152
209
  class ZipCache < Cache
153
210
 
154
211
  def tar_io
212
+ unpack!
213
+ return Exec::popen('tar','-c','.', chdir: unpacked_tmpdir)
214
+ end
215
+
216
+ def copy_to(dst)
217
+ update!
218
+ Exec['unzip', tempfile, '-d', dst ]
219
+ end
220
+
221
+ def prefix
222
+ unpack!
223
+ Source::prefix(unpacked_tmpdir)
224
+ end
225
+ private
226
+
227
+ def unpack!
155
228
  if !::File.directory?( unpacked_tmpdir )
156
229
  workdir = unpacked_tmpdir + '.tmp'
157
230
  begin
@@ -163,18 +236,6 @@ module FPM; module Fry ; module Source
163
236
  copy_to( workdir )
164
237
  File.rename(workdir, unpacked_tmpdir)
165
238
  end
166
- cmd = ['tar','-c','.']
167
- logger.debug("Running tar",cmd: cmd, dir: unpacked_tmpdir)
168
- ::Dir.chdir(unpacked_tmpdir) do
169
- return IO.popen(cmd)
170
- end
171
- end
172
-
173
- def copy_to(dst)
174
- update!
175
- cmd = ['unzip', tempfile, '-d', dst ]
176
- logger.debug("Running unzip",cmd: cmd)
177
- system(*cmd, out: '/dev/null')
178
239
  end
179
240
 
180
241
  def unpacked_tmpdir
@@ -186,12 +247,8 @@ module FPM; module Fry ; module Source
186
247
 
187
248
  def tar_io
188
249
  update!
189
- cmd = ['tar','-c',::File.basename(tempfile)]
190
250
  dir = File.dirname(tempfile)
191
- logger.debug("Running tar",cmd: cmd, dir: dir)
192
- ::Dir.chdir(dir) do
193
- return IO.popen(cmd)
194
- end
251
+ Exec::popen('tar','-c',::File.basename(tempfile), logger: logger, chdir: dir)
195
252
  end
196
253
 
197
254
  def copy_to(dst)
@@ -199,6 +256,10 @@ module FPM; module Fry ; module Source
199
256
  FileUtils.cp( tempfile, dst )
200
257
  end
201
258
 
259
+ def prefix
260
+ ""
261
+ end
262
+
202
263
  end
203
264
 
204
265
  CACHE_CLASSES = {
@@ -211,26 +272,45 @@ module FPM; module Fry ; module Source
211
272
  '.bundle' => PlainCache
212
273
  }
213
274
 
214
- attr :file_map, :data, :url, :extension, :checksum, :checksum_algorithm, :agent, :logger
275
+ attr :file_map, :data, :url, :checksum, :checksum_algorithm, :logger, :to
215
276
 
277
+ # @param [URI] url
278
+ # @param [Hash] options
279
+ # @option options [Cabin::Channel] :logger (default cabin channel)
280
+ # @option options [String] :checksum a checksum of the archive
281
+ # @raise [UnknownArchiveType] when the archive type is unknown
216
282
  def initialize( url, options = {} )
217
283
  @url = URI(url)
218
- @extension = options.fetch(:extension){
219
- CACHE_CLASSES.keys.find{|ext|
220
- @url.path.end_with?(ext)
221
- }
222
- }
284
+ @cache_class = guess_cache_class(@url)
223
285
  @logger = options.fetch(:logger){ Cabin::Channel.get }
224
286
  @checksum = options[:checksum]
225
287
  @checksum_algorithm = guess_checksum_algorithm(options[:checksum])
226
- @file_map = options.fetch(:file_map){ {'' => ''} }
288
+ @file_map = options[:file_map]
289
+ @to = options[:to]
227
290
  end
228
291
 
292
+ # Creates a cache.
293
+ #
294
+ # @param [String] tempdir
295
+ # @return [TarCache] for plain .tar files
296
+ # @return [TarGzCache] for .tar.gz files
297
+ # @return [TarBz2Cache] for .tar.bz2 files
298
+ # @return [ZipCache] for .zip files
299
+ # @return [PlainCache] for .bin files
229
300
  def build_cache(tempdir)
230
- CACHE_CLASSES.fetch(extension).new(self, tempdir)
301
+ @cache_class.new(self, tempdir)
231
302
  end
232
303
  private
233
304
 
305
+ def guess_cache_class( url )
306
+ CACHE_CLASSES.each do |ext,klass|
307
+ if url.path.end_with?(ext)
308
+ return klass
309
+ end
310
+ end
311
+ raise UnknownArchiveType.new("Unknown archive type", url: url.to_s, known_extensions: CACHE_CLASSES.keys)
312
+ end
313
+
234
314
  def guess_checksum_algorithm( checksum )
235
315
  case(checksum)
236
316
  when nil
@@ -1,6 +1,10 @@
1
1
  require 'fpm/fry/source'
2
+ require 'fpm/fry/exec'
2
3
  require 'fileutils'
3
4
  require 'digest'
5
+ require 'cabin/channel'
6
+ require 'fpm/fry/tar'
7
+
4
8
  module FPM; module Fry ; module Source
5
9
  class Dir
6
10
 
@@ -17,14 +21,10 @@ module FPM; module Fry ; module Source
17
21
  class Cache < Struct.new(:package, :dir)
18
22
  extend Forwardable
19
23
 
20
- def_delegators :package, :url, :logger, :file_map
24
+ def_delegators :package, :url, :logger, :file_map, :to
21
25
 
22
26
  def tar_io
23
- cmd = ['tar','-c','.']
24
- logger.debug("Running tar",cmd: cmd, dir: dir)
25
- ::Dir.chdir(dir) do
26
- return IO.popen(cmd)
27
- end
27
+ Exec::popen('tar','-c','.', chdir: dir, logger: logger)
28
28
  end
29
29
 
30
30
  def copy_to(dst)
@@ -39,9 +39,13 @@ module FPM; module Fry ; module Source
39
39
  end
40
40
  return dig.hexdigest
41
41
  end
42
+
43
+ def prefix
44
+ Source::prefix(dir)
45
+ end
42
46
  end
43
47
 
44
- attr :url, :logger, :file_map
48
+ attr :url, :logger, :file_map, :to
45
49
 
46
50
  def initialize( url, options = {} )
47
51
  @url = URI(url)
@@ -49,7 +53,8 @@ module FPM; module Fry ; module Source
49
53
  @url.path = File.expand_path(@url.path)
50
54
  end
51
55
  @logger = options.fetch(:logger){ Cabin::Channel.get }
52
- @file_map = options.fetch(:file_map){ {'' => ''} }
56
+ @file_map = options[:file_map]
57
+ @to = options[:to]
53
58
  end
54
59
 
55
60
  def build_cache(_)
@@ -1,16 +1,41 @@
1
1
  require 'fileutils'
2
2
  require 'forwardable'
3
- require 'open3'
3
+ require 'fpm/fry/exec'
4
4
  require 'fpm/fry/source'
5
5
  module FPM; module Fry ; module Source
6
+ # Used to build directly from git.
7
+ #
8
+ # @example in a recipe
9
+ # source 'https://github.com/ggreer/the_silver_searcher.git'
10
+ #
11
+ # It automatically recognizes the following url patterns:
12
+ #
13
+ # - git://…
14
+ # - git+…://…
15
+ # - user@host:….git
16
+ # - https://….git
17
+ # - https://git.…
18
+ #
6
19
  class Git
7
20
 
8
- REGEX = %r!\A(?:git:|\S+@\S+:\S+\.git\z|https?:.*\.git\z|ssh:.*\.git\z)!
21
+ REGEX = %r!\A(?:git:|\S+@\S+:\S+\.git\z|https?:(?://git\.|.*\.git\z)|ssh:.*\.git\z|git\+[a-z0-9]+:)!
9
22
 
23
+ # @return [:git]
10
24
  def self.name
11
25
  :git
12
26
  end
13
27
 
28
+ # Guesses if this url is a git url.
29
+ #
30
+ # @example not a git url
31
+ # FPM::Fry::Source::Git.guess( "bzr://something" ) #=> nil
32
+ #
33
+ # @example a git url
34
+ # FPM::Fry::Source::Git.guess( "git://something" ) #=> 4
35
+ #
36
+ # @param [URI,String] url
37
+ # @return [nil] when this uri doesn't match
38
+ # @return [Numeric] number of characters that were used
14
39
  def self.guess( url )
15
40
  Source::guess_regex(REGEX, url)
16
41
  end
@@ -18,76 +43,87 @@ module FPM; module Fry ; module Source
18
43
  class Cache < Struct.new(:package, :tempdir)
19
44
  extend Forwardable
20
45
 
21
- def_delegators :package, :url, :rev, :logger, :file_map
22
-
23
- def update
24
- begin
25
- if !File.exists? repodir
26
- if (ecode = git('init', '--bare')) != 0
27
- raise CacheFailed.new("Initializing git repository failed", exit_code: ecode)
28
- end
29
- end
30
- if (ecode = git('fetch','--depth=1', url.to_s, rev)) != 0
31
- raise CacheFailed.new("Failed to fetch from remote", exit_code: ecode, url: url.to_s, rev: rev)
32
- end
33
- return self
34
- rescue Errno::ENOENT
35
- raise "Cannot find git binary. Is it installed?"
36
- end
37
- end
46
+ def_delegators :package, :url, :rev, :logger, :file_map, :to
38
47
 
39
48
  def tar_io
40
- cmd = [package.git, "--git-dir=#{repodir}",'archive','--format=tar','FETCH_HEAD']
41
- logger.debug("Running git",cmd: cmd)
42
- IO.popen(cmd)
49
+ Exec::popen(package.git, "--git-dir=#{repodir}",'archive','--format=tar','FETCH_HEAD', logger: logger)
43
50
  end
44
51
 
45
52
  def copy_to(dst)
46
- cmd = [package.git, "--git-dir=#{repodir}", "--work-tree=#{dst}",'checkout','FETCH_HEAD','--','*']
47
- logger.debug("Running git",cmd: cmd)
48
- system(*cmd, chdir: dst)
53
+ Exec[
54
+ package.git, "--git-dir=#{repodir}", "--work-tree=#{dst}",'checkout','FETCH_HEAD','--','*',
55
+ chdir: dst, logger: logger
56
+ ]
49
57
  end
50
58
 
51
59
  def cachekey
52
- cmd = [package.git, "--git-dir=#{repodir}",'rev-parse','FETCH_HEAD^{tree}']
53
- logger.debug("Running git",cmd: cmd)
54
- return IO.popen(cmd).read.chomp
60
+ Exec::exec(package.git, "--git-dir=#{repodir}",'rev-parse','FETCH_HEAD^{tree}', logger: logger).chomp
61
+ end
62
+
63
+ def prefix
64
+ ""
55
65
  end
66
+
56
67
  private
57
- def repodir
58
- File.join(tempdir,File.basename(url.path))
68
+ def initialize(*_)
69
+ super
70
+ update
59
71
  end
60
72
 
61
- def git(*args)
62
- cmd = [package.git, "--git-dir=#{repodir}",*args]
63
- logger.debug("Running git",cmd: cmd.join(' '))
64
- Open3.popen3(*cmd) do |sin, out, err, thr|
65
- sin.close
66
- out.each_line do |line|
67
- logger.debug(line.chomp)
68
- end
69
- err.each_line do |line|
70
- logger.debug(line.chomp)
73
+ def update
74
+ begin
75
+ if !File.exists? repodir
76
+ Exec::exec(package.git, "--git-dir=#{repodir}",'init', '--bare', description: "initializing git repository", logger: logger)
71
77
  end
72
- return thr.value.exitstatus
78
+ Exec::exec(package.git, "--git-dir=#{repodir}",'fetch','--depth=1', url.to_s, rev, description: 'fetching from remote', logger: logger)
79
+ return self
80
+ rescue => e
81
+ raise CacheFailed.new(e, url: url.to_s, rev: rev)
73
82
  end
74
83
  end
75
-
84
+ def repodir
85
+ File.join(tempdir,File.basename(url.path))
86
+ end
76
87
  end
77
88
 
78
- attr :logger, :git, :rev, :file_map, :url
89
+ # @return [Cabin::Channel] logger
90
+ attr :logger
91
+
92
+ # @return [String] the git binary (default: "git")
93
+ attr :git
94
+
95
+ # @return [String] the git rev to pull (default "HEAD")
96
+ attr :rev
97
+
98
+ # @return [Hash<String,String>,nil] the file map for generating a docker file
99
+ attr :file_map
100
+
101
+ # @return [URI] the uri to pull from
102
+ attr :url
103
+
104
+ # @return [String,nil]
105
+ attr :to
79
106
 
107
+ # @param [URI] url the url to pull from
108
+ # @param [Hash] options
109
+ # @option options [Cabin::Channel] :logger (cabin default channel)
110
+ # @option options [String] :branch git branch to pull
111
+ # @option options [String] :tag git tag to pull
112
+ # @option options [Hash<String,String>] :file_map ({""=>""}) the file map to create the docker file from
80
113
  def initialize( url, options = {} )
81
114
  url = url.sub(/\A(\S+@\S+):(\S+\.git)\z/,'ssh://\1/\2')
82
115
  @url = URI(url)
83
116
  @logger = options.fetch(:logger){ Cabin::Channel.get }
84
117
  @rev = options[:branch] || options[:tag] || options[:rev] || 'HEAD'
85
- @file_map = options.fetch(:file_map){ {'' => ''} }
118
+ @file_map = options[:file_map]
86
119
  @git = options[:git] || 'git'
120
+ @to = options[:to]
87
121
  end
88
122
 
123
+ # @param [String] tempdir
124
+ # @return [Cache]
89
125
  def build_cache(tempdir)
90
- Cache.new(self, tempdir).update
126
+ Cache.new(self, tempdir)
91
127
  end
92
128
 
93
129
  end