fpm-fry 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/cabin/nice_output.rb +16 -1
- data/lib/fpm/fry/block_enumerator.rb +6 -3
- data/lib/fpm/fry/channel.rb +13 -0
- data/lib/fpm/fry/chroot.rb +2 -2
- data/lib/fpm/fry/client.rb +81 -6
- data/lib/fpm/fry/command.rb +11 -31
- data/lib/fpm/fry/command/cook.rb +19 -51
- data/lib/fpm/fry/detector.rb +43 -98
- data/lib/fpm/fry/docker_file.rb +3 -11
- data/lib/fpm/fry/exec.rb +76 -0
- data/lib/fpm/fry/inspector.rb +70 -0
- data/lib/fpm/fry/joined_io.rb +1 -1
- data/lib/fpm/fry/plugin/edit_staging.rb +1 -1
- data/lib/fpm/fry/plugin/init.rb +71 -42
- data/lib/fpm/fry/plugin/service.rb +108 -49
- data/lib/fpm/fry/recipe.rb +46 -21
- data/lib/fpm/fry/recipe/builder.rb +26 -13
- data/lib/fpm/fry/source.rb +14 -12
- data/lib/fpm/fry/source/{package.rb → archive.rb} +68 -31
- data/lib/fpm/fry/source/dir.rb +2 -5
- data/lib/fpm/fry/source/git.rb +70 -43
- data/lib/fpm/fry/source/patched.rb +14 -10
- data/lib/fpm/fry/with_data.rb +34 -0
- data/lib/fpm/package/docker.rb +15 -0
- metadata +5 -4
- data/lib/fpm/fry/os_db.rb +0 -36
data/lib/fpm/fry/recipe.rb
CHANGED
@@ -1,26 +1,28 @@
|
|
1
1
|
require 'fpm/fry/source'
|
2
|
-
require 'fpm/fry/source/
|
2
|
+
require 'fpm/fry/source/archive'
|
3
3
|
require 'fpm/fry/source/dir'
|
4
4
|
require 'fpm/fry/source/patched'
|
5
5
|
require 'fpm/fry/source/git'
|
6
6
|
require 'fpm/fry/plugin'
|
7
|
-
require 'fpm/fry/
|
7
|
+
require 'fpm/fry/exec'
|
8
8
|
require 'shellwords'
|
9
9
|
require 'cabin'
|
10
|
-
require 'open3'
|
11
10
|
module FPM; module Fry
|
12
11
|
|
12
|
+
# A FPM::Fry::Recipe contains all information needed to build a package.
|
13
|
+
#
|
14
|
+
# It is usually created by {FPM::Fry::Recipe::Builder}.
|
13
15
|
class Recipe
|
14
16
|
|
17
|
+
# A FPM::Fry::Recipe::Step is a named build step.
|
18
|
+
#
|
19
|
+
# @see FPM::Fry::Recipe#steps
|
15
20
|
class Step < Struct.new(:name, :value)
|
16
21
|
def to_s
|
17
22
|
value.to_s
|
18
23
|
end
|
19
24
|
end
|
20
25
|
|
21
|
-
class DuplicateDependency < ArgumentError
|
22
|
-
end
|
23
|
-
|
24
26
|
class PackageRecipe
|
25
27
|
attr_accessor :name,
|
26
28
|
:iteration,
|
@@ -60,6 +62,10 @@ module FPM; module Fry
|
|
60
62
|
|
61
63
|
alias dependencies depends
|
62
64
|
|
65
|
+
# Applies settings to output package
|
66
|
+
# @param [FPM::Package] package
|
67
|
+
# @return [FPM::Package] package
|
68
|
+
# @api private
|
63
69
|
def apply_output( package )
|
64
70
|
package.name = name
|
65
71
|
package.version = version
|
@@ -87,8 +93,11 @@ module FPM; module Fry
|
|
87
93
|
|
88
94
|
alias apply apply_output
|
89
95
|
|
96
|
+
# @api private
|
90
97
|
SYNTAX_CHECK_SHELLS = ['/bin/sh','/bin/bash', '/bin/dash']
|
91
98
|
|
99
|
+
# Lints the settings for some common problems
|
100
|
+
# @return [Array<String>] problems
|
92
101
|
def lint
|
93
102
|
problems = []
|
94
103
|
problems << "Name is empty." if name.to_s == ''
|
@@ -110,28 +119,37 @@ module FPM; module Fry
|
|
110
119
|
problems << "#{type} script doesn't have a valid command in shebang"
|
111
120
|
end
|
112
121
|
if SYNTAX_CHECK_SHELLS.include? args[0]
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
problems << "#{type} script is not valid #{args[0]} code: #{serr.read.chomp}"
|
122
|
+
begin
|
123
|
+
Exec::exec(args[0],'-n', stdin_data: s)
|
124
|
+
rescue Exec::Failed => e
|
125
|
+
problems << "#{type} script is not valid #{args[0]} code: #{e.stderr.chomp}"
|
118
126
|
end
|
119
|
-
serr.close
|
120
|
-
sout.close
|
121
127
|
end
|
122
128
|
end
|
123
129
|
return problems
|
124
130
|
end
|
125
131
|
end
|
126
132
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
133
|
+
# @return [FPM::Fry::Source] the source used for building
|
134
|
+
attr_accessor :source
|
135
|
+
|
136
|
+
attr_accessor :build_mounts
|
137
|
+
attr_accessor :apt_setup
|
138
|
+
|
139
|
+
# @return [Array<#to_s>] steps that will be carried out before build
|
140
|
+
attr_accessor :before_build_steps
|
141
|
+
|
142
|
+
# @return [Array<#to_s>] steps that will be carried out during build
|
143
|
+
attr_accessor :steps
|
144
|
+
|
145
|
+
# @return [Array<FPM::Fry::PackageRecipe>] a list of packages that will be created
|
146
|
+
attr_accessor :packages
|
147
|
+
|
148
|
+
# @return [Hash<String,Hash>] build dependencies
|
149
|
+
attr_accessor :build_depends
|
150
|
+
|
151
|
+
# @return [Array<#call>] hooks that will be called on the input package
|
152
|
+
attr_accessor :input_hooks
|
135
153
|
|
136
154
|
def initialize
|
137
155
|
@source = Source::Null
|
@@ -145,6 +163,8 @@ module FPM; module Fry
|
|
145
163
|
@apt_setup = []
|
146
164
|
end
|
147
165
|
|
166
|
+
# Calculates all dependencies of this recipe
|
167
|
+
# @return [Hash<String,Hash>] the dependencies
|
148
168
|
def depends
|
149
169
|
depends = @packages.map(&:depends).inject(:merge)
|
150
170
|
@packages.map(&:name).each do | n |
|
@@ -153,10 +173,15 @@ module FPM; module Fry
|
|
153
173
|
return depends
|
154
174
|
end
|
155
175
|
|
176
|
+
# Checks all packages for common errors
|
177
|
+
# @return [Array<String>] problems
|
156
178
|
def lint
|
157
179
|
packages.flat_map(&:lint)
|
158
180
|
end
|
159
181
|
|
182
|
+
# Applies input settings to package
|
183
|
+
# @param [FPM::Package] package
|
184
|
+
# @return [FPM::Package]
|
160
185
|
def apply_input( package )
|
161
186
|
input_hooks.each{|h| h.call(self, package) }
|
162
187
|
return package
|
@@ -7,13 +7,26 @@ module FPM::Fry
|
|
7
7
|
class NotFound < StandardError
|
8
8
|
end
|
9
9
|
|
10
|
-
class PackageBuilder
|
10
|
+
class PackageBuilder
|
11
11
|
|
12
|
+
# @return [Hash<Symbol,Object>]
|
13
|
+
attr :variables
|
14
|
+
|
15
|
+
# @return [FPM::Fry::PackageRecipe]
|
16
|
+
attr :package_recipe
|
17
|
+
|
18
|
+
# @return [Cabin::Channel]
|
12
19
|
attr :logger
|
13
20
|
|
14
|
-
|
15
|
-
|
21
|
+
# @return [FPM::Fry::Inspector,nil]
|
22
|
+
attr :inspector
|
23
|
+
|
24
|
+
# @api private
|
25
|
+
def initialize( variables, package_recipe, options = {})
|
26
|
+
@variables = variables
|
27
|
+
@package_recipe = package_recipe
|
16
28
|
@logger = options.fetch(:logger){ Cabin::Channel.get }
|
29
|
+
@inspector = options[:inspector]
|
17
30
|
end
|
18
31
|
|
19
32
|
def flavour
|
@@ -174,17 +187,17 @@ module FPM::Fry
|
|
174
187
|
|
175
188
|
class Builder < PackageBuilder
|
176
189
|
|
190
|
+
# @return [FPM::Fry::Recipe]
|
177
191
|
attr :recipe
|
178
192
|
|
179
|
-
|
193
|
+
# @param [Hash<Symbol,Object>] variables
|
194
|
+
# @param [Hash] options
|
195
|
+
# @option options [FPM::Fry::Recipe] :recipe (Recipe.new)
|
196
|
+
# @option options [Cabin::Channel] :logger (default cabin channel)
|
197
|
+
# @option options [FPM::Fry::Inspector] :inspector
|
198
|
+
def initialize( variables, options = {} )
|
199
|
+
recipe = options.fetch(:recipe){ Recipe.new }
|
180
200
|
variables = variables.dup
|
181
|
-
if variables[:distribution] && !variables[:flavour] && OsDb[variables[:distribution]]
|
182
|
-
variables[:flavour] = OsDb[variables[:distribution]][:flavour]
|
183
|
-
end
|
184
|
-
if !variables[:codename] && OsDb[variables[:distribution]] && variables[:distribution_version]
|
185
|
-
codename = OsDb[variables[:distribution]][:codenames].find{|name,version| variables[:distribution_version].start_with? version }
|
186
|
-
variables[:codename] = codename[0] if codename
|
187
|
-
end
|
188
201
|
variables.freeze
|
189
202
|
@recipe = recipe
|
190
203
|
@before_build = false
|
@@ -266,7 +279,7 @@ module FPM::Fry
|
|
266
279
|
pr.version = package_recipe.version
|
267
280
|
pr.iteration = package_recipe.iteration
|
268
281
|
recipe.packages << pr
|
269
|
-
PackageBuilder.new(variables, pr).instance_eval(&block)
|
282
|
+
PackageBuilder.new(variables, pr, logger: logger, inspector: inspector).instance_eval(&block)
|
270
283
|
end
|
271
284
|
|
272
285
|
protected
|
@@ -289,7 +302,7 @@ module FPM::Fry
|
|
289
302
|
|
290
303
|
def register_default_source_types!
|
291
304
|
register_source_type Source::Git
|
292
|
-
register_source_type Source::
|
305
|
+
register_source_type Source::Archive
|
293
306
|
register_source_type Source::Dir
|
294
307
|
end
|
295
308
|
|
data/lib/fpm/fry/source.rb
CHANGED
@@ -1,34 +1,35 @@
|
|
1
|
+
require 'fpm/fry/with_data'
|
1
2
|
module FPM; module Fry ; module Source
|
2
3
|
|
4
|
+
# Raised when building a cache failed.
|
3
5
|
class CacheFailed < StandardError
|
4
|
-
|
5
|
-
attr :data
|
6
|
-
|
7
|
-
def initialize(e, data = {})
|
8
|
-
if e.kind_of? Exception
|
9
|
-
@data = {reason: e}.merge data
|
10
|
-
super(e.message)
|
11
|
-
else
|
12
|
-
@data = data.dup
|
13
|
-
super(e.to_s)
|
14
|
-
end
|
15
|
-
end
|
6
|
+
include WithData
|
16
7
|
end
|
17
8
|
|
9
|
+
# A special source that is empty.
|
18
10
|
module Null
|
19
11
|
|
12
|
+
# A special cache that is empty.
|
20
13
|
module Cache
|
14
|
+
|
15
|
+
# @return [IO] an empty tar
|
21
16
|
def self.tar_io
|
22
17
|
StringIO.new("\x00"*1024)
|
23
18
|
end
|
19
|
+
|
20
|
+
# @return [Hash] an empty hash
|
24
21
|
def self.file_map
|
25
22
|
return {}
|
26
23
|
end
|
24
|
+
|
25
|
+
# @return [String] 32 times zero
|
27
26
|
def self.cachekey
|
28
27
|
return '0' * 32
|
29
28
|
end
|
30
29
|
end
|
31
30
|
|
31
|
+
# @see FPM::Fry::Source::Null::Cache
|
32
|
+
# @return {FPM::Fry::Source::Null::Cache}
|
32
33
|
def self.build_cache(*_)
|
33
34
|
return Cache
|
34
35
|
end
|
@@ -37,6 +38,7 @@ module FPM; module Fry ; module Source
|
|
37
38
|
|
38
39
|
class << self
|
39
40
|
|
41
|
+
# @api private
|
40
42
|
def guess_regex(rx, url)
|
41
43
|
if m = rx.match(url.to_s)
|
42
44
|
return m[0].size
|
@@ -4,32 +4,65 @@ 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'
|
8
9
|
module FPM; module Fry ; module Source
|
9
|
-
|
10
|
+
# Used to build from an archive.
|
11
|
+
#
|
12
|
+
# @example in a recipe
|
13
|
+
# source 'http://curl.haxx.se/download/curl-7.36.0.tar.gz',
|
14
|
+
# checksum: '33015795d5650a2bfdd9a4a28ce4317cef944722a5cfca0d1563db8479840e90'
|
15
|
+
#
|
16
|
+
# It is highly advised to supply a checksum ( althought it's not mandatory ).
|
17
|
+
# This checksum will be used to test for cache validity and data integrity. The
|
18
|
+
# checksum algorithm is automatically guessed based on the length of the checksum.
|
19
|
+
#
|
20
|
+
# - 40 characters = sha1
|
21
|
+
# - 64 characters = sha256
|
22
|
+
#
|
23
|
+
# Let's be honest: all other checksum algorithms aren't or shouldn't be in use anyway.
|
24
|
+
class Archive
|
10
25
|
|
11
26
|
REGEX = %r!\Ahttps?:!
|
12
27
|
|
28
|
+
# @return [:archive]
|
13
29
|
def self.name
|
14
30
|
:package
|
15
31
|
end
|
16
32
|
|
17
33
|
def self.aliases
|
18
|
-
[:http]
|
34
|
+
[:package,:http]
|
19
35
|
end
|
20
36
|
|
37
|
+
# Guesses if the given url is an archive.
|
38
|
+
#
|
39
|
+
# @example not an archive
|
40
|
+
# FPM::Fry::Source::Archive.guess("bzr://something") # => nil
|
41
|
+
#
|
42
|
+
# @example an archive
|
43
|
+
# FPM::Fry::Source::Archive.guess("https://some/thing.tar.gz") #=> 6
|
44
|
+
#
|
45
|
+
# @return [nil] when it's not an archive
|
46
|
+
# @return [Numeric] number of characters used
|
21
47
|
def self.guess( url )
|
22
48
|
Source::guess_regex(REGEX, url)
|
23
49
|
end
|
24
50
|
|
51
|
+
# Raised when too many redirects happened.
|
25
52
|
class RedirectError < CacheFailed
|
26
53
|
end
|
27
54
|
|
55
|
+
# Raised when the archive type is not known.
|
56
|
+
class UnknownArchiveType < StandardError
|
57
|
+
include WithData
|
58
|
+
end
|
59
|
+
|
28
60
|
class Cache < Struct.new(:package,:tempdir)
|
29
61
|
extend Forwardable
|
30
62
|
|
31
|
-
def_delegators :package, :url, :checksum, :checksum_algorithm, :
|
63
|
+
def_delegators :package, :url, :checksum, :checksum_algorithm, :logger, :file_map
|
32
64
|
|
65
|
+
# @return [String] cachekey which is equal to the checksum
|
33
66
|
def cachekey
|
34
67
|
@observed_checksum || checksum
|
35
68
|
end
|
@@ -92,7 +125,7 @@ module FPM; module Fry ; module Source
|
|
92
125
|
case(resp)
|
93
126
|
when Net::HTTPRedirection
|
94
127
|
if redirs == 0
|
95
|
-
raise RedirectError
|
128
|
+
raise RedirectError.new("Too many redirects", url: url.to_s, location: resp['location'])
|
96
129
|
end
|
97
130
|
logger.debug("Following redirect", url: url.to_s , location: resp['location'])
|
98
131
|
return fetch_url( resp['location'], redirs - 1, &block)
|
@@ -119,9 +152,7 @@ module FPM; module Fry ; module Source
|
|
119
152
|
|
120
153
|
def copy_to(dst)
|
121
154
|
update!
|
122
|
-
|
123
|
-
logger.debug("Running tar",cmd: cmd)
|
124
|
-
system(*cmd)
|
155
|
+
Exec['tar','-xf',tempfile,'-C',dst, logger: logger]
|
125
156
|
end
|
126
157
|
|
127
158
|
protected
|
@@ -142,9 +173,7 @@ module FPM; module Fry ; module Source
|
|
142
173
|
|
143
174
|
def tar_io
|
144
175
|
update!
|
145
|
-
|
146
|
-
logger.debug("Running bzcat",cmd: cmd)
|
147
|
-
return IO.popen(cmd)
|
176
|
+
return Exec::popen('bzcat', tempfile, logger: logger)
|
148
177
|
end
|
149
178
|
|
150
179
|
end
|
@@ -163,20 +192,14 @@ module FPM; module Fry ; module Source
|
|
163
192
|
copy_to( workdir )
|
164
193
|
File.rename(workdir, unpacked_tmpdir)
|
165
194
|
end
|
166
|
-
|
167
|
-
logger.debug("Running tar",cmd: cmd, dir: unpacked_tmpdir)
|
168
|
-
::Dir.chdir(unpacked_tmpdir) do
|
169
|
-
return IO.popen(cmd)
|
170
|
-
end
|
195
|
+
return Exec::popen('tar','-c','.', chdir: unpacked_tmpdir)
|
171
196
|
end
|
172
197
|
|
173
198
|
def copy_to(dst)
|
174
199
|
update!
|
175
|
-
|
176
|
-
logger.debug("Running unzip",cmd: cmd)
|
177
|
-
system(*cmd, out: '/dev/null')
|
200
|
+
Exec['unzip', tempfile, '-d', dst ]
|
178
201
|
end
|
179
|
-
|
202
|
+
private
|
180
203
|
def unpacked_tmpdir
|
181
204
|
File.join(tempdir, cachekey)
|
182
205
|
end
|
@@ -186,12 +209,8 @@ module FPM; module Fry ; module Source
|
|
186
209
|
|
187
210
|
def tar_io
|
188
211
|
update!
|
189
|
-
cmd = ['tar','-c',::File.basename(tempfile)]
|
190
212
|
dir = File.dirname(tempfile)
|
191
|
-
|
192
|
-
::Dir.chdir(dir) do
|
193
|
-
return IO.popen(cmd)
|
194
|
-
end
|
213
|
+
Exec::popen('tar','-c',::File.basename(tempfile), logger: logger, chdir: dir)
|
195
214
|
end
|
196
215
|
|
197
216
|
def copy_to(dst)
|
@@ -211,26 +230,44 @@ module FPM; module Fry ; module Source
|
|
211
230
|
'.bundle' => PlainCache
|
212
231
|
}
|
213
232
|
|
214
|
-
attr :file_map, :data, :url, :
|
233
|
+
attr :file_map, :data, :url, :checksum, :checksum_algorithm, :logger
|
215
234
|
|
235
|
+
# @param [URI] url
|
236
|
+
# @param [Hash] options
|
237
|
+
# @option options [Cabin::Channel] :logger (default cabin channel)
|
238
|
+
# @option options [String] :checksum a checksum of the archive
|
239
|
+
# @raise [UnknownArchiveType] when the archive type is unknown
|
216
240
|
def initialize( url, options = {} )
|
217
241
|
@url = URI(url)
|
218
|
-
@
|
219
|
-
CACHE_CLASSES.keys.find{|ext|
|
220
|
-
@url.path.end_with?(ext)
|
221
|
-
}
|
222
|
-
}
|
242
|
+
@cache_class = guess_cache_class(@url)
|
223
243
|
@logger = options.fetch(:logger){ Cabin::Channel.get }
|
224
244
|
@checksum = options[:checksum]
|
225
245
|
@checksum_algorithm = guess_checksum_algorithm(options[:checksum])
|
226
246
|
@file_map = options.fetch(:file_map){ {'' => ''} }
|
227
247
|
end
|
228
248
|
|
249
|
+
# Creates a cache.
|
250
|
+
#
|
251
|
+
# @param [String] tempdir
|
252
|
+
# @return [TarCache] for plain .tar files
|
253
|
+
# @return [TarGzCache] for .tar.gz files
|
254
|
+
# @return [TarBz2Cache] for .tar.bz2 files
|
255
|
+
# @return [ZipCache] for .zip files
|
256
|
+
# @return [PlainCache] for .bin files
|
229
257
|
def build_cache(tempdir)
|
230
|
-
|
258
|
+
@cache_class.new(self, tempdir)
|
231
259
|
end
|
232
260
|
private
|
233
261
|
|
262
|
+
def guess_cache_class( url )
|
263
|
+
CACHE_CLASSES.each do |ext,klass|
|
264
|
+
if url.path.end_with?(ext)
|
265
|
+
return klass
|
266
|
+
end
|
267
|
+
end
|
268
|
+
raise UnknownArchiveType.new("Unknown archive type", url: url.to_s, known_extensions: CACHE_CLASSES.keys)
|
269
|
+
end
|
270
|
+
|
234
271
|
def guess_checksum_algorithm( checksum )
|
235
272
|
case(checksum)
|
236
273
|
when nil
|