fpm-fry 0.3.0 → 0.4.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: de6bc3a9f29acd7c147a4016c4906fd0a35c6481
4
- data.tar.gz: afece6f8aea44147df5e357015115f9c74614071
2
+ SHA256:
3
+ metadata.gz: 4d3c9e551a387a09f3453921a0fb68a0a63a23b629b7ff0777addbce54682f77
4
+ data.tar.gz: 69b571f675f174f2bcf45cd1a4e0c6fd38e7b72eff4b10ae4c913db6451ca366
5
5
  SHA512:
6
- metadata.gz: 3fea3ee2114b957eca0789a06faab401d88ff9269bf95ffab42ce79f367716d4b962b964e44aa03ef58087c50c1df5c904195f1ac44cde705bc17c5c0a9d2e63
7
- data.tar.gz: ccee1a75f08a186ff5a44e673d297ca41e2ce6faee2b4df57715542eafbf8640b605e0f7c619958f8b4a7967abd76b8192eadd6e72d5ffcecb07ab1ca27c9949
6
+ metadata.gz: 18e060177ce006f584080adfba77393d04143595430821ee32db7c1bc035521553476af561aad169239c8cc30cfb0b55fe2b99ad12ab3ac3ebdc4a857458c1f7
7
+ data.tar.gz: 141fe4547cdf93d153ae5a7d135d32fa1bf4c343302629d1839b56c854eaad5981938a2c1c3bd4415c1d41ec20e23d1696399cdee856cb83710c3d1dfa1b1700
@@ -10,12 +10,17 @@ module FPM; module Fry
10
10
  end
11
11
 
12
12
  def call(chunk, *_)
13
- json = JSON.parse(chunk)
14
- stream = json['stream']
15
- if /\ASuccessfully built (\w+)\Z/.match(stream)
16
- images << $1
13
+ # new docker for Mac results in data like this:
14
+ # "{'stream':' ---\\u003e 3bc51d6a4c46\\n'}\r\n{'stream':'Step 2 : WORKDIR /tmp/build\\n'}\r\n"
15
+ # this isn't valid JSON, of course, so we process each part individually
16
+ chunk.split("\r\n").each do |sub_chunk|
17
+ json = JSON.parse(sub_chunk)
18
+ stream = json['stream']
19
+ if /\ASuccessfully built (\w+)\Z/.match(stream)
20
+ images << $1
21
+ end
22
+ out << stream
17
23
  end
18
- out << stream
19
24
  end
20
25
 
21
26
  end
@@ -18,7 +18,7 @@ class FPM::Fry::Client
18
18
  include FPM::Fry::WithData
19
19
  end
20
20
 
21
- # Raised when trying to read file that can't be read e.g. because it's a
21
+ # Raised when trying to read file that can't be read e.g. because it's a
22
22
  # directory.
23
23
  class NotAFile < StandardError
24
24
  include FPM::Fry::WithData
@@ -84,12 +84,21 @@ class FPM::Fry::Client
84
84
 
85
85
  def read(name, resource)
86
86
  return to_enum(:read, name, resource) unless block_given?
87
- res = agent.get(
88
- path: url('containers',name,'archive'),
89
- query: {'path' => resource},
90
- headers: { 'Content-Type' => 'application/json' },
91
- expects: [200,404,500]
92
- )
87
+ res = if (server_version['ApiVersion'] < "1.20")
88
+ agent.post(
89
+ path: url('containers', name, 'copy'),
90
+ headers: { 'Content-Type' => 'application/json' },
91
+ body: JSON.generate({'Resource' => resource}),
92
+ expects: [200,404,500]
93
+ )
94
+ else
95
+ agent.get(
96
+ path: url('containers', name, 'archive'),
97
+ headers: { 'Content-Type' => 'application/json' },
98
+ query: {:path => resource},
99
+ expects: [200,404,500]
100
+ )
101
+ end
93
102
  if [404,500].include? res.status
94
103
  body_message = Hash[JSON.load(res.body).map{|k,v| ["docker.#{k}",v] }] rescue {'docker.message' => res.body}
95
104
  body_message['docker.container'] = name
@@ -99,7 +108,7 @@ class FPM::Fry::Client
99
108
  raise FileNotFound.new("file not found", {'path' => resource}.merge(body_message))
100
109
  end
101
110
  sio = StringIO.new(res.body)
102
- tar = ::Gem::Package::TarReader.new( sio )
111
+ tar = FPM::Fry::Tar::Reader.new( sio )
103
112
  tar.each do |entry|
104
113
  yield entry
105
114
  end
@@ -13,6 +13,9 @@ module FPM; module Fry
13
13
  option '--debug', :flag, 'Turns on debugging'
14
14
  option '--[no-]tls', :flag, 'Turns on tls ( default is false for schema unix, tcp and http and true for https )'
15
15
  option '--[no-]tlsverify', :flag, 'Turns off tls peer verification', default:true, environment_variable: 'DOCKER_TLS_VERIFY'
16
+ option ["-t", "--tmpdir"], "PATH", 'Write tmp data to PATH', default: '/tmp/fpm-fry', attribute_name: :dir do |s|
17
+ String(s)
18
+ end
16
19
 
17
20
  subcommand 'fpm', 'Works like fpm but with docker support', FPM::Command
18
21
 
@@ -22,7 +25,7 @@ module FPM; module Fry
22
25
 
23
26
  def initialize(invocation_path, ctx = {}, parent_attribute_values = {})
24
27
  super
25
- @ui = ctx.fetch(:ui){ UI.new }
28
+ @ui = ctx.fetch(:ui){ UI.new(tmpdir: dir) }
26
29
  @client = ctx[:client]
27
30
  end
28
31
 
@@ -122,6 +122,9 @@ module FPM; module Fry
122
122
  path: client.url("build?rm=1&dockerfile=#{DockerFile::NAME}&t=#{cachetag}"),
123
123
  request_block: BlockEnumerator.new(df.tar_io)
124
124
  )
125
+ else
126
+ # Hack to trigger hints/warnings even when the cache is valid.
127
+ DockerFile::Source.new(builder.variables.merge(image: image_id),cache).send(:file_map)
125
128
  end
126
129
 
127
130
  df = DockerFile::Build.new(cachetag, builder.variables.dup,builder.recipe, update: update?)
@@ -150,10 +153,14 @@ module FPM; module Fry
150
153
  case(update)
151
154
  when 'auto'
152
155
  Inspector.for_image(client, image) do |inspector|
153
- inspector.read('/var/lib/apt/lists') do |file|
154
- next if file.header.name == 'lists/'
155
- logger.hint("/var/lib/apt/lists is not empty, you could try to speed up builds with --update=never", documentation: 'https://github.com/xing/fpm-fry/wiki/The-update-parameter')
156
- break
156
+ begin
157
+ inspector.read('/var/lib/apt/lists') do |file|
158
+ next if file.header.name == 'lists/'
159
+ logger.hint("/var/lib/apt/lists is not empty, you could try to speed up builds with --update=never", documentation: 'https://github.com/xing/fpm-fry/wiki/The-update-parameter')
160
+ break
161
+ end
162
+ rescue FPM::Fry::Client::FileNotFound
163
+ logger.hint("/var/lib/apt/lists does not exists, so we will autoupdate")
157
164
  end
158
165
  end
159
166
  return true
@@ -275,6 +282,7 @@ module FPM; module Fry
275
282
 
276
283
  out_map.each do |output, package|
277
284
  package.apply_output(output)
285
+ adjust_package_settings(output)
278
286
  adjust_config_files(output)
279
287
  end
280
288
 
@@ -291,18 +299,24 @@ module FPM; module Fry
291
299
 
292
300
  end
293
301
 
302
+ def adjust_package_settings( output )
303
+ # FPM ignores the file permissions on rpm packages.
304
+ output.attributes[:rpm_use_file_permissions?] = true
305
+ output.attributes[:rpm_user] = 'root'
306
+ output.attributes[:rpm_group] = 'root'
307
+ end
294
308
 
295
309
  def adjust_config_files( output )
296
310
  # FPM flags all files in /etc as config files but only for debian :/.
297
- # Actually this behavior makes sense to me for all packages because it's
298
- # the thing I usually want. By setting this attribute at least the
311
+ # Actually this behavior makes sense to me for all packages because it's
312
+ # the thing I usually want. By setting this attribute at least the
299
313
  # misleading warning goes away.
300
314
  output.attributes[:deb_no_default_config_files?] = true
301
315
  output.attributes[:deb_auto_config_files?] = false
302
316
 
303
317
  return if output.attributes[:fry_config_explicitly_used]
304
318
 
305
- # Now that we have disabled this for debian we have to reenable if it for
319
+ # Now that we have disabled this for debian we have to reenable if it for
306
320
  # all.
307
321
  etc = File.expand_path('etc', output.staging_path)
308
322
  if File.exists?( etc )
@@ -13,6 +13,11 @@ module FPM; module Fry
13
13
  def initialize(variables, cache = Source::Null::Cache)
14
14
  variables = variables.dup.freeze
15
15
  super(variables, cache)
16
+ if cache.respond_to? :logger
17
+ @logger = cache.logger
18
+ else
19
+ @logger = Cabin::Channel.get
20
+ end
16
21
  end
17
22
 
18
23
  def dockerfile
@@ -21,8 +26,8 @@ module FPM; module Fry
21
26
 
22
27
  df << "RUN mkdir /tmp/build"
23
28
 
24
- cache.file_map.each do |from, to|
25
- df << "ADD #{map_from(from)} #{map_to(to)}"
29
+ file_map.each do |from, to|
30
+ df << "COPY #{map_from(from)} #{map_to(to)}"
26
31
  end
27
32
 
28
33
  df << ""
@@ -47,6 +52,33 @@ module FPM; module Fry
47
52
  return sio
48
53
  end
49
54
 
55
+ private
56
+
57
+ attr :logger
58
+
59
+ def file_map
60
+ prefix = ""
61
+ to = ""
62
+ if cache.respond_to? :prefix
63
+ prefix = cache.prefix
64
+ end
65
+ if cache.respond_to? :to
66
+ to = cache.to || ""
67
+ end
68
+ fm = cache.file_map
69
+ if fm.nil?
70
+ return { prefix => to }
71
+ end
72
+ if fm.size == 1
73
+ key, value = fm.first
74
+ key = key.gsub(%r!\A\./|/\z!,'')
75
+ if ["",".","./"].include?(value) && key == prefix
76
+ logger.hint("You can remove the file_map: #{fm.inspect} option on source. The given value is the default")
77
+ end
78
+ end
79
+ return fm
80
+ end
81
+
50
82
  def map_to(dir)
51
83
  if ['','.'].include? dir
52
84
  return '/tmp/build'
@@ -78,17 +110,27 @@ module FPM; module Fry
78
110
  end
79
111
 
80
112
  def dockerfile
81
- df = []
82
- df << "FROM #{base}"
83
- df << "WORKDIR /tmp/build"
113
+ df = {
114
+ source: [],
115
+ dependencies: [],
116
+ build: []
117
+ }
118
+ df[:source] << "FROM #{base}"
119
+ workdir = '/tmp/build'
120
+ # TODO: get this from cache, not from the source itself
121
+ if recipe.source.respond_to? :to
122
+ to = recipe.source.to || ""
123
+ workdir = File.expand_path(to, workdir)
124
+ end
125
+ df[:source] << "WORKDIR #{workdir}"
84
126
 
85
127
  # need to add external sources before running any command
86
128
  recipe.build_mounts.each do |source, target|
87
- df << "ADD #{source} /tmp/build/#{target}"
129
+ df[:dependencies] << "COPY #{source} ./#{target}"
88
130
  end
89
131
 
90
- recipe.apt_setup.each do |step|
91
- df << "RUN #{step}"
132
+ recipe.before_dependencies_steps.each do |step|
133
+ df[:dependencies] << "RUN #{step.to_s}"
92
134
  end
93
135
 
94
136
  if build_dependencies.any?
@@ -98,22 +140,22 @@ module FPM; module Fry
98
140
  if options[:update]
99
141
  update = 'apt-get update && '
100
142
  end
101
- df << "RUN #{update}apt-get install --yes #{Shellwords.join(build_dependencies)}"
143
+ df[:dependencies] << "RUN #{update}apt-get install --yes #{Shellwords.join(build_dependencies)}"
102
144
  when 'redhat'
103
- df << "RUN yum -y install #{Shellwords.join(build_dependencies)}"
145
+ df[:dependencies] << "RUN yum -y install #{Shellwords.join(build_dependencies)}"
104
146
  else
105
147
  raise "Unknown flavour: #{variables[:flavour]}"
106
148
  end
107
149
  end
108
150
 
109
151
  recipe.before_build_steps.each do |step|
110
- df << "RUN #{step.to_s}"
152
+ df[:build] << "RUN #{step.to_s}"
111
153
  end
112
154
 
113
- df << "ADD .build.sh /tmp/build/"
114
- df << "ENTRYPOINT /tmp/build/.build.sh"
115
- df << ''
116
- return df.join("\n")
155
+ df[:build] << "COPY .build.sh #{workdir}/"
156
+ df[:build] << "CMD #{workdir}/.build.sh"
157
+ recipe.apply_dockerfile_hooks(df)
158
+ return [*df[:source],*df[:dependencies],*df[:build],""].join("\n")
117
159
  end
118
160
 
119
161
  def build_sh
@@ -0,0 +1,52 @@
1
+ require 'fpm/fry/plugin'
2
+ module FPM::Fry::Plugin ;
3
+
4
+ # Allows adding a debian repository.
5
+ #
6
+ # @note experimental
7
+ #
8
+ # @example in a recipe
9
+ # plugin 'apt' do |apt|
10
+ # apt.repository "https://repo.varnish-cache.org/#{distribution}", "trusty", "varnish-4.1"
11
+ # end
12
+ #
13
+ class Apt
14
+
15
+ # Adds a debian repository
16
+ #
17
+ # @param [String] url
18
+ # @param [String] distribution
19
+ # @param [String,Array<String>] components
20
+ # @param [Hash] options
21
+ def repository(url, distribution, components, options = {} )
22
+ name = "#{url}-#{distribution}".gsub(/[^a-zA-Z0-9_\-]/,'-')
23
+ source = ['deb']
24
+ source << '[trusted=yes]'
25
+ source << url
26
+ source << distribution
27
+ source << Array(components).join(' ')
28
+ @builder.before_dependencies do
29
+ @builder.bash "echo '#{source.join(' ')}' >> /etc/apt/sources.list.d/#{name}.list && apt-get update -o Dir::Etc::sourcelist='sources.list.d/#{name}.list' -o Dir::Etc::sourceparts='-' -o APT::Get::List-Cleanup='0'"
30
+ end
31
+ end
32
+
33
+ def self.apply(builder, &block)
34
+ if builder.flavour != "debian"
35
+ builder.logger.info('skipped apt plugin')
36
+ return
37
+ end
38
+ dsl = self.new(builder)
39
+ if block.arity == 1
40
+ block.call(dsl)
41
+ else
42
+ dsl.instance_eval(&block)
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def initialize(builder)
49
+ @builder = builder
50
+ end
51
+
52
+ end ; end
@@ -0,0 +1,45 @@
1
+ require 'fpm/fry/plugin'
2
+ # The env plugin sets global environment variables.
3
+ #
4
+ # @example add something to PATH in a recipe
5
+ # plugin 'env', 'PATH' => '$PATH:/usr/local/go/bin'
6
+ #
7
+ module FPM::Fry::Plugin::Env
8
+
9
+ # @api private
10
+ class AddEnv < Struct.new(:env)
11
+
12
+ def call(_, df)
13
+ df[:source] << format
14
+ end
15
+ private
16
+
17
+ def format
18
+ "ENV " + env.map{|k,v| "#{k}=#{escape(v)}" }.join(" ")
19
+ end
20
+
21
+ def escape(v)
22
+ v.gsub(/([ \n\\])/,'\\\\\\1')
23
+ end
24
+ end
25
+
26
+ def self.apply(builder, env)
27
+ unless env.kind_of? Hash
28
+ raise FPM::Fry::WithData(
29
+ ArgumentError.new("ENV must be a Hash, got #{env.inspect}"),
30
+ documentation: 'https://github.com/xing/fpm-fry/wiki/Plugin-env'
31
+ )
32
+ end
33
+ env.each do |k,v|
34
+ k = k.to_s if k.kind_of? Symbol
35
+ unless k.kind_of?(String) && k =~ /\A[A-Z][A-Z0-9_]*\z/
36
+ raise FPM::Fry::WithData(
37
+ ArgumentError.new("environment variable names must be strings consisiting of uppercase letters, numbers and underscores, got #{k.inspect}"),
38
+ documentation: 'https://github.com/xing/fpm-fry/wiki/Plugin-env'
39
+ )
40
+ end
41
+ end
42
+ builder.recipe.dockerfile_hooks << AddEnv.new(env.dup.freeze)
43
+ end
44
+
45
+ end
@@ -40,7 +40,7 @@ module FPM::Fry::Plugin::Init
40
40
 
41
41
  private
42
42
  def self.detect(inspector)
43
- if inspector.link_target('/sbin/init') == '/lib/systemd/systemd'
43
+ if inspector.exists?('/lib/systemd/systemd')
44
44
  return System.new(:systemd, {})
45
45
  end
46
46
  if inspector.exists?('/etc/init')
@@ -61,9 +61,9 @@ private
61
61
 
62
62
  def self.detect_sysv(inspector)
63
63
  features = {
64
- chkconfig: inspector.exists?('/sbin/chkconfig'),
65
- 'update-rc.d': inspector.exists?('/usr/sbin/update-rc.d'),
66
- 'invoke-rc.d': inspector.exists?('/usr/sbin/invoke-rc.d')
64
+ :chkconfig => inspector.exists?('/sbin/chkconfig'),
65
+ :'update-rc.d' => inspector.exists?('/usr/sbin/update-rc.d'),
66
+ :'invoke-rc.d' => inspector.exists?('/usr/sbin/invoke-rc.d')
67
67
  }
68
68
  return System.new(:sysv,features)
69
69
  end
@@ -0,0 +1,75 @@
1
+ require 'fpm/fry/plugin'
2
+ require 'fpm/fry/chroot'
3
+
4
+ # Automatically adds the appropriate maintainer scripts for every systemd unit.
5
+ #
6
+ # @note experimental
7
+ #
8
+ # @example in a recipe
9
+ # plugin 'systemd' # no options required, just install your units in /lib/systemd/system
10
+ module FPM::Fry::Plugin::Systemd
11
+
12
+ # @api private
13
+ VALID_UNITS = /\A[a-z_0-9\-]+@?\.(service|socket|timer)\z/
14
+
15
+ # @api private
16
+ INSTANTIATED_UNITS = /\A[a-z_0-9\-]+@\.(service|socket|timer)\z/
17
+
18
+ # @api private
19
+ class Callback < Struct.new(:script_helper)
20
+
21
+ def call(_, package)
22
+ chroot = FPM::Fry::Chroot.new(package.staging_path)
23
+ files = chroot.entries('lib/systemd/system') - ['.','..']
24
+ valid, invalid = files.partition{|file| VALID_UNITS =~ file }
25
+ if invalid.any?
26
+ package.logger.warning("Found #{invalid.size} files in systemd unit path that are no systemd units", files: invalid)
27
+ end
28
+ units = valid.grep_v(INSTANTIATED_UNITS)
29
+ return if units.none?
30
+ package.logger.info("Added #{units.size} systemd units", units: valid)
31
+ script_helper.after_install_or_upgrade install(units)
32
+ script_helper.before_remove_entirely before_remove(units)
33
+ script_helper.after_remove_entirely after_remove(units)
34
+ end
35
+
36
+ private
37
+ def install(units)
38
+ <<BASH
39
+ if systemctl is-system-running ; then
40
+ systemctl preset #{units.join(' ')}
41
+ if systemctl is-enabled #{units.join(' ')} ; then
42
+ systemctl daemon-reload
43
+ systemctl restart #{units.join(' ')}
44
+ fi
45
+ fi
46
+ BASH
47
+ end
48
+
49
+ def before_remove(units)
50
+ <<BASH
51
+ if systemctl is-system-running ; then
52
+ systemctl disable --now #{units.join(' ')}
53
+ fi
54
+ BASH
55
+ end
56
+
57
+ def after_remove(units)
58
+ <<BASH
59
+ if systemctl is-system-running ; then
60
+ systemctl daemon-reload
61
+ systemctl reset-failed #{units.join(' ')}
62
+ fi
63
+ BASH
64
+ end
65
+
66
+ end
67
+
68
+ def self.apply(builder)
69
+ return unless builder.plugin('init').systemd?
70
+ builder.plugin('script_helper') do |sh|
71
+ builder.output_hooks << Callback.new(sh)
72
+ end
73
+ end
74
+
75
+ end
@@ -67,6 +67,7 @@ module FPM; module Fry
67
67
  # @return [FPM::Package] package
68
68
  # @api private
69
69
  def apply_output( package )
70
+ output_hooks.each{|h| h.call(self, package) }
70
71
  package.name = name
71
72
  package.version = version
72
73
  package.iteration = iteration
@@ -87,7 +88,6 @@ module FPM; module Fry
87
88
  end
88
89
  end
89
90
  end
90
- output_hooks.each{|h| h.call(self, package) }
91
91
  return package
92
92
  end
93
93
 
@@ -134,7 +134,9 @@ module FPM; module Fry
134
134
  attr_accessor :source
135
135
 
136
136
  attr_accessor :build_mounts
137
- attr_accessor :apt_setup
137
+
138
+ # @return [Array<#to_s>] steps that will be carried out before dependencies are installed
139
+ attr_accessor :before_dependencies_steps
138
140
 
139
141
  # @return [Array<#to_s>] steps that will be carried out before build
140
142
  attr_accessor :before_build_steps
@@ -151,16 +153,20 @@ module FPM; module Fry
151
153
  # @return [Array<#call>] hooks that will be called on the input package
152
154
  attr_accessor :input_hooks
153
155
 
156
+ # @return [Array<#call>] hooks that will be called when building the Dockerfile
157
+ attr_accessor :dockerfile_hooks
158
+
154
159
  def initialize
155
160
  @source = Source::Null
161
+ @before_dependencies_steps = []
156
162
  @before_build_steps = []
157
163
  @steps = []
158
164
  @packages = [PackageRecipe.new]
159
165
  @packages[0].files << '**'
160
166
  @build_depends = {}
161
167
  @input_hooks = []
168
+ @dockerfile_hooks = []
162
169
  @build_mounts = []
163
- @apt_setup = []
164
170
  end
165
171
 
166
172
  # Calculates all dependencies of this recipe
@@ -187,6 +193,16 @@ module FPM; module Fry
187
193
  return package
188
194
  end
189
195
 
196
+ # Filters the dockerfile
197
+ # @api experimental
198
+ # @param [Hash] df
199
+ def apply_dockerfile_hooks( df )
200
+ dockerfile_hooks.each do |hook|
201
+ hook.call(self, df)
202
+ end
203
+ return nil
204
+ end
205
+
190
206
  end
191
207
 
192
208
  end ; end
@@ -1,6 +1,8 @@
1
1
  require 'fpm/fry/recipe'
2
2
  require 'fpm/fry/recipe/error'
3
3
  require 'forwardable'
4
+ require 'fpm/fry/channel'
5
+
4
6
  module FPM::Fry
5
7
  class Recipe
6
8
 
@@ -29,6 +31,8 @@ module FPM::Fry
29
31
  @inspector = options[:inspector]
30
32
  end
31
33
 
34
+ # Returns the package type ( e.g. "debian" or "redhat" ).
35
+ # @return [String]
32
36
  def flavour
33
37
  variables[:flavour]
34
38
  end
@@ -38,9 +42,13 @@ module FPM::Fry
38
42
  end
39
43
  alias platform distribution
40
44
 
41
- def distribution_version
42
- variables[:distribution_version]
45
+ # The release version of the distribution ( e.g. "12.04" or "6.0.7" )
46
+ # @return [String]
47
+ def release
48
+ variables[:release]
43
49
  end
50
+
51
+ alias distribution_version release
44
52
  alias platform_version distribution_version
45
53
 
46
54
  def codename
@@ -200,7 +208,7 @@ module FPM::Fry
200
208
  variables = variables.dup
201
209
  variables.freeze
202
210
  @recipe = recipe
203
- @before_build = false
211
+ @steps = :steps
204
212
  register_default_source_types!
205
213
  super(variables, recipe.packages[0], options)
206
214
  end
@@ -231,7 +239,9 @@ module FPM::Fry
231
239
  end
232
240
 
233
241
  def apt_setup(cmd)
234
- recipe.apt_setup << cmd
242
+ before_dependencies do
243
+ bash cmd
244
+ end
235
245
  end
236
246
 
237
247
  def run(*args)
@@ -250,7 +260,10 @@ module FPM::Fry
250
260
  code = Recipe::Step.new(name, code)
251
261
  end
252
262
  # Don't do this at home
253
- if @before_build
263
+ case(@steps)
264
+ when :before_dependencies
265
+ recipe.before_dependencies_steps << code
266
+ when :before_build
254
267
  recipe.before_build_steps << code
255
268
  else
256
269
  recipe.steps << code
@@ -258,10 +271,17 @@ module FPM::Fry
258
271
  end
259
272
 
260
273
  def before_build
261
- @before_build = true
274
+ steps, @steps = @steps, :before_build
275
+ yield
276
+ ensure
277
+ @steps = steps
278
+ end
279
+
280
+ def before_dependencies
281
+ steps, @steps = @steps, :before_dependencies
262
282
  yield
263
283
  ensure
264
- @before_build = false
284
+ @steps = steps
265
285
  end
266
286
 
267
287
  def build_depends( name , options = {} )
@@ -26,6 +26,11 @@ module FPM; module Fry ; module Source
26
26
  def self.cachekey
27
27
  return '0' * 32
28
28
  end
29
+
30
+ # @return [String] common path prefix of all files that should be stripped
31
+ def self.prefix
32
+ return ""
33
+ end
29
34
  end
30
35
 
31
36
  # @see FPM::Fry::Source::Null::Cache
@@ -45,5 +50,27 @@ module FPM; module Fry ; module Source
45
50
  end
46
51
  end
47
52
 
53
+ # @api private
54
+ # @param dir [String] directory
55
+ # @return [String] prefix
56
+ def prefix(dir)
57
+ e = ::Dir.entries(dir)
58
+ if e.size != 3
59
+ return ""
60
+ end
61
+ other = (e - ['.','..']).first
62
+ path = File.join(dir, other)
63
+ if File.directory?( path )
64
+ pf = prefix(path)
65
+ if pf == ""
66
+ return other
67
+ else
68
+ return File.join(other, pf)
69
+ end
70
+ else
71
+ return ""
72
+ end
73
+ end
74
+
48
75
  end
49
76
  end ; end ; end
@@ -6,6 +6,8 @@ require 'zlib'
6
6
  require 'fpm/fry/source'
7
7
  require 'fpm/fry/exec'
8
8
  require 'cabin'
9
+ require 'cabin/channel'
10
+
9
11
  module FPM; module Fry ; module Source
10
12
  # Used to build from an archive.
11
13
  #
@@ -60,7 +62,7 @@ module FPM; module Fry ; module Source
60
62
  class Cache < Struct.new(:package,:tempdir)
61
63
  extend Forwardable
62
64
 
63
- def_delegators :package, :url, :checksum, :checksum_algorithm, :logger, :file_map
65
+ def_delegators :package, :url, :checksum, :checksum_algorithm, :logger, :file_map, :to
64
66
 
65
67
  # @return [String] cachekey which is equal to the checksum
66
68
  def cachekey
@@ -155,6 +157,32 @@ module FPM; module Fry ; module Source
155
157
  Exec['tar','-xf',tempfile,'-C',dst, logger: logger]
156
158
  end
157
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('/')
184
+ end
185
+
158
186
  protected
159
187
  def ioclass
160
188
  File
@@ -181,6 +209,22 @@ module FPM; module Fry ; module Source
181
209
  class ZipCache < Cache
182
210
 
183
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!
184
228
  if !::File.directory?( unpacked_tmpdir )
185
229
  workdir = unpacked_tmpdir + '.tmp'
186
230
  begin
@@ -192,14 +236,8 @@ module FPM; module Fry ; module Source
192
236
  copy_to( workdir )
193
237
  File.rename(workdir, unpacked_tmpdir)
194
238
  end
195
- return Exec::popen('tar','-c','.', chdir: unpacked_tmpdir)
196
239
  end
197
240
 
198
- def copy_to(dst)
199
- update!
200
- Exec['unzip', tempfile, '-d', dst ]
201
- end
202
- private
203
241
  def unpacked_tmpdir
204
242
  File.join(tempdir, cachekey)
205
243
  end
@@ -218,6 +256,10 @@ module FPM; module Fry ; module Source
218
256
  FileUtils.cp( tempfile, dst )
219
257
  end
220
258
 
259
+ def prefix
260
+ ""
261
+ end
262
+
221
263
  end
222
264
 
223
265
  CACHE_CLASSES = {
@@ -230,7 +272,7 @@ module FPM; module Fry ; module Source
230
272
  '.bundle' => PlainCache
231
273
  }
232
274
 
233
- attr :file_map, :data, :url, :checksum, :checksum_algorithm, :logger
275
+ attr :file_map, :data, :url, :checksum, :checksum_algorithm, :logger, :to
234
276
 
235
277
  # @param [URI] url
236
278
  # @param [Hash] options
@@ -243,7 +285,8 @@ module FPM; module Fry ; module Source
243
285
  @logger = options.fetch(:logger){ Cabin::Channel.get }
244
286
  @checksum = options[:checksum]
245
287
  @checksum_algorithm = guess_checksum_algorithm(options[:checksum])
246
- @file_map = options.fetch(:file_map){ {'' => ''} }
288
+ @file_map = options[:file_map]
289
+ @to = options[:to]
247
290
  end
248
291
 
249
292
  # Creates a cache.
@@ -2,6 +2,9 @@ require 'fpm/fry/source'
2
2
  require 'fpm/fry/exec'
3
3
  require 'fileutils'
4
4
  require 'digest'
5
+ require 'cabin/channel'
6
+ require 'fpm/fry/tar'
7
+
5
8
  module FPM; module Fry ; module Source
6
9
  class Dir
7
10
 
@@ -18,7 +21,7 @@ module FPM; module Fry ; module Source
18
21
  class Cache < Struct.new(:package, :dir)
19
22
  extend Forwardable
20
23
 
21
- def_delegators :package, :url, :logger, :file_map
24
+ def_delegators :package, :url, :logger, :file_map, :to
22
25
 
23
26
  def tar_io
24
27
  Exec::popen('tar','-c','.', chdir: dir, logger: logger)
@@ -36,9 +39,13 @@ module FPM; module Fry ; module Source
36
39
  end
37
40
  return dig.hexdigest
38
41
  end
42
+
43
+ def prefix
44
+ Source::prefix(dir)
45
+ end
39
46
  end
40
47
 
41
- attr :url, :logger, :file_map
48
+ attr :url, :logger, :file_map, :to
42
49
 
43
50
  def initialize( url, options = {} )
44
51
  @url = URI(url)
@@ -46,7 +53,8 @@ module FPM; module Fry ; module Source
46
53
  @url.path = File.expand_path(@url.path)
47
54
  end
48
55
  @logger = options.fetch(:logger){ Cabin::Channel.get }
49
- @file_map = options.fetch(:file_map){ {'' => ''} }
56
+ @file_map = options[:file_map]
57
+ @to = options[:to]
50
58
  end
51
59
 
52
60
  def build_cache(_)
@@ -43,7 +43,7 @@ module FPM; module Fry ; module Source
43
43
  class Cache < Struct.new(:package, :tempdir)
44
44
  extend Forwardable
45
45
 
46
- def_delegators :package, :url, :rev, :logger, :file_map
46
+ def_delegators :package, :url, :rev, :logger, :file_map, :to
47
47
 
48
48
  def tar_io
49
49
  Exec::popen(package.git, "--git-dir=#{repodir}",'archive','--format=tar','FETCH_HEAD', logger: logger)
@@ -59,6 +59,11 @@ module FPM; module Fry ; module Source
59
59
  def cachekey
60
60
  Exec::exec(package.git, "--git-dir=#{repodir}",'rev-parse','FETCH_HEAD^{tree}', logger: logger).chomp
61
61
  end
62
+
63
+ def prefix
64
+ ""
65
+ end
66
+
62
67
  private
63
68
  def initialize(*_)
64
69
  super
@@ -90,12 +95,15 @@ module FPM; module Fry ; module Source
90
95
  # @return [String] the git rev to pull (default "HEAD")
91
96
  attr :rev
92
97
 
93
- # @return [Hash<String,String>] the file map for generating a docker file
98
+ # @return [Hash<String,String>,nil] the file map for generating a docker file
94
99
  attr :file_map
95
100
 
96
101
  # @return [URI] the uri to pull from
97
102
  attr :url
98
103
 
104
+ # @return [String,nil]
105
+ attr :to
106
+
99
107
  # @param [URI] url the url to pull from
100
108
  # @param [Hash] options
101
109
  # @option options [Cabin::Channel] :logger (cabin default channel)
@@ -107,8 +115,9 @@ module FPM; module Fry ; module Source
107
115
  @url = URI(url)
108
116
  @logger = options.fetch(:logger){ Cabin::Channel.get }
109
117
  @rev = options[:branch] || options[:tag] || options[:rev] || 'HEAD'
110
- @file_map = options.fetch(:file_map){ {'' => ''} }
118
+ @file_map = options[:file_map]
111
119
  @git = options[:git] || 'git'
120
+ @to = options[:to]
112
121
  end
113
122
 
114
123
  # @param [String] tempdir
@@ -8,6 +8,7 @@ module FPM; module Fry ; module Source
8
8
  extend Forwardable
9
9
 
10
10
  def_delegators :package, :logger, :file_map
11
+ def_delegators :inner, :prefix, :to
11
12
 
12
13
  attr :inner
13
14
 
@@ -18,39 +19,64 @@ module FPM; module Fry ; module Source
18
19
  end
19
20
 
20
21
  def update!
21
- @updated ||= begin
22
- if !File.directory?(unpacked_tmpdir)
23
- workdir = unpacked_tmpdir + '.tmp'
22
+ return if @updated
23
+ if !File.directory?(unpacked_tmpdir)
24
+ workdir = unpacked_tmpdir + '.tmp'
25
+ begin
26
+ FileUtils.mkdir(workdir)
27
+ rescue Errno::EEXIST
28
+ FileUtils.rm_rf(workdir)
29
+ FileUtils.mkdir(workdir)
30
+ end
31
+ if inner.respond_to? :copy_to
32
+ inner.copy_to(workdir)
33
+ else
34
+ ex = Tar::Extractor.new(logger: logger)
35
+ tio = inner.tar_io
24
36
  begin
25
- FileUtils.mkdir(workdir)
26
- rescue Errno::EEXIST
27
- FileUtils.rm_rf(workdir)
28
- FileUtils.mkdir(workdir)
37
+ ex.extract(workdir, FPM::Fry::Tar::Reader.new(tio), chown: false)
38
+ ensure
39
+ tio.close
29
40
  end
30
- if inner.respond_to? :copy_to
31
- inner.copy_to(workdir)
32
- else
33
- ex = Tar::Extractor.new(logger: logger)
34
- tio = inner.tar_io
35
- begin
36
- ex.extract(workdir, ::Gem::Package::TarReader.new(tio), chown: false)
37
- ensure
38
- tio.close
41
+ end
42
+ base = workdir
43
+ if inner.respond_to? :prefix
44
+ base = File.expand_path(inner.prefix, base)
45
+ end
46
+ package.patches.each do |patch|
47
+ cmd = ['patch','-N','-p1','-i',patch[:file]]
48
+ chdir = base
49
+ if patch.key? :chdir
50
+ given_chdir = File.expand_path(patch[:chdir],workdir)
51
+ if given_chdir != chdir
52
+ chdir = given_chdir
53
+ else
54
+ logger.hint("You can remove the chdir: #{patch[:chdir].inspect} option for #{patch[:file]}. The given value is the default.", documentation: 'https://github.com/xing/fpm-fry/wiki/Source-patching#chdir' )
39
55
  end
40
56
  end
41
- package.patches.each do |patch|
42
- cmd = ['patch','-p1','-i',patch[:file]]
43
- chdir = File.expand_path(patch.fetch(:chdir,'.'),workdir)
44
- begin
45
- Fry::Exec[*cmd, chdir: chdir, logger: logger]
46
- rescue Exec::Failed => e
47
- raise CacheFailed.new(e, patch: patch[:file])
57
+ begin
58
+ Fry::Exec[*cmd, chdir: chdir, logger: logger]
59
+ rescue Exec::Failed => e
60
+ raise CacheFailed.new(e, patch: patch[:file])
61
+ end
62
+ end
63
+ File.rename(workdir, unpacked_tmpdir)
64
+ else
65
+ #
66
+ base = unpacked_tmpdir
67
+ if inner.respond_to? :prefix
68
+ base = File.expand_path(inner.prefix, base)
69
+ end
70
+ package.patches.each do |patch|
71
+ if patch.key? :chdir
72
+ given_chdir = File.expand_path(patch[:chdir],unpacked_tmpdir)
73
+ if given_chdir == base
74
+ logger.hint("You can remove the chdir: #{patch[:chdir].inspect} option for #{patch[:file]}. The given value is the default.", documentation: 'https://github.com/xing/fpm-fry/wiki/Source-patching#chdir' )
48
75
  end
49
76
  end
50
- File.rename(workdir, unpacked_tmpdir)
51
77
  end
52
- true
53
78
  end
79
+ @updated = true
54
80
  end
55
81
  private :update!
56
82
 
@@ -119,4 +145,3 @@ module FPM; module Fry ; module Source
119
145
  end
120
146
 
121
147
  end ; end ; end
122
-
data/lib/fpm/fry/tar.rb CHANGED
@@ -1,7 +1,70 @@
1
+ require "rubygems/package"
2
+ require 'fpm/fry/channel'
3
+
1
4
  module FPM; module Fry; end ; end
2
5
 
3
6
  class FPM::Fry::Tar
4
7
 
8
+ class Reader
9
+ include Enumerable
10
+
11
+ def initialize(io)
12
+ @reader = Gem::Package::TarReader.new(io)
13
+ end
14
+
15
+ def each
16
+ return to_enum(:each) unless block_given?
17
+
18
+ last_pax_header = nil
19
+ @reader.each do |entry|
20
+ if entry.header.typeflag == 'x'
21
+ last_pax_header = extract_pax_header(entry.read)
22
+ else
23
+ if last_pax_header && (path = last_pax_header["path"])
24
+ entry.header.instance_variable_set :@name, path
25
+ last_pax_header = nil
26
+ end
27
+ yield entry
28
+ end
29
+ end
30
+ end
31
+
32
+ def map
33
+ return to_enum(:map) unless block_given?
34
+
35
+ res = []
36
+ each do |entry|
37
+ res << yield(entry)
38
+ end
39
+ res
40
+ end
41
+
42
+ private
43
+
44
+ def extract_pax_header(string)
45
+ res = {}
46
+ s = StringIO.new(string)
47
+ while !s.eof?
48
+ total_len = 0
49
+ prefix_len = 0
50
+ # read number prefix and following blank
51
+ while (c = s.getc) && (c =~ /\d/)
52
+ total_len = 10 * total_len + c.to_i
53
+ prefix_len += 1
54
+ end
55
+ field = s.read(total_len - prefix_len - 2)
56
+ if field =~ /\A([^=]+)=(.+)\z/
57
+ res[$1] = $2
58
+ else
59
+ raise "malformed pax header: #{field}"
60
+ end
61
+ s.read(1) # read trailing newline
62
+ end
63
+ res
64
+ end
65
+
66
+ end
67
+
5
68
  class Extractor
6
69
 
7
70
  def initialize( options = {} )
@@ -1,7 +1,7 @@
1
1
  #!/bin/bash
2
2
 
3
3
  case "$1" in
4
- remove)
4
+ remove|purge)
5
5
  <%= remove.join("\n") %>
6
6
  ;;
7
7
  upgrade)
@@ -1,7 +1,7 @@
1
1
  #!/bin/bash
2
2
 
3
3
  case "$1" in
4
- remove)
4
+ remove|purge)
5
5
  <%= remove.join("\n") %>
6
6
  ;;
7
7
  upgrade)
data/lib/fpm/fry/ui.rb CHANGED
@@ -3,7 +3,7 @@ require 'fpm/fry/channel'
3
3
  module FPM; module Fry
4
4
  class UI < Struct.new(:out, :err, :logger, :tmpdir)
5
5
 
6
- def initialize( out = STDOUT, err = STDERR, logger = nil , tmpdir = '/tmp/fpm-fry' )
6
+ def initialize( out: STDOUT, err: STDERR, logger: nil , tmpdir: '/tmp/fpm-fry' )
7
7
  logger ||= Channel.new.tap{|chan| chan.subscribe(Cabin::NiceOutput.new(out)) }
8
8
  FileUtils.mkdir_p( tmpdir )
9
9
  super( out, err, logger, tmpdir )
@@ -1,5 +1,6 @@
1
1
  require 'fpm'
2
2
  require 'fpm/package'
3
+ require 'fpm/fry/channel'
3
4
 
4
5
  require 'fpm/fry/client'
5
6
 
metadata CHANGED
@@ -1,14 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fpm-fry
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.7
5
5
  platform: ruby
6
6
  authors:
7
+ - Maxime Lagresle
8
+ - Stefan Kaes
9
+ - Sebastian Brandt
7
10
  - Hannes Georg
11
+ - Julian Tabel
8
12
  autorequire:
9
13
  bindir: bin
10
14
  cert_chain: []
11
- date: 2016-10-19 00:00:00.000000000 Z
15
+ date: 2021-03-02 00:00:00.000000000 Z
12
16
  dependencies:
13
17
  - !ruby/object:Gem::Dependency
14
18
  name: excon
@@ -16,14 +20,14 @@ dependencies:
16
20
  requirements:
17
21
  - - "~>"
18
22
  - !ruby/object:Gem::Version
19
- version: '0.30'
23
+ version: 0.71.0
20
24
  type: :runtime
21
25
  prerelease: false
22
26
  version_requirements: !ruby/object:Gem::Requirement
23
27
  requirements:
24
28
  - - "~>"
25
29
  - !ruby/object:Gem::Version
26
- version: '0.30'
30
+ version: 0.71.0
27
31
  - !ruby/object:Gem::Dependency
28
32
  name: fpm
29
33
  requirement: !ruby/object:Gem::Requirement
@@ -39,21 +43,83 @@ dependencies:
39
43
  - !ruby/object:Gem::Version
40
44
  version: '1.0'
41
45
  - !ruby/object:Gem::Dependency
42
- name: json
46
+ name: rake
43
47
  requirement: !ruby/object:Gem::Requirement
44
48
  requirements:
45
49
  - - "~>"
46
50
  - !ruby/object:Gem::Version
47
- version: '1.8'
48
- type: :runtime
51
+ version: '12.0'
52
+ type: :development
53
+ prerelease: false
54
+ version_requirements: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - "~>"
57
+ - !ruby/object:Gem::Version
58
+ version: '12.0'
59
+ - !ruby/object:Gem::Dependency
60
+ name: rspec
61
+ requirement: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: 3.0.0
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ type: :development
70
+ prerelease: false
71
+ version_requirements: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 3.0.0
76
+ - - "~>"
77
+ - !ruby/object:Gem::Version
78
+ version: '3.0'
79
+ - !ruby/object:Gem::Dependency
80
+ name: webmock
81
+ requirement: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - "~>"
84
+ - !ruby/object:Gem::Version
85
+ version: '3.0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - "~>"
91
+ - !ruby/object:Gem::Version
92
+ version: '3.0'
93
+ - !ruby/object:Gem::Dependency
94
+ name: coveralls
95
+ requirement: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - "~>"
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ type: :development
101
+ prerelease: false
102
+ version_requirements: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - "~>"
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ - !ruby/object:Gem::Dependency
108
+ name: simplecov
109
+ requirement: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - "~>"
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ type: :development
49
115
  prerelease: false
50
116
  version_requirements: !ruby/object:Gem::Requirement
51
117
  requirements:
52
118
  - - "~>"
53
119
  - !ruby/object:Gem::Version
54
- version: '1.8'
120
+ version: '0'
55
121
  description: deep-fried package builder
56
- email: hannes.georg@xing.com
122
+ email: maxime.lagresle@xing.com
57
123
  executables:
58
124
  - fpm-fry
59
125
  extensions: []
@@ -75,14 +141,17 @@ files:
75
141
  - lib/fpm/fry/joined_io.rb
76
142
  - lib/fpm/fry/plugin.rb
77
143
  - lib/fpm/fry/plugin/alternatives.rb
144
+ - lib/fpm/fry/plugin/apt.rb
78
145
  - lib/fpm/fry/plugin/config.rb
79
146
  - lib/fpm/fry/plugin/edit_staging.rb
147
+ - lib/fpm/fry/plugin/env.rb
80
148
  - lib/fpm/fry/plugin/exclude.rb
81
149
  - lib/fpm/fry/plugin/init.rb
82
150
  - lib/fpm/fry/plugin/platforms.rb
83
151
  - lib/fpm/fry/plugin/same_version.rb
84
152
  - lib/fpm/fry/plugin/script_helper.rb
85
153
  - lib/fpm/fry/plugin/service.rb
154
+ - lib/fpm/fry/plugin/systemd.rb
86
155
  - lib/fpm/fry/plugin/user.rb
87
156
  - lib/fpm/fry/recipe.rb
88
157
  - lib/fpm/fry/recipe/builder.rb
@@ -127,8 +196,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
127
196
  - !ruby/object:Gem::Version
128
197
  version: '0'
129
198
  requirements: []
130
- rubyforge_project:
131
- rubygems_version: 2.5.1
199
+ rubygems_version: 3.0.3
132
200
  signing_key:
133
201
  specification_version: 4
134
202
  summary: FPM Fry