fpm 1.10.2 → 1.15.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.
@@ -3,9 +3,14 @@ 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
+ require "zlib"
10
+
11
+ # For handling conversion
12
+ require "fpm/package/cpan"
13
+ require "fpm/package/gem"
9
14
 
10
15
  # Support for debian packages (.deb files)
11
16
  #
@@ -22,7 +27,23 @@ class FPM::Package::Deb < FPM::Package
22
27
  } unless defined?(SCRIPT_MAP)
23
28
 
24
29
  # The list of supported compression types. Default is gz (gzip)
25
- COMPRESSION_TYPES = [ "gz", "bzip2", "xz" ]
30
+ COMPRESSION_TYPES = [ "gz", "bzip2", "xz", "none" ]
31
+
32
+ # https://www.debian.org/doc/debian-policy/ch-relationships.html#syntax-of-relationship-fields
33
+ # Example value with version relationship: libc6 (>= 2.2.1)
34
+ # Example value: libc6
35
+
36
+ # Package name docs here: https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-source
37
+ # Package names (both source and binary, see Package) must consist only of lower case letters (a-z),
38
+ # digits (0-9), plus (+) and minus (-) signs, and periods (.).
39
+ # They must be at least two characters long and must start with an alphanumeric character.
40
+
41
+ # Version string docs here: https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-version
42
+ # The format is: [epoch:]upstream_version[-debian_revision].
43
+ # epoch - This is a single (generally small) unsigned integer
44
+ # upstream_version - must contain only alphanumerics 6 and the characters . + - ~
45
+ # debian_revision - only alphanumerics and the characters + . ~
46
+ RELATIONSHIP_FIELD_PATTERN = /^(?<name>[A-z0-9][A-z0-9_.-]+)(?: *\((?<relation>[<>=]+) *(?<version>(?:[0-9]+:)?[0-9A-Za-z+~.-]+(?:-[0-9A-Za-z+~.]+)?)\))?$/
26
47
 
27
48
  option "--ignore-iteration-in-dependencies", :flag,
28
49
  "For '=' (equal) dependencies, allow iterations on the specified " \
@@ -77,7 +98,7 @@ class FPM::Package::Deb < FPM::Package
77
98
  end
78
99
 
79
100
  option "--priority", "PRIORITY",
80
- "The debian package 'priority' value.", :default => "extra"
101
+ "The debian package 'priority' value.", :default => "optional"
81
102
 
82
103
  option "--use-file-permissions", :flag,
83
104
  "Use existing file permissions when defining ownership and modes"
@@ -177,10 +198,14 @@ class FPM::Package::Deb < FPM::Package
177
198
  end
178
199
 
179
200
  option "--systemd", "FILEPATH", "Add FILEPATH as a systemd script",
180
- :multivalued => true do |file|
201
+ :multivalued => true do |file|
181
202
  next File.expand_path(file)
182
203
  end
183
204
 
205
+ option "--systemd-enable", :flag , "Enable service on install or upgrade", :default => false
206
+
207
+ option "--systemd-auto-start", :flag , "Start service after install or upgrade", :default => false
208
+
184
209
  option "--systemd-restart-after-upgrade", :flag , "Restart service after upgrade", :default => true
185
210
 
186
211
  option "--after-purge", "FILE",
@@ -189,9 +214,14 @@ class FPM::Package::Deb < FPM::Package
189
214
  File.expand_path(val) # Get the full path to the script
190
215
  end # --after-purge
191
216
 
217
+ option "--maintainerscripts-force-errorchecks", :flag ,
218
+ "Activate errexit shell option according to lintian. " \
219
+ "https://lintian.debian.org/tags/maintainer-script-ignores-errors.html",
220
+ :default => false
221
+
192
222
  def initialize(*args)
193
223
  super(*args)
194
- attributes[:deb_priority] = "extra"
224
+ attributes[:deb_priority] = "optional"
195
225
  end # def initialize
196
226
 
197
227
  private
@@ -218,6 +248,9 @@ class FPM::Package::Deb < FPM::Package
218
248
  when "x86_64"
219
249
  # Debian calls x86_64 "amd64"
220
250
  @architecture = "amd64"
251
+ when "aarch64"
252
+ # Debian calls aarch64 "arm64"
253
+ @architecture = "arm64"
221
254
  when "noarch"
222
255
  # Debian calls noarch "all"
223
256
  @architecture = "all"
@@ -265,10 +298,32 @@ class FPM::Package::Deb < FPM::Package
265
298
  end # def input
266
299
 
267
300
  def extract_info(package)
301
+ compression = `#{ar_cmd[0]} t #{package}`.split("\n").grep(/control.tar/).first.split(".").last
302
+ case compression
303
+ when "gz"
304
+ controltar = "control.tar.gz"
305
+ compression = "-z"
306
+ when "bzip2","bz2"
307
+ controltar = "control.tar.bz2"
308
+ compression = "-j"
309
+ when "xz"
310
+ controltar = "control.tar.xz"
311
+ compression = "-J"
312
+ when 'tar'
313
+ controltar = "control.tar"
314
+ compression = ""
315
+ when nil
316
+ raise FPM::InvalidPackageConfiguration, "Missing control.tar in deb source package #{package}"
317
+ else
318
+ raise FPM::InvalidPackageConfiguration,
319
+ "Unknown compression type '#{compression}' for control.tar in deb source package #{package}"
320
+ end
321
+
268
322
  build_path("control").tap do |path|
269
323
  FileUtils.mkdir(path) if !File.directory?(path)
324
+ # unpack the control.tar.{,gz,bz2,xz} from the deb package into staging_path
270
325
  # Unpack the control tarball
271
- safesystem(ar_cmd[0] + " p #{package} control.tar.gz | tar -zxf - -C #{path}")
326
+ safesystem(ar_cmd[0] + " p #{package} #{controltar} | tar #{compression} -xf - -C #{path}")
272
327
 
273
328
  control = File.read(File.join(path, "control"))
274
329
 
@@ -376,19 +431,28 @@ class FPM::Package::Deb < FPM::Package
376
431
  when "xz"
377
432
  datatar = "data.tar.xz"
378
433
  compression = "-J"
434
+ when 'tar'
435
+ datatar = "data.tar"
436
+ compression = ""
437
+ when nil
438
+ raise FPM::InvalidPackageConfiguration, "Missing data.tar in deb source package #{package}"
379
439
  else
380
440
  raise FPM::InvalidPackageConfiguration,
381
- "Unknown compression type '#{self.attributes[:deb_compression]}' "
382
- "in deb source package #{package}"
441
+ "Unknown compression type '#{compression}' for data.tar in deb source package #{package}"
383
442
  end
384
443
 
385
444
  # unpack the data.tar.{gz,bz2,xz} from the deb package into staging_path
386
- safesystem(ar_cmd[0] + " p #{package} #{datatar} " \
387
- "| tar #{compression} -xf - -C #{staging_path}")
445
+ safesystem(ar_cmd[0] + " p #{package} #{datatar} | tar #{compression} -xf - -C #{staging_path}")
388
446
  end # def extract_files
389
447
 
390
448
  def output(output_path)
391
449
  self.provides = self.provides.collect { |p| fix_provides(p) }
450
+
451
+ self.provides.each do |provide|
452
+ if !valid_provides_field?(provide)
453
+ raise FPM::InvalidPackageConfiguration, "Found invalid Provides field values (#{provide.inspect}). This is not valid in a Debian package."
454
+ end
455
+ end
392
456
  output_check(output_path)
393
457
  # Abort if the target path already exists.
394
458
 
@@ -428,6 +492,7 @@ class FPM::Package::Deb < FPM::Package
428
492
  raise "#{name}: tar is insufficient to support source_date_epoch."
429
493
  end
430
494
 
495
+ attributes[:deb_systemd] = []
431
496
  attributes.fetch(:deb_systemd_list, []).each do |systemd|
432
497
  name = File.basename(systemd, ".service")
433
498
  dest_systemd = staging_path("lib/systemd/system/#{name}.service")
@@ -435,19 +500,19 @@ class FPM::Package::Deb < FPM::Package
435
500
  FileUtils.cp(systemd, dest_systemd)
436
501
  File.chmod(0644, dest_systemd)
437
502
 
438
- # set the attribute with the systemd service name
439
- attributes[:deb_systemd] = name
503
+ # add systemd service name to attribute
504
+ attributes[:deb_systemd] << name
440
505
  end
441
506
 
442
- if script?(:before_upgrade) or script?(:after_upgrade) or attributes[:deb_systemd]
507
+ if script?(:before_upgrade) or script?(:after_upgrade) or attributes[:deb_systemd].any?
443
508
  puts "Adding action files"
444
509
  if script?(:before_install) or script?(:before_upgrade)
445
510
  scripts[:before_install] = template("deb/preinst_upgrade.sh.erb").result(binding)
446
511
  end
447
- if script?(:before_remove) or attributes[:deb_systemd]
512
+ if script?(:before_remove) or not attributes[:deb_systemd].empty?
448
513
  scripts[:before_remove] = template("deb/prerm_upgrade.sh.erb").result(binding)
449
514
  end
450
- if script?(:after_install) or script?(:after_upgrade) or attributes[:deb_systemd]
515
+ if script?(:after_install) or script?(:after_upgrade) or attributes[:deb_systemd].any?
451
516
  scripts[:after_install] = template("deb/postinst_upgrade.sh.erb").result(binding)
452
517
  end
453
518
  if script?(:after_remove)
@@ -552,19 +617,25 @@ class FPM::Package::Deb < FPM::Package
552
617
  case self.attributes[:deb_compression]
553
618
  when "gz", nil
554
619
  datatar = build_path("data.tar.gz")
555
- compression = "-z"
620
+ controltar = build_path("control.tar.gz")
621
+ compression_flags = ["-z"]
556
622
  when "bzip2"
557
623
  datatar = build_path("data.tar.bz2")
558
- compression = "-j"
624
+ controltar = build_path("control.tar.gz")
625
+ compression_flags = ["-j"]
559
626
  when "xz"
560
627
  datatar = build_path("data.tar.xz")
561
- compression = "-J"
628
+ controltar = build_path("control.tar.xz")
629
+ compression_flags = ["-J"]
630
+ when "none"
631
+ datatar = build_path("data.tar")
632
+ controltar = build_path("control.tar")
633
+ compression_flags = []
562
634
  else
563
635
  raise FPM::InvalidPackageConfiguration,
564
636
  "Unknown compression type '#{self.attributes[:deb_compression]}'"
565
637
  end
566
-
567
- args = [ tar_cmd, "-C", staging_path, compression ] + data_tar_flags + [ "-cf", datatar, "." ]
638
+ args = [ tar_cmd, "-C", staging_path ] + compression_flags + data_tar_flags + [ "-cf", datatar, "." ]
568
639
  if tar_cmd_supports_sort_names_and_set_mtime? and not attributes[:source_date_epoch].nil?
569
640
  # Use gnu tar options to force deterministic file order and timestamp
570
641
  args += ["--sort=name", ("--mtime=@%s" % attributes[:source_date_epoch])]
@@ -577,7 +648,7 @@ class FPM::Package::Deb < FPM::Package
577
648
  # the 'debian-binary' file has to be first
578
649
  File.expand_path(output_path).tap do |output_path|
579
650
  ::Dir.chdir(build_path) do
580
- safesystem(*ar_cmd, output_path, "debian-binary", "control.tar.gz", datatar)
651
+ safesystem(*ar_cmd, output_path, "debian-binary", controltar, datatar)
581
652
  end
582
653
  end
583
654
 
@@ -614,6 +685,43 @@ class FPM::Package::Deb < FPM::Package
614
685
  fix_provides(provides)
615
686
  end.flatten
616
687
 
688
+ if origin == FPM::Package::CPAN
689
+ # The fpm cpan code presents dependencies and provides fields as perl(ModuleName)
690
+ # so we'll need to convert them to something debian supports.
691
+
692
+ # Replace perl(ModuleName) > 1.0 with Debian-style perl-ModuleName (> 1.0)
693
+ perldepfix = lambda do |dep|
694
+ m = dep.match(/perl\((?<name>[A-Za-z0-9_:]+)\)\s*(?<op>.*$)/)
695
+ if m.nil?
696
+ # 'dep' syntax didn't look like 'perl(Name) > 1.0'
697
+ dep
698
+ else
699
+ # Also replace '::' in the perl module name with '-'
700
+ modulename = m["name"].gsub("::", "-")
701
+
702
+ # Fix any upper-casing or other naming concerns Debian has about packages
703
+ name = "#{attributes[:cpan_package_name_prefix]}-#{modulename}"
704
+
705
+ if m["op"].empty?
706
+ name
707
+ else
708
+ # 'dep' syntax was like this (version constraint): perl(Module) > 1.0
709
+ "#{name} (#{m["op"]})"
710
+ end
711
+ end
712
+ end
713
+
714
+ rejects = [ "perl(vars)", "perl(warnings)", "perl(strict)", "perl(Config)" ]
715
+ self.dependencies = self.dependencies.reject do |dep|
716
+ # Reject non-module Perl dependencies like 'vars' and 'warnings'
717
+ rejects.include?(dep)
718
+ end.collect(&perldepfix).collect(&method(:fix_dependency))
719
+
720
+ # Also fix the Provides field 'perl(ModuleName) = version' to be 'perl-modulename (= version)'
721
+ self.provides = self.provides.collect(&perldepfix).collect(&method(:fix_provides))
722
+
723
+ end # if origin == FPM::Packagin::CPAN
724
+
617
725
  if origin == FPM::Package::Deb
618
726
  changelog_path = staging_path("usr/share/doc/#{name}/changelog.Debian.gz")
619
727
  if File.exists?(changelog_path)
@@ -641,6 +749,19 @@ class FPM::Package::Deb < FPM::Package
641
749
  File.unlink(changelog_path)
642
750
  end
643
751
  end
752
+
753
+ if origin == FPM::Package::Gem
754
+ # fpm's gem input will have provides as "rubygem-name = version"
755
+ # and we need to convert this to Debian-style "rubygem-name (= version)"
756
+ self.provides = self.provides.collect do |provides|
757
+ m = /^(#{attributes[:gem_package_name_prefix]})-([^\s]+)\s*=\s*(.*)$/.match(provides)
758
+ if m
759
+ "#{m[1]}-#{m[2]} (= #{m[3]})"
760
+ else
761
+ provides
762
+ end
763
+ end
764
+ end
644
765
  end # def converted_from
645
766
 
646
767
  def debianize_op(op)
@@ -682,8 +803,13 @@ class FPM::Package::Deb < FPM::Package
682
803
  name, version = dep.gsub(/[()~>]/, "").split(/ +/)[0..1]
683
804
  nextversion = version.split(".").collect { |v| v.to_i }
684
805
  l = nextversion.length
685
- nextversion[l-2] += 1
686
- nextversion[l-1] = 0
806
+ if l > 1
807
+ nextversion[l-2] += 1
808
+ nextversion[l-1] = 0
809
+ else
810
+ # Single component versions ~> 1
811
+ nextversion[l-1] += 1
812
+ end
687
813
  nextversion = nextversion.join(".")
688
814
  return ["#{name} (>= #{version})", "#{name} (<< #{nextversion})"]
689
815
  elsif (m = dep.match(/(\S+)\s+\(!= (.+)\)/))
@@ -710,6 +836,32 @@ class FPM::Package::Deb < FPM::Package
710
836
  end
711
837
  end # def fix_dependency
712
838
 
839
+ def valid_provides_field?(text)
840
+ m = RELATIONSHIP_FIELD_PATTERN.match(text)
841
+ if m.nil?
842
+ logger.error("Invalid relationship field for debian package: #{text}")
843
+ return false
844
+ end
845
+
846
+ # Per Debian Policy manual, https://www.debian.org/doc/debian-policy/ch-relationships.html#syntax-of-relationship-fields
847
+ # >> The relations allowed are <<, <=, =, >= and >> for strictly earlier, earlier or equal,
848
+ # >> exactly equal, later or equal and strictly later, respectively. The exception is the
849
+ # >> Provides field, for which only = is allowed
850
+ if m["relation"] == "=" || m["relation"] == nil
851
+ return true
852
+ end
853
+ return false
854
+ end
855
+
856
+ def valid_relationship_field?(text)
857
+ m = RELATIONSHIP_FIELD_PATTERN.match(text)
858
+ if m.nil?
859
+ logger.error("Invalid relationship field for debian package: #{text}")
860
+ return false
861
+ end
862
+ return true
863
+ end
864
+
713
865
  def fix_provides(provides)
714
866
  name_re = /^[^ \(]+/
715
867
  name = provides[name_re]
@@ -724,6 +876,11 @@ class FPM::Package::Deb < FPM::Package
724
876
  "debs don't like underscores")
725
877
  provides = provides.gsub("_", "-")
726
878
  end
879
+
880
+ if m = provides.match(/^([A-Za-z0-9_-]+)\s*=\s*(\d+.*$)/)
881
+ logger.warn("Replacing 'provides' entry #{provides} with syntax 'name (= version)'")
882
+ provides = "#{m[1]} (= #{m[2]})"
883
+ end
727
884
  return provides.rstrip
728
885
  end
729
886
 
@@ -749,11 +906,27 @@ class FPM::Package::Deb < FPM::Package
749
906
  write_triggers # write trigger config to 'triggers' file
750
907
  write_md5sums # write the md5sums file
751
908
 
909
+ # Tar up the staging_path into control.tar.{compression type}
910
+ case self.attributes[:deb_compression]
911
+ when "gz", "bzip2", nil
912
+ controltar = "control.tar.gz"
913
+ compression_flags = ["-z"]
914
+ when "xz"
915
+ controltar = "control.tar.xz"
916
+ compression_flags = ["-J"]
917
+ when "none"
918
+ controltar = "control.tar"
919
+ compression_flags = []
920
+ else
921
+ raise FPM::InvalidPackageConfiguration,
922
+ "Unknown compression type '#{self.attributes[:deb_compression]}'"
923
+ end
924
+
752
925
  # Make the control.tar.gz
753
- build_path("control.tar.gz").tap do |controltar|
926
+ build_path(controltar).tap do |controltar|
754
927
  logger.info("Creating", :path => controltar, :from => control_path)
755
928
 
756
- args = [ tar_cmd, "-C", control_path, "-zcf", controltar,
929
+ args = [ tar_cmd, "-C", control_path ] + compression_flags + [ "-cf", controltar,
757
930
  "--owner=0", "--group=0", "--numeric-owner", "." ]
758
931
  if tar_cmd_supports_sort_names_and_set_mtime? and not attributes[:source_date_epoch].nil?
759
932
  # Force deterministic file order and timestamp
@@ -842,7 +1015,7 @@ class FPM::Package::Deb < FPM::Package
842
1015
  etcfiles = []
843
1016
  # Add everything in /etc
844
1017
  begin
845
- if !attributes[:deb_no_default_config_files?]
1018
+ if !attributes[:deb_no_default_config_files?] && File.exists?(staging_path("/etc"))
846
1019
  logger.warn("Debian packaging tools generally labels all files in /etc as config files, " \
847
1020
  "as mandated by policy, so fpm defaults to this behavior for deb packages. " \
848
1021
  "You can disable this default behavior with --deb-no-default-config-files flag")
@@ -931,7 +1104,7 @@ class FPM::Package::Deb < FPM::Package
931
1104
 
932
1105
  if attributes[:deb_templates]
933
1106
  FileUtils.cp(attributes[:deb_templates], control_path("templates"))
934
- File.chmod(0755, control_path("templates"))
1107
+ File.chmod(0644, control_path("templates"))
935
1108
  end
936
1109
  end # def write_debconf
937
1110
 
@@ -1,6 +1,6 @@
1
1
  require "fpm/package"
2
2
  require "fpm/util"
3
- require "backports"
3
+ require "backports/latest"
4
4
  require "fileutils"
5
5
  require "find"
6
6
  require "socket"
@@ -83,8 +83,8 @@ class FPM::Package::Dir < FPM::Package
83
83
  # can include license data from themselves (rpms, gems, etc),
84
84
  # but to make sure a simple dir -> rpm works without having
85
85
  # to specify a license.
86
- self.license = "unknown"
87
- self.vendor = [ENV["USER"], Socket.gethostname].join("@")
86
+ self.license ||= "unknown"
87
+ self.vendor ||= [ENV["USER"], Socket.gethostname].join("@")
88
88
  ensure
89
89
  # Clean up any logger context we added.
90
90
  logger.remove("method")
@@ -1,9 +1,21 @@
1
1
  require "fpm/package"
2
- require "backports"
2
+ require "backports/latest"
3
3
 
4
4
  # Empty Package type. For strict/meta/virtual package creation
5
5
 
6
6
  class FPM::Package::Empty < FPM::Package
7
+ def initialize(*args)
8
+ super(*args)
9
+
10
+ # Override FPM::Package's default "native" architecture value
11
+ # This feels like the right default because an empty package has no
12
+ # architecture-specific files, and in most cases an empty package should be
13
+ # installable anywhere.
14
+ #
15
+ # https://github.com/jordansissel/fpm/issues/1846
16
+ @architecture = "all"
17
+ end
18
+
7
19
  def output(output_path)
8
20
  logger.warn("Your package has gone into the void.")
9
21
  end
@@ -1,4 +1,4 @@
1
- require "backports" # gem backports
1
+ require "backports/latest" # gem backports/latest
2
2
  require "fpm/package"
3
3
  require "fpm/util"
4
4
  require "digest"
@@ -17,10 +17,6 @@ class FPM::Package::FreeBSD < FPM::Package
17
17
  :default => "fpm/<name>"
18
18
 
19
19
  def output(output_path)
20
- # See https://github.com/jordansissel/fpm/issues/1090
21
- # require xz later, because this triggers a load of liblzma.so.5 that is
22
- # unavailable on older CentOS/RH distros.
23
- require "xz"
24
20
  output_check(output_path)
25
21
 
26
22
  # Build the packaging metadata files.
@@ -80,22 +76,17 @@ class FPM::Package::FreeBSD < FPM::Package
80
76
  file.write(pkgdata.to_json + "\n")
81
77
  end
82
78
 
83
- # Create the .txz package archive from the files in staging_path.
84
- File.open(output_path, "wb") do |file|
85
- XZ::StreamWriter.new(file) do |xz|
86
- FPM::Util::TarWriter.new(xz) do |tar|
87
- # The manifests must come first for pkg.
88
- add_path(tar, "+COMPACT_MANIFEST",
89
- File.join(staging_path, "+COMPACT_MANIFEST"))
90
- add_path(tar, "+MANIFEST",
91
- File.join(staging_path, "+MANIFEST"))
92
-
93
- checksums.keys.each do |path|
94
- add_path(tar, "/" + path, File.join(staging_path, path))
95
- end
96
- end
97
- end
79
+ file_list = File.new(build_path("file_list"), "w")
80
+ files.each do |i|
81
+ file_list.puts(i)
98
82
  end
83
+ file_list.close
84
+
85
+ # Create the .txz package archive from the files in staging_path.
86
+ # We use --files-from here to keep the tar entries from having `./` as the prefix.
87
+ # This is done as a best effor to mimic what FreeBSD packages do, having everything at the top-level as
88
+ # file names, like "+MANIFEST" instead of "./+MANIFEST"
89
+ safesystem("tar", "-Jcf", output_path, "-C", staging_path, "--files-from", build_path("file_list"), "--transform", 's|^\([^+]\)|/\1|')
99
90
  end # def output
100
91
 
101
92
  # Handle architecture naming conversion:
@@ -110,6 +101,8 @@ class FPM::Package::FreeBSD < FPM::Package
110
101
  wordsize = case @architecture
111
102
  when nil, 'native'
112
103
  %x{getconf LONG_BIT}.chomp # 'native' is current arch
104
+ when 'arm64'
105
+ '64'
113
106
  when 'amd64'
114
107
  '64'
115
108
  when 'i386'
@@ -53,6 +53,15 @@ class FPM::Package::Gem < FPM::Package
53
53
  "The directory where fpm installs the gem temporarily before conversion. " \
54
54
  "Normally a random subdirectory of workdir."
55
55
 
56
+ option "--git-repo", "GIT_REPO",
57
+ "Use this git repo address as the source of the gem instead of " \
58
+ "rubygems.org.", :default => nil
59
+
60
+ option "--git-branch", "GIT_BRANCH",
61
+ "When using a git repo as the source of the gem instead of " \
62
+ "rubygems.org, use this git branch.",
63
+ :default => nil
64
+
56
65
  # Override parent method
57
66
  def staging_path(path=nil)
58
67
  @gem_staging_path ||= attributes[:gem_stagingdir] || Stud::Temporary.directory("package-#{type}-staging")
@@ -91,20 +100,32 @@ class FPM::Package::Gem < FPM::Package
91
100
 
92
101
  logger.info("Trying to download", :gem => gem_name, :version => gem_version)
93
102
 
94
- gem_fetch = [ "#{attributes[:gem_gem]}", "fetch", gem_name]
95
-
96
- gem_fetch += ["--prerelease"] if attributes[:gem_prerelease?]
97
- gem_fetch += ["--version", gem_version] if gem_version
98
-
99
103
  download_dir = build_path(gem_name)
100
104
  FileUtils.mkdir(download_dir) unless File.directory?(download_dir)
101
105
 
102
- ::Dir.chdir(download_dir) do |dir|
103
- logger.debug("Downloading in directory #{dir}")
104
- safesystem(*gem_fetch)
105
- end
106
+ if attributes[:gem_git_repo]
107
+ logger.debug("Git cloning in directory #{download_dir}")
108
+ safesystem("git", "-C", download_dir, "clone", attributes[:gem_git_repo], ".")
109
+ if attributes[:gem_git_branch]
110
+ safesystem("git", "-C", download_dir, "checkout", attributes[:gem_git_branch])
111
+ end
106
112
 
107
- gem_files = ::Dir.glob(File.join(download_dir, "*.gem"))
113
+ gem_build = [ "#{attributes[:gem_gem]}", "build", "#{download_dir}/#{gem_name}.gemspec"]
114
+ ::Dir.chdir(download_dir) do |dir|
115
+ logger.debug("Building in directory #{dir}")
116
+ safesystem(*gem_build)
117
+ end
118
+ gem_files = ::Dir.glob(File.join(download_dir, "*.gem"))
119
+ else
120
+ gem_fetch = [ "#{attributes[:gem_gem]}", "fetch", gem_name]
121
+ gem_fetch += ["--prerelease"] if attributes[:gem_prerelease?]
122
+ gem_fetch += ["--version", gem_version] if gem_version
123
+ ::Dir.chdir(download_dir) do |dir|
124
+ logger.debug("Downloading in directory #{dir}")
125
+ safesystem(*gem_fetch)
126
+ end
127
+ gem_files = ::Dir.glob(File.join(download_dir, "*.gem"))
128
+ end
108
129
 
109
130
  if gem_files.length != 1
110
131
  raise "Unexpected number of gem files in #{download_dir}, #{gem_files.length} should be 1"
@@ -113,9 +134,19 @@ class FPM::Package::Gem < FPM::Package
113
134
  return gem_files.first
114
135
  end # def download
115
136
 
137
+ GEMSPEC_YAML_CLASSES = [ ::Gem::Specification, ::Gem::Version, Time, ::Gem::Dependency, ::Gem::Requirement, Symbol ]
116
138
  def load_package_info(gem_path)
117
-
118
- spec = YAML.load(%x{#{attributes[:gem_gem]} specification #{gem_path} --yaml})
139
+ # TODO(sissel): Maybe we should check if `safe_load` method exists instead of this version check?
140
+ if ::Gem::Version.new(RUBY_VERSION) >= ::Gem::Version.new("3.1.0")
141
+ # Ruby 3.1.0 switched to a Psych/YAML version that defaults to "safe" loading
142
+ # and unfortunately `gem specification --yaml` emits YAML that requires
143
+ # class loaders to process correctly
144
+ spec = YAML.load(%x{#{attributes[:gem_gem]} specification #{gem_path} --yaml},
145
+ :permitted_classes => GEMSPEC_YAML_CLASSES)
146
+ else
147
+ # Older versions of ruby call this method YAML.safe_load
148
+ spec = YAML.safe_load(%x{#{attributes[:gem_gem]} specification #{gem_path} --yaml}, GEMSPEC_YAML_CLASSES)
149
+ end
119
150
 
120
151
  if !attributes[:gem_package_prefix].nil?
121
152
  attributes[:gem_package_name_prefix] = attributes[:gem_package_prefix]
@@ -198,8 +229,13 @@ class FPM::Package::Gem < FPM::Package
198
229
 
199
230
  ::FileUtils.mkdir_p(installdir)
200
231
  # TODO(sissel): Allow setting gem tool path
201
- args = [attributes[:gem_gem], "install", "--quiet", "--no-ri", "--no-rdoc",
202
- "--no-user-install", "--install-dir", installdir]
232
+ args = [attributes[:gem_gem], "install", "--quiet", "--no-user-install", "--install-dir", installdir]
233
+ if ::Gem::VERSION =~ /^[012]\./
234
+ args += [ "--no-ri", "--no-rdoc" ]
235
+ else
236
+ # Rubygems 3.0.0 changed --no-ri to --no-document
237
+ args += [ "--no-document" ]
238
+ end
203
239
 
204
240
  if !attributes[:gem_embed_dependencies?]
205
241
  args += ["--ignore-dependencies"]
@@ -4,7 +4,6 @@ require "fileutils"
4
4
  require "fpm/package/dir"
5
5
  require 'tempfile' # stdlib
6
6
  require 'pathname' # stdlib
7
- require 'rexml/document' # stdlib
8
7
 
9
8
  # Use an OS X pkg built with pkgbuild.
10
9
  #
@@ -103,6 +102,7 @@ class FPM::Package::OSXpkg < FPM::Package
103
102
 
104
103
  # Extract name and version from PackageInfo XML
105
104
  def extract_info(package)
105
+ require 'rexml/document'
106
106
  build_path("expand").tap do |path|
107
107
  doc = REXML::Document.new File.open(File.join(path, "PackageInfo"))
108
108
  pkginfo_elem = doc.elements["pkg-info"]
@@ -148,6 +148,11 @@ class FPM::Package::OSXpkg < FPM::Package
148
148
  write_scripts
149
149
  args += ["--scripts", scripts_path]
150
150
  end
151
+
152
+ if attributes[:prefix]
153
+ args += ["--install-location", attributes[:prefix]]
154
+ end
155
+
151
156
  args << output_path
152
157
 
153
158
  safesystem("pkgbuild", *args)