fpm-aeppert 1.6.2 → 1.11.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.
@@ -20,6 +20,7 @@ llasram
20
20
  sbuss
21
21
  Brett Gailey (github: dnbert)
22
22
  Daniel Haskin (github: djhaskin987)
23
+ Richard Grainger (github: liger1978)
23
24
 
24
25
  If you have contributed (bug reports, feature requests, help in IRC, blog
25
26
  posts, code, etc) and aren't listed here, please let me know if you wish to be
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  (This is an MIT-style license)
2
2
 
3
- Copyright (c) 2011-2016 Jordan Sissel and contributors.
3
+ Copyright (c) 2011-2017 Jordan Sissel and contributors.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/bin/fpm CHANGED
@@ -1,6 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "rubygems"
4
3
  $: << File.join(File.dirname(__FILE__), "..", "lib")
5
4
  require "fpm"
6
5
  require "fpm/command"
data/lib/fpm.rb CHANGED
@@ -18,3 +18,4 @@ require "fpm/package/p5p"
18
18
  require "fpm/package/pkgin"
19
19
  require "fpm/package/freebsd"
20
20
  require "fpm/package/apk"
21
+ require "fpm/package/snap"
@@ -45,13 +45,13 @@ class FPM::Command < Clamp::Command
45
45
  return lines.join("\n")
46
46
  end # def help
47
47
 
48
- option "-t", "OUTPUT_TYPE",
48
+ option ["-t", "--output-type"], "OUTPUT_TYPE",
49
49
  "the type of package you want to create (deb, rpm, solaris, etc)",
50
50
  :attribute_name => :output_type
51
- option "-s", "INPUT_TYPE",
51
+ option ["-s", "--input-type"], "INPUT_TYPE",
52
52
  "the package type to use as input (gem, rpm, python, etc)",
53
53
  :attribute_name => :input_type
54
- option "-C", "CHDIR",
54
+ option ["-C", "--chdir"], "CHDIR",
55
55
  "Change directory to here before searching for files",
56
56
  :attribute_name => :chdir
57
57
  option "--prefix", "PREFIX",
@@ -233,6 +233,20 @@ class FPM::Command < Clamp::Command
233
233
  "copying, downloading, etc. Roughly any scratch space fpm needs to build " \
234
234
  "your package.", :default => Dir.tmpdir
235
235
 
236
+ option "--source-date-epoch-from-changelog", :flag,
237
+ "Use release date from changelog as timestamp on generated files to reduce nondeterminism. " \
238
+ "Experimental; only implemented for gem so far. ",
239
+ :default => false
240
+
241
+ option "--source-date-epoch-default", "SOURCE_DATE_EPOCH_DEFAULT",
242
+ "If no release date otherwise specified, use this value as timestamp on generated files to reduce nondeterminism. " \
243
+ "Reproducible build environments such as dpkg-dev and rpmbuild set this via envionment variable SOURCE_DATE_EPOCH " \
244
+ "variable to the integer unix timestamp to use in generated archives, " \
245
+ "and expect tools like fpm to use it as a hint to avoid nondeterministic output. " \
246
+ "This is a Unix timestamp, i.e. number of seconds since 1 Jan 1970 UTC. " \
247
+ "See https://reproducible-builds.org/specs/source-date-epoch ",
248
+ :environment_variable => "SOURCE_DATE_EPOCH"
249
+
236
250
  parameter "[ARGS] ...",
237
251
  "Inputs to the source package type. For the 'dir' type, this is the files" \
238
252
  " and directories you want to include in the package. For others, like " \
@@ -256,12 +270,6 @@ class FPM::Command < Clamp::Command
256
270
 
257
271
  # Execute this command. See Clamp::Command#execute and Clamp's documentation
258
272
  def execute
259
- # Short-circuit if someone simply runs `fpm --version`
260
- if ARGV == [ "--version" ]
261
- puts FPM::VERSION
262
- return 0
263
- end
264
-
265
273
  logger.level = :warn
266
274
  logger.level = :info if verbose? # --verbose
267
275
  logger.level = :debug if debug? # --debug
@@ -269,7 +277,6 @@ class FPM::Command < Clamp::Command
269
277
  logger.level = log_level.to_sym
270
278
  end
271
279
 
272
-
273
280
  if (stray_flags = args.grep(/^-/); stray_flags.any?)
274
281
  logger.warn("All flags should be before the first argument " \
275
282
  "(stray flags found: #{stray_flags}")
@@ -514,9 +521,16 @@ class FPM::Command < Clamp::Command
514
521
  end
515
522
  end # def execute
516
523
 
517
- def run(*run_args)
524
+ def run(run_args)
518
525
  logger.subscribe(STDOUT)
519
526
 
527
+ # Short circuit for a `fpm --version` or `fpm -v` short invocation that
528
+ # is the user asking us for the version of fpm.
529
+ if run_args == [ "-v" ] || run_args == [ "--version" ]
530
+ puts FPM::VERSION
531
+ return 0
532
+ end
533
+
520
534
  # fpm initialization files, note the order of the following array is
521
535
  # important, try .fpm in users home directory first and then the current
522
536
  # directory
@@ -557,7 +571,7 @@ class FPM::Command < Clamp::Command
557
571
 
558
572
  ARGV.unshift(*flags)
559
573
  ARGV.push(*args)
560
- super(*run_args)
574
+ super(run_args)
561
575
  rescue FPM::Package::InvalidArgument => e
562
576
  logger.error("Invalid package argument: #{e}")
563
577
  return 1
@@ -3,7 +3,8 @@ require "fpm/util" # local
3
3
  require "pathname" # stdlib
4
4
  require "find"
5
5
  require "tmpdir" # stdlib
6
- require "backports" # gem 'backports'
6
+ require "ostruct"
7
+ require "backports/latest"
7
8
  require "socket" # stdlib, for Socket.gethostname
8
9
  require "shellwords" # stdlib, for Shellwords.escape
9
10
  require "erb" # stdlib, for template processing
@@ -118,7 +119,10 @@ class FPM::Package
118
119
 
119
120
  def initialize
120
121
  # Attributes for this specific package
121
- @attributes = {}
122
+ @attributes = {
123
+ # Default work location
124
+ :workdir => ::Dir.tmpdir
125
+ }
122
126
 
123
127
  # Reference
124
128
  # http://www.debian.org/doc/manuals/maint-guide/first.en.html
@@ -175,8 +179,8 @@ class FPM::Package
175
179
  @directories = []
176
180
  @attrs = {}
177
181
 
178
- staging_path
179
182
  build_path
183
+ # Dont' initialize staging_path just yet, do it lazily so subclass can get a word in.
180
184
  end # def initialize
181
185
 
182
186
  # Get the 'type' for this instance.
@@ -386,7 +390,7 @@ class FPM::Package
386
390
  installdir = staging_path
387
391
  end
388
392
 
389
- Find.find(staging_path) do |path|
393
+ Find.find(installdir) do |path|
390
394
  match_path = path.sub("#{installdir.chomp('/')}/", '')
391
395
 
392
396
  attributes[:excludes].each do |wildcard|
@@ -492,6 +496,22 @@ class FPM::Package
492
496
  return scripts.include?(name)
493
497
  end # def script?
494
498
 
499
+ # write all scripts to .scripts (tar and dir)
500
+ def write_scripts
501
+ scripts_path = File.join(staging_path, ".scripts")
502
+ target_scripts = [:before_install, :after_install, :before_remove, :after_remove]
503
+ if target_scripts.any? {|name| script?(name)}
504
+ ::Dir.mkdir(scripts_path)
505
+ target_scripts.each do |name|
506
+ next unless script?(name)
507
+ out = File.join(scripts_path, name.to_s)
508
+ logger.debug('Writing script', :source => name, :target => out)
509
+ File.write(out, script(name))
510
+ File.chmod(0755, out)
511
+ end
512
+ end
513
+ end
514
+
495
515
  # Get the contents of the script by a given name.
496
516
  #
497
517
  # If template_scripts? is set in attributes (often by the --template-scripts
@@ -3,12 +3,12 @@ require "fpm/namespace"
3
3
  require "fpm/package"
4
4
  require "fpm/errors"
5
5
  require "fpm/util"
6
- require "backports"
6
+ require "backports/latest"
7
7
  require "fileutils"
8
8
  require "digest"
9
9
  require 'digest/sha1'
10
10
 
11
- # Support for debian packages (.deb files)
11
+ # Support for Alpine packages (.apk files)
12
12
  #
13
13
  # This class supports both input and output of packages.
14
14
  class FPM::Package::APK< FPM::Package
@@ -144,9 +144,9 @@ class FPM::Package::APK< FPM::Package
144
144
  scripts = {}
145
145
 
146
146
  scripts = register_script('post-install', :after_install, scripts)
147
- scripts = register_script('post-install', :before_install, scripts)
148
- scripts = register_script('post-install', :before_upgrade, scripts)
149
- scripts = register_script('post-install', :after_upgrade, scripts)
147
+ scripts = register_script('pre-install', :before_install, scripts)
148
+ scripts = register_script('pre-upgrade', :before_upgrade, scripts)
149
+ scripts = register_script('post-upgrade', :after_upgrade, scripts)
150
150
  scripts = register_script('pre-deinstall', :before_remove, scripts)
151
151
  scripts = register_script('post-deinstall', :after_remove, scripts)
152
152
 
@@ -238,7 +238,7 @@ class FPM::Package::APK< FPM::Package
238
238
  record_length = determine_record_length(record_length)
239
239
 
240
240
  until(data.length == record_length)
241
- data += file.read(TAR_CHUNK_SIZE)
241
+ data << file.read(TAR_CHUNK_SIZE)
242
242
  end
243
243
 
244
244
  # Clear ownership fields
@@ -25,6 +25,9 @@ class FPM::Package::CPAN < FPM::Package
25
25
  option "--test", :flag,
26
26
  "Run the tests before packaging?", :default => true
27
27
 
28
+ option "--verbose", :flag,
29
+ "Produce verbose output from cpanm?", :default => false
30
+
28
31
  option "--perl-lib-path", "PERL_LIB_PATH",
29
32
  "Path of target Perl Libraries"
30
33
 
@@ -49,6 +52,7 @@ class FPM::Package::CPAN < FPM::Package
49
52
 
50
53
  if File.exist?(package)
51
54
  moduledir = package
55
+ result = {}
52
56
  else
53
57
  result = search(package)
54
58
  tarball = download(result, version)
@@ -98,6 +102,12 @@ class FPM::Package::CPAN < FPM::Package
98
102
  self.name = fix_name(metadata["name"])
99
103
  end
100
104
 
105
+ unless metadata["module"].nil?
106
+ metadata["module"].each do |m|
107
+ self.provides << cap_name(m["name"]) + " = #{self.version}"
108
+ end
109
+ end
110
+
101
111
  # author is not always set or it may be a string instead of an array
102
112
  self.vendor = case metadata["author"]
103
113
  when String; metadata["author"]
@@ -130,6 +140,7 @@ class FPM::Package::CPAN < FPM::Package
130
140
  cpanm_flags += ["--mirror", "#{attributes[:cpan_mirror]}"] if !attributes[:cpan_mirror].nil?
131
141
  cpanm_flags += ["--mirror-only"] if attributes[:cpan_mirror_only?] && !attributes[:cpan_mirror].nil?
132
142
  cpanm_flags += ["--force"] if attributes[:cpan_cpanm_force?]
143
+ cpanm_flags += ["--verbose"] if attributes[:cpan_verbose?]
133
144
 
134
145
  safesystem(attributes[:cpan_cpanm_bin], *cpanm_flags)
135
146
 
@@ -149,16 +160,16 @@ class FPM::Package::CPAN < FPM::Package
149
160
  found_dependencies.each do |dep_name, version|
150
161
  # Special case for representing perl core as a version.
151
162
  if dep_name == "perl"
163
+ m = version.to_s.match(/^(\d)\.(\d{3})(\d{3})$/)
164
+ if m
165
+ version = m[1] + '.' + m[2].sub(/^0*/, '') + '.' + m[3].sub(/^0*/, '')
166
+ end
152
167
  self.dependencies << "#{dep_name} >= #{version}"
153
168
  next
154
169
  end
155
170
  dep = search(dep_name)
156
171
 
157
- if dep.include?("distribution")
158
- name = fix_name(dep["distribution"])
159
- else
160
- name = fix_name(dep_name)
161
- end
172
+ name = cap_name(dep_name)
162
173
 
163
174
  if version.to_s == "0"
164
175
  # Assume 'Foo = 0' means any version?
@@ -166,12 +177,14 @@ class FPM::Package::CPAN < FPM::Package
166
177
  else
167
178
  # The 'version' string can be something complex like:
168
179
  # ">= 0, != 1.0, != 1.2"
180
+ # If it is not specified explicitly, require the given
181
+ # version or newer, as that is all CPAN itself enforces
169
182
  if version.is_a?(String)
170
183
  version.split(/\s*,\s*/).each do |v|
171
184
  if v =~ /\s*[><=]/
172
185
  self.dependencies << "#{name} #{v}"
173
186
  else
174
- self.dependencies << "#{name} = #{v}"
187
+ self.dependencies << "#{name} >= #{v}"
175
188
  end
176
189
  end
177
190
  else
@@ -232,11 +245,7 @@ class FPM::Package::CPAN < FPM::Package
232
245
  # Empty install_base to avoid local::lib being used.
233
246
  "INSTALL_BASE=")
234
247
  end
235
- if attributes[:cpan_test?]
236
- make = [ "env", "PERL5LIB=#{build_path("cpan/lib/perl5")}", "make" ]
237
- else
238
- make = [ "make" ]
239
- end
248
+ make = [ "env", "PERL5LIB=#{build_path("cpan/lib/perl5")}", "make" ]
240
249
  safesystem(*make)
241
250
  safesystem(*(make + ["test"])) if attributes[:cpan_test?]
242
251
  safesystem(*(make + ["DESTDIR=#{staging_path}", "install"]))
@@ -258,7 +267,7 @@ class FPM::Package::CPAN < FPM::Package
258
267
  :path => path.gsub(staging_path, ""))
259
268
  File.unlink(path)
260
269
  end
261
-
270
+
262
271
  # Remove useless .packlist files and their empty parent folders
263
272
  # https://github.com/jordansissel/fpm/issues/1179
264
273
  ::Dir.glob(File.join(staging_path, glob_prefix, "**/.packlist")).each do |path|
@@ -308,38 +317,35 @@ class FPM::Package::CPAN < FPM::Package
308
317
  :distribution => distribution,
309
318
  :version => cpan_version)
310
319
 
311
- # default to latest versionunless we specify one
320
+ # default to latest version unless we specify one
312
321
  if cpan_version.nil?
313
- self.version = metadata["version"]
322
+ self.version = "#{metadata["version"]}"
314
323
  else
315
- if metadata["version"] =~ /^v\d/
316
- self.version = "v#{cpan_version}"
317
- else
318
- self.version = cpan_version
319
- end
324
+ self.version = "#{cpan_version}"
320
325
  end
321
326
 
322
- metacpan_release_url = "http://api.metacpan.org/v0/release/#{author}/#{distribution}-#{self.version}"
327
+ # Search metacpan to get download URL for this version of the module
328
+ metacpan_search_url = "https://fastapi.metacpan.org/v1/release/_search"
329
+ metacpan_search_query = '{"fields":["download_url"],"filter":{"term":{"name":"' + "#{distribution}-#{self.version}" + '"}}}'
323
330
  begin
324
- release_response = httpfetch(metacpan_release_url)
331
+ search_response = httppost(metacpan_search_url,metacpan_search_query)
325
332
  rescue Net::HTTPServerException => e
326
333
  logger.error("metacpan release query failed.", :error => e.message,
327
- :url => metacpan_release_url)
334
+ :url => metacpan_search_url)
328
335
  raise FPM::InvalidPackageConfiguration, "metacpan release query failed"
329
336
  end
330
337
 
331
- data = release_response.body
338
+ data = search_response.body
332
339
  release_metadata = JSON.parse(data)
333
- archive = release_metadata["archive"]
334
340
 
335
- # should probably be basepathed from the url
336
- tarball = File.basename(archive)
341
+ download_url = release_metadata['hits']['hits'][0]['fields']['download_url']
342
+ download_path = URI.parse(download_url).path
343
+ tarball = File.basename(download_path)
337
344
 
338
345
  url_base = "http://www.cpan.org/"
339
346
  url_base = "#{attributes[:cpan_mirror]}" if !attributes[:cpan_mirror].nil?
340
347
 
341
- #url = "http://www.cpan.org/CPAN/authors/id/#{author[0,1]}/#{author[0,2]}/#{author}/#{tarball}"
342
- url = "#{url_base}/authors/id/#{author[0,1]}/#{author[0,2]}/#{author}/#{archive}"
348
+ url = "#{url_base}#{download_path}"
343
349
  logger.debug("Fetching perl module", :url => url)
344
350
 
345
351
  begin
@@ -360,7 +366,7 @@ class FPM::Package::CPAN < FPM::Package
360
366
 
361
367
  def search(package)
362
368
  logger.info("Asking metacpan about a module", :module => package)
363
- metacpan_url = "http://api.metacpan.org/v0/module/" + package
369
+ metacpan_url = "https://fastapi.metacpan.org/v1/module/" + package
364
370
  begin
365
371
  response = httpfetch(metacpan_url)
366
372
  rescue Net::HTTPServerException => e
@@ -378,6 +384,10 @@ class FPM::Package::CPAN < FPM::Package
378
384
  return metadata
379
385
  end # def metadata
380
386
 
387
+ def cap_name(name)
388
+ return "perl(" + name.gsub("-", "::") + ")"
389
+ end # def cap_name
390
+
381
391
  def fix_name(name)
382
392
  case name
383
393
  when "perl"; return "perl"
@@ -393,6 +403,7 @@ class FPM::Package::CPAN < FPM::Package
393
403
  else
394
404
  http = Net::HTTP.new(uri.host, uri.port)
395
405
  end
406
+ http.use_ssl = uri.scheme == 'https'
396
407
  response = http.request(Net::HTTP::Get.new(uri.request_uri))
397
408
  case response
398
409
  when Net::HTTPSuccess; return response
@@ -401,5 +412,22 @@ class FPM::Package::CPAN < FPM::Package
401
412
  end
402
413
  end
403
414
 
415
+ def httppost(url, body)
416
+ uri = URI.parse(url)
417
+ if ENV['http_proxy']
418
+ proxy = URI.parse(ENV['http_proxy'])
419
+ http = Net::HTTP.Proxy(proxy.host,proxy.port,proxy.user,proxy.password).new(uri.host, uri.port)
420
+ else
421
+ http = Net::HTTP.new(uri.host, uri.port)
422
+ end
423
+ http.use_ssl = uri.scheme == 'https'
424
+ response = http.post(uri.request_uri, body)
425
+ case response
426
+ when Net::HTTPSuccess; return response
427
+ when Net::HTTPRedirection; return httppost(response["location"])
428
+ else; response.error!
429
+ end
430
+ end
431
+
404
432
  public(:input)
405
433
  end # class FPM::Package::NPM
@@ -3,7 +3,7 @@ require "fpm/namespace"
3
3
  require "fpm/package"
4
4
  require "fpm/errors"
5
5
  require "fpm/util"
6
- require "backports"
6
+ require "backports/latest"
7
7
  require "fileutils"
8
8
  require "digest"
9
9
 
@@ -18,10 +18,11 @@ class FPM::Package::Deb < FPM::Package
18
18
  :after_install => "postinst",
19
19
  :before_remove => "prerm",
20
20
  :after_remove => "postrm",
21
+ :after_purge => "postrm",
21
22
  } unless defined?(SCRIPT_MAP)
22
23
 
23
24
  # The list of supported compression types. Default is gz (gzip)
24
- COMPRESSION_TYPES = [ "gz", "bzip2", "xz" ]
25
+ COMPRESSION_TYPES = [ "gz", "bzip2", "xz", "none" ]
25
26
 
26
27
  option "--ignore-iteration-in-dependencies", :flag,
27
28
  "For '=' (equal) dependencies, allow iterations on the specified " \
@@ -49,6 +50,8 @@ class FPM::Package::Deb < FPM::Package
49
50
  value
50
51
  end
51
52
 
53
+ option "--dist", "DIST-TAG", "Set the deb distribution.", :default => "unstable"
54
+
52
55
  # Take care about the case when we want custom control file but still use fpm ...
53
56
  option "--custom-control", "FILEPATH",
54
57
  "Custom version of the Debian control file." do |control|
@@ -87,6 +90,10 @@ class FPM::Package::Deb < FPM::Package
87
90
  File.expand_path(file)
88
91
  end
89
92
 
93
+ option "--generate-changes", :flag,
94
+ "Generate PACKAGENAME.changes file.",
95
+ :default => false
96
+
90
97
  option "--upstream-changelog", "FILEPATH", "Add FILEPATH as upstream changelog" do |file|
91
98
  File.expand_path(file)
92
99
  end
@@ -121,6 +128,18 @@ class FPM::Package::Deb < FPM::Package
121
128
  next @activated_triggers
122
129
  end
123
130
 
131
+ option "--interest-noawait", "EVENT", "Package is interested in EVENT trigger without awaiting" do |event|
132
+ @interested_noawait_triggers ||= []
133
+ @interested_noawait_triggers << event
134
+ next @interested_noawait_triggers
135
+ end
136
+
137
+ option "--activate-noawait", "EVENT", "Package activates EVENT trigger" do |event|
138
+ @activated_noawait_triggers ||= []
139
+ @activated_noawait_triggers << event
140
+ next @activated_noawait_triggers
141
+ end
142
+
124
143
  option "--field", "'FIELD: VALUE'", "Add custom field to the control file" do |fv|
125
144
  @custom_fields ||= {}
126
145
  field, value = fv.split(/: */, 2)
@@ -162,8 +181,18 @@ class FPM::Package::Deb < FPM::Package
162
181
  next File.expand_path(file)
163
182
  end
164
183
 
184
+ option "--systemd-enable", :flag , "Enable service on install or upgrade", :default => false
185
+
186
+ option "--systemd-auto-start", :flag , "Start service after install or upgrade", :default => false
187
+
165
188
  option "--systemd-restart-after-upgrade", :flag , "Restart service after upgrade", :default => true
166
189
 
190
+ option "--after-purge", "FILE",
191
+ "A script to be run after package removal to purge remaining (config) files " \
192
+ "(a.k.a. postrm purge within apt-get purge)" do |val|
193
+ File.expand_path(val) # Get the full path to the script
194
+ end # --after-purge
195
+
167
196
  def initialize(*args)
168
197
  super(*args)
169
198
  attributes[:deb_priority] = "extra"
@@ -240,10 +269,32 @@ class FPM::Package::Deb < FPM::Package
240
269
  end # def input
241
270
 
242
271
  def extract_info(package)
272
+ compression = `#{ar_cmd[0]} t #{package}`.split("\n").grep(/control.tar/).first.split(".").last
273
+ case compression
274
+ when "gz"
275
+ controltar = "control.tar.gz"
276
+ compression = "-z"
277
+ when "bzip2","bz2"
278
+ controltar = "control.tar.bz2"
279
+ compression = "-j"
280
+ when "xz"
281
+ controltar = "control.tar.xz"
282
+ compression = "-J"
283
+ when 'tar'
284
+ controltar = "control.tar"
285
+ compression = ""
286
+ when nil
287
+ raise FPM::InvalidPackageConfiguration, "Missing control.tar in deb source package #{package}"
288
+ else
289
+ raise FPM::InvalidPackageConfiguration,
290
+ "Unknown compression type '#{compression}' for control.tar in deb source package #{package}"
291
+ end
292
+
243
293
  build_path("control").tap do |path|
244
294
  FileUtils.mkdir(path) if !File.directory?(path)
295
+ # unpack the control.tar.{,gz,bz2,xz} from the deb package into staging_path
245
296
  # Unpack the control tarball
246
- safesystem("ar p #{package} control.tar.gz | tar -zxf - -C #{path}")
297
+ safesystem(ar_cmd[0] + " p #{package} #{controltar} | tar #{compression} -xf - -C #{path}")
247
298
 
248
299
  control = File.read(File.join(path, "control"))
249
300
 
@@ -340,26 +391,29 @@ class FPM::Package::Deb < FPM::Package
340
391
 
341
392
  def extract_files(package)
342
393
  # Find out the compression type
343
- compression = `ar t #{package}`.split("\n").grep(/data.tar/).first.split(".").last
394
+ compression = `#{ar_cmd[0]} t #{package}`.split("\n").grep(/data.tar/).first.split(".").last
344
395
  case compression
345
396
  when "gz"
346
397
  datatar = "data.tar.gz"
347
398
  compression = "-z"
348
- when "bzip2"
399
+ when "bzip2","bz2"
349
400
  datatar = "data.tar.bz2"
350
401
  compression = "-j"
351
402
  when "xz"
352
403
  datatar = "data.tar.xz"
353
404
  compression = "-J"
405
+ when 'tar'
406
+ datatar = "data.tar"
407
+ compression = ""
408
+ when nil
409
+ raise FPM::InvalidPackageConfiguration, "Missing data.tar in deb source package #{package}"
354
410
  else
355
411
  raise FPM::InvalidPackageConfiguration,
356
- "Unknown compression type '#{self.attributes[:deb_compression]}' "
357
- "in deb source package #{package}"
412
+ "Unknown compression type '#{compression}' for data.tar in deb source package #{package}"
358
413
  end
359
414
 
360
415
  # unpack the data.tar.{gz,bz2,xz} from the deb package into staging_path
361
- safesystem("ar p #{package} #{datatar} " \
362
- "| tar #{compression} -xf - -C #{staging_path}")
416
+ safesystem(ar_cmd[0] + " p #{package} #{datatar} | tar #{compression} -xf - -C #{staging_path}")
363
417
  end # def extract_files
364
418
 
365
419
  def output(output_path)
@@ -387,6 +441,23 @@ class FPM::Package::Deb < FPM::Package
387
441
  end
388
442
  end
389
443
 
444
+ if attributes[:source_date_epoch].nil? and not attributes[:source_date_epoch_default].nil?
445
+ attributes[:source_date_epoch] = attributes[:source_date_epoch_default]
446
+ end
447
+ if attributes[:source_date_epoch] == "0"
448
+ logger.error("Alas, ruby's Zlib::GzipWriter does not support setting an mtime of zero. Aborting.")
449
+ raise "#{name}: source_date_epoch of 0 not supported."
450
+ end
451
+ if not attributes[:source_date_epoch].nil? and not ar_cmd_deterministic?
452
+ logger.error("Alas, could not find an ar that can handle -D option. Try installing recent gnu binutils. Aborting.")
453
+ raise "#{name}: ar is insufficient to support source_date_epoch."
454
+ end
455
+ if not attributes[:source_date_epoch].nil? and not tar_cmd_supports_sort_names_and_set_mtime?
456
+ logger.error("Alas, could not find a tar that can set mtime and sort. Try installing recent gnu tar. Aborting.")
457
+ raise "#{name}: tar is insufficient to support source_date_epoch."
458
+ end
459
+
460
+ attributes[:deb_systemd] = []
390
461
  attributes.fetch(:deb_systemd_list, []).each do |systemd|
391
462
  name = File.basename(systemd, ".service")
392
463
  dest_systemd = staging_path("lib/systemd/system/#{name}.service")
@@ -394,42 +465,27 @@ class FPM::Package::Deb < FPM::Package
394
465
  FileUtils.cp(systemd, dest_systemd)
395
466
  File.chmod(0644, dest_systemd)
396
467
 
397
- # set the attribute with the systemd service name
398
- attributes[:deb_systemd] = name
468
+ # add systemd service name to attribute
469
+ attributes[:deb_systemd] << name
399
470
  end
400
471
 
401
- if script?(:before_upgrade) or script?(:after_upgrade) or attributes[:deb_systemd]
472
+ if script?(:before_upgrade) or script?(:after_upgrade) or attributes[:deb_systemd].any?
402
473
  puts "Adding action files"
403
474
  if script?(:before_install) or script?(:before_upgrade)
404
475
  scripts[:before_install] = template("deb/preinst_upgrade.sh.erb").result(binding)
405
476
  end
406
- if script?(:before_remove) or attributes[:deb_systemd]
477
+ if script?(:before_remove) or not attributes[:deb_systemd].empty?
407
478
  scripts[:before_remove] = template("deb/prerm_upgrade.sh.erb").result(binding)
408
479
  end
409
- if script?(:after_install) or script?(:after_upgrade) or attributes[:deb_systemd]
480
+ if script?(:after_install) or script?(:after_upgrade) or attributes[:deb_systemd].any?
410
481
  scripts[:after_install] = template("deb/postinst_upgrade.sh.erb").result(binding)
411
482
  end
412
483
  if script?(:after_remove)
413
484
  scripts[:after_remove] = template("deb/postrm_upgrade.sh.erb").result(binding)
414
485
  end
415
- end
416
-
417
- write_control_tarball
418
-
419
- # Tar up the staging_path into data.tar.{compression type}
420
- case self.attributes[:deb_compression]
421
- when "gz", nil
422
- datatar = build_path("data.tar.gz")
423
- compression = "-z"
424
- when "bzip2"
425
- datatar = build_path("data.tar.bz2")
426
- compression = "-j"
427
- when "xz"
428
- datatar = build_path("data.tar.xz")
429
- compression = "-J"
430
- else
431
- raise FPM::InvalidPackageConfiguration,
432
- "Unknown compression type '#{self.attributes[:deb_compression]}'"
486
+ if script?(:after_purge)
487
+ scripts[:after_purge] = template("deb/postrm_upgrade.sh.erb").result(binding)
488
+ end
433
489
  end
434
490
 
435
491
  # There are two changelogs that may appear:
@@ -442,6 +498,9 @@ class FPM::Package::Deb < FPM::Package
442
498
  mkdir_p(File.dirname(dest_changelog))
443
499
  File.new(dest_changelog, "wb", 0644).tap do |changelog|
444
500
  Zlib::GzipWriter.new(changelog, Zlib::BEST_COMPRESSION).tap do |changelog_gz|
501
+ if not attributes[:source_date_epoch].nil?
502
+ changelog_gz.mtime = attributes[:source_date_epoch].to_i
503
+ end
445
504
  if attributes[:deb_changelog]
446
505
  logger.info("Writing user-specified changelog", :source => attributes[:deb_changelog])
447
506
  File.new(attributes[:deb_changelog]).tap do |fd|
@@ -461,6 +520,9 @@ class FPM::Package::Deb < FPM::Package
461
520
  if attributes[:deb_upstream_changelog]
462
521
  File.new(dest_upstream_changelog, "wb", 0644).tap do |changelog|
463
522
  Zlib::GzipWriter.new(changelog, Zlib::BEST_COMPRESSION).tap do |changelog_gz|
523
+ if not attributes[:source_date_epoch].nil?
524
+ changelog_gz.mtime = attributes[:source_date_epoch].to_i
525
+ end
464
526
  logger.info("Writing user-specified upstream changelog", :source => attributes[:deb_upstream_changelog])
465
527
  File.new(attributes[:deb_upstream_changelog]).tap do |fd|
466
528
  chunk = nil
@@ -494,6 +556,7 @@ class FPM::Package::Deb < FPM::Package
494
556
 
495
557
  attributes.fetch(:deb_upstart_list, []).each do |upstart|
496
558
  name = File.basename(upstart, ".upstart")
559
+ dest_init = staging_path("etc/init.d/#{name}")
497
560
  name = "#{name}.conf" if !(name =~ /\.conf$/)
498
561
  dest_upstart = staging_path("etc/init/#{name}")
499
562
  mkdir_p(File.dirname(dest_upstart))
@@ -501,7 +564,6 @@ class FPM::Package::Deb < FPM::Package
501
564
  File.chmod(0644, dest_upstart)
502
565
 
503
566
  # Install an init.d shim that calls upstart
504
- dest_init = staging_path("etc/init.d/#{name}")
505
567
  mkdir_p(File.dirname(dest_init))
506
568
  FileUtils.ln_s("/lib/init/upstart-job", dest_init)
507
569
  end
@@ -527,21 +589,54 @@ class FPM::Package::Deb < FPM::Package
527
589
  when "xz"
528
590
  datatar = build_path("data.tar.xz")
529
591
  compression = "-J"
592
+ when "none"
593
+ datatar = build_path("data.tar")
594
+ compression = ""
530
595
  else
531
596
  raise FPM::InvalidPackageConfiguration,
532
597
  "Unknown compression type '#{self.attributes[:deb_compression]}'"
533
598
  end
534
599
 
535
600
  args = [ tar_cmd, "-C", staging_path, compression ] + data_tar_flags + [ "-cf", datatar, "." ]
601
+ if tar_cmd_supports_sort_names_and_set_mtime? and not attributes[:source_date_epoch].nil?
602
+ # Use gnu tar options to force deterministic file order and timestamp
603
+ args += ["--sort=name", ("--mtime=@%s" % attributes[:source_date_epoch])]
604
+ # gnu tar obeys GZIP environment variable with options for gzip; -n = forget original filename and date
605
+ args.unshift({"GZIP" => "-9n"})
606
+ end
536
607
  safesystem(*args)
537
608
 
538
609
  # pack up the .deb, which is just an 'ar' archive with 3 files
539
610
  # the 'debian-binary' file has to be first
540
611
  File.expand_path(output_path).tap do |output_path|
541
612
  ::Dir.chdir(build_path) do
542
- safesystem("ar", "-qc", output_path, "debian-binary", "control.tar.gz", datatar)
613
+ safesystem(*ar_cmd, output_path, "debian-binary", "control.tar.gz", datatar)
543
614
  end
544
615
  end
616
+
617
+ # if a PACKAGENAME.changes file is to be created
618
+ if self.attributes[:deb_generate_changes?]
619
+ distribution = self.attributes[:deb_dist]
620
+
621
+ # gather information about the files to distribute
622
+ files = [ output_path ]
623
+ changes_files = []
624
+ files.each do |path|
625
+ changes_files.push({
626
+ :name => path,
627
+ :size => File.size?(path),
628
+ :md5sum => Digest::MD5.file(path).hexdigest,
629
+ :sha1sum => Digest::SHA1.file(path).hexdigest,
630
+ :sha256sum => Digest::SHA2.file(path).hexdigest,
631
+ })
632
+ end
633
+
634
+ # write change infos to .changes file
635
+ changes_path = File.basename(output_path, '.deb') + '.changes'
636
+ changes_data = template("deb/deb.changes.erb").result(binding)
637
+ File.write(changes_path, changes_data)
638
+ logger.log("Created changes", :path => changes_path)
639
+ end # if deb_generate_changes
545
640
  end # def output
546
641
 
547
642
  def converted_from(origin)
@@ -687,12 +782,37 @@ class FPM::Package::Deb < FPM::Package
687
782
  write_triggers # write trigger config to 'triggers' file
688
783
  write_md5sums # write the md5sums file
689
784
 
785
+ # Tar up the staging_path into control.tar.{compression type}
786
+ case self.attributes[:deb_compression]
787
+ when "gz", nil
788
+ controltar = "control.tar.gz"
789
+ compression = "-z"
790
+ when "bzip2"
791
+ controltar = "control.tar.bz2"
792
+ compression = "-j"
793
+ when "xz"
794
+ controltar = "control.tar.xz"
795
+ compression = "-J"
796
+ when "none"
797
+ controltar = "control.tar"
798
+ compression = ""
799
+ else
800
+ raise FPM::InvalidPackageConfiguration,
801
+ "Unknown compression type '#{self.attributes[:deb_compression]}'"
802
+ end
803
+
690
804
  # Make the control.tar.gz
691
- build_path("control.tar.gz").tap do |controltar|
805
+ build_path(controltar).tap do |controltar|
692
806
  logger.info("Creating", :path => controltar, :from => control_path)
693
807
 
694
- args = [ tar_cmd, "-C", control_path, "-zcf", controltar,
808
+ args = [ tar_cmd, "-C", control_path, compression, "-cf", controltar,
695
809
  "--owner=0", "--group=0", "--numeric-owner", "." ]
810
+ if tar_cmd_supports_sort_names_and_set_mtime? and not attributes[:source_date_epoch].nil?
811
+ # Force deterministic file order and timestamp
812
+ args += ["--sort=name", ("--mtime=@%s" % attributes[:source_date_epoch])]
813
+ # gnu tar obeys GZIP environment variable with options for gzip; -n = forget original filename and date
814
+ args.unshift({"GZIP" => "-9n"})
815
+ end
696
816
  safesystem(*args)
697
817
  end
698
818
 
@@ -789,11 +909,25 @@ class FPM::Package::Deb < FPM::Package
789
909
 
790
910
  # scan all conf file paths for files and add them
791
911
  config_files.each do |path|
912
+ logger.debug("Checking if #{path} exists")
913
+ cfe = File.exist?("#{path}")
914
+ logger.debug("Check result #{cfe}")
792
915
  begin
793
916
  add_path(path, allconfigs)
794
917
  rescue Errno::ENOENT
795
- raise FPM::InvalidPackageConfiguration,
796
- "Error trying to use '#{path}' as a config file in the package. Does it exist?"
918
+ if !cfe
919
+ raise FPM::InvalidPackageConfiguration,
920
+ "Error trying to use '#{path}' as a config file in the package. Does it exist?"
921
+ else
922
+ dcl = File.join(staging_path, path)
923
+ if !File.exist?("#{dcl}")
924
+ logger.debug("Adding config file #{path} to Staging area #{staging_path}")
925
+ FileUtils.mkdir_p(File.dirname(dcl))
926
+ FileUtils.cp_r path, dcl
927
+ else
928
+ logger.debug("Config file aready exists in staging area.")
929
+ end
930
+ end
797
931
  end
798
932
  end
799
933
 
@@ -849,7 +983,7 @@ class FPM::Package::Deb < FPM::Package
849
983
 
850
984
  if attributes[:deb_templates]
851
985
  FileUtils.cp(attributes[:deb_templates], control_path("templates"))
852
- File.chmod(0755, control_path("templates"))
986
+ File.chmod(0644, control_path("templates"))
853
987
  end
854
988
  end # def write_debconf
855
989
 
@@ -865,7 +999,10 @@ class FPM::Package::Deb < FPM::Package
865
999
 
866
1000
  def write_triggers
867
1001
  lines = [['interest', :deb_interest],
868
- ['activate', :deb_activate]].map { |label, attr|
1002
+ ['activate', :deb_activate],
1003
+ ['interest-noawait', :deb_interest_noawait],
1004
+ ['activate-noawait', :deb_activate_noawait],
1005
+ ].map { |label, attr|
869
1006
  (attributes[attr] || []).map { |e| "#{label} #{e}\n" }
870
1007
  }.flatten.join('')
871
1008