fpm-aeppert 1.6.2 → 1.11.0

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