fpm-fry 0.2.2 → 0.3.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 +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
|