fpm-fry 0.3.0 → 0.4.7

Sign up to get free protection for your applications and to get access to all the features.
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