fpm 1.17.0 → 1.18.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ef536c2af546fea798392b6cca4c07b81b5f9a3d52654e72c5cb9543d1d4a21d
4
- data.tar.gz: 95a4ea389bb037d2555ea95fd99313897b0f30c5b3bb8c5519a19d330b1e2842
3
+ metadata.gz: 0a0ebf62e58835b9331f5f56ec9689e95e725666d7fc2563afd617f2fc882bb4
4
+ data.tar.gz: 7d0043765bdef7b5db3f493978cc94951fbb6cb5ad15bc823a9875e67ad2afd5
5
5
  SHA512:
6
- metadata.gz: ccd36e905583145a6a80ca5efc62388ea2abdc1e35a8d3a077b5f946f2d2ea864a282fac0e77f9b5c0c82af09a96e6ae84067a6424b6d0d88f56282eea888111
7
- data.tar.gz: 6c4a93a51fe6af403ecda1910466b41843a8508837cea3f68042e56dba1083543f61a45b14d560167063b265279da57166368471dfc32bd8837c5e9d65847ed7
6
+ metadata.gz: e887592f69377440a23e193c3a2ce049c0fdc286b6699931bed7c4589785da9532998ef3ccd09946e61974a3f441719d28d9b19332d4a5aa3ccf3c63bfb5c210
7
+ data.tar.gz: 1d21ed70407432b95a8c7095e226073e357bbf7754579df271e216ffc28f4121df949ecb01b4a8718d47033ebd6687175fb133edcca4793269f8bc3f9ffb31db
data/CHANGELOG.rst CHANGED
@@ -1,6 +1,17 @@
1
1
  Release Notes and Change Log
2
2
  ============================
3
3
 
4
+ 1.18.0 (August 26, 2026)
5
+ ^^^^^^^^^^^^^^^^^^^^^^^^
6
+
7
+ * deb/macOS: Fix a bug on macOS caused by a behavior change to the ``ar`` utility provided by Xcode (`#2149`_, `#2148`_; Dan Pashin)
8
+ * Ruby 2.x support: Restored compatibility with older versions of Ruby such as Ruby 2.5.0. (`#2123`_; Jordan Sissel)
9
+ * cpan: Add flag --cpan-metacpan-api-url for custom MetaCPAN urls (`#2146`_; Nicholas B. Hubbard)
10
+ * cpan: Add flag ``--cpan-disable-dependency <name>`` to remove a dependency from the package (`#2128`_, `#2125`_; Nicholas B. Hubbard)
11
+ * cpan: MetaCPAN upgraded their Elasticsearch version which means fpm also needs to use the newer query syntax. (`#2144`_, `#2143`_; Jordan Sissel)
12
+ * cpan: Improvements to cpan search to better support cpan distributions (`#2125`_; Nicholas B. Hubbard)
13
+ * tests: Fixed a bunch of tests that were failing under Github Actions (`#2122`_; Nicholas B. Hubbard)
14
+
4
15
  1.17.0 (October 2, 2025)
5
16
  ^^^^^^^^^^^^^^^^^^^^^^^^
6
17
 
data/lib/fpm/command.rb CHANGED
@@ -673,7 +673,7 @@ class FPM::Command < Clamp::Command
673
673
  option.of(self).take(value)
674
674
  end
675
675
  end
676
- end
676
+ end # def load_options
677
677
 
678
678
  # A simple flag validator
679
679
  #
@@ -757,4 +757,4 @@ class FPM::Command < Clamp::Command
757
757
 
758
758
  public(:initialize, :ok?, :messages)
759
759
  end # class Validator
760
- end # class FPM::Program
760
+ end # class FPM::Command
@@ -22,6 +22,13 @@ class FPM::Package::CPAN < FPM::Package
22
22
  option "--package-name-prefix", "NAME_PREFIX",
23
23
  "Name to prefix the package name with.", :default => "perl"
24
24
 
25
+ option "--disable-dependency", "MODULE",
26
+ "Filter modules matching MODULE from those generated by " \
27
+ "the auto-depends for CPAN modules. This flag can be specified " \
28
+ "multiple times. Use the perl module name, IE: URI::Escape.",
29
+ :multivalued => true, :attribute_name => :rejects,
30
+ :default => ['vars','warnings','strict','Config']
31
+
25
32
  option "--test", :flag,
26
33
  "Run the tests before packaging?", :default => true
27
34
 
@@ -37,6 +44,10 @@ class FPM::Package::CPAN < FPM::Package
37
44
  option "--cpanm-force", :flag,
38
45
  "Pass the --force parameter to cpanm", :default => false
39
46
 
47
+ option "--metacpan-api-url", "URL",
48
+ "URL for the MetaCPAN api",
49
+ :default => "https://fastapi.metacpan.org"
50
+
40
51
  private
41
52
  def input(package)
42
53
  #if RUBY_VERSION =~ /^1\.8/
@@ -46,7 +57,6 @@ class FPM::Package::CPAN < FPM::Package
46
57
  #"me know by filing an issue: " \
47
58
  #"https://github.com/jordansissel/fpm/issues"
48
59
  #end
49
- #require "ftw" # for http access
50
60
  require "net/http"
51
61
  require "json"
52
62
 
@@ -54,7 +64,7 @@ class FPM::Package::CPAN < FPM::Package
54
64
  moduledir = package
55
65
  result = {}
56
66
  else
57
- result = search(package)
67
+ result = search_module(package, version)
58
68
  tarball = download(result, version)
59
69
  moduledir = unpack(tarball)
60
70
  end
@@ -92,20 +102,17 @@ class FPM::Package::CPAN < FPM::Package
92
102
  else; metadata["license"]
93
103
  end
94
104
 
95
- unless metadata["distribution"].nil?
105
+ unless metadata["release"].nil?
106
+ dist_name, _, dist_version = metadata["release"].rpartition('-')
96
107
  logger.info("Setting package name from 'distribution'",
97
- :distribution => metadata["distribution"])
98
- self.name = fix_name(metadata["distribution"])
108
+ :distribution => dist_name)
109
+ self.name = fix_name(dist_name)
110
+ self.provides = search_provided_modules(dist_name, dist_version)
99
111
  else
100
112
  logger.info("Setting package name from 'name'",
101
113
  :name => metadata["name"])
102
114
  self.name = fix_name(metadata["name"])
103
- end
104
-
105
- unless metadata["module"].nil?
106
- metadata["module"].each do |m|
107
- self.provides << cap_name(m["name"]) + " = #{self.version}"
108
- end
115
+ self.provides << cap_name(metadata["name"]) + " = #{self.version}"
109
116
  end
110
117
 
111
118
  # author is not always set or it may be a string instead of an array
@@ -168,7 +175,6 @@ class FPM::Package::CPAN < FPM::Package
168
175
  self.dependencies << "#{dep_name} >= #{version}"
169
176
  next
170
177
  end
171
- dep = search(dep_name)
172
178
 
173
179
  name = cap_name(dep_name)
174
180
 
@@ -299,7 +305,7 @@ class FPM::Package::CPAN < FPM::Package
299
305
  self.architecture = "native"
300
306
  end
301
307
  end
302
- end
308
+ end # def input
303
309
 
304
310
  def unpack(tarball)
305
311
  directory = build_path("module")
@@ -308,7 +314,7 @@ class FPM::Package::CPAN < FPM::Package
308
314
  %q{--transform=s,[./]*[^/]*/,,} ]
309
315
  safesystem("tar", *args)
310
316
  return directory
311
- end
317
+ end # def unpack
312
318
 
313
319
  def download(metadata, cpan_version=nil)
314
320
  distribution = metadata["distribution"]
@@ -326,8 +332,8 @@ class FPM::Package::CPAN < FPM::Package
326
332
  end
327
333
 
328
334
  # Search metacpan to get download URL for this version of the module
329
- metacpan_search_url = "https://fastapi.metacpan.org/v1/release/_search"
330
- metacpan_search_query = '{"fields":["download_url"],"filter":{"term":{"name":"' + "#{distribution}-#{self.version}" + '"}}}'
335
+ metacpan_search_url = "#{attributes[:cpan_metacpan_api_url]}/v1/release/_search?_source=download_url"
336
+ metacpan_search_query = {"query":{"term":{"name": "#{distribution}-#{self.version}" } } }.to_json
331
337
  begin
332
338
  search_response = httppost(metacpan_search_url,metacpan_search_query)
333
339
  rescue Net::HTTPServerException => e
@@ -339,7 +345,7 @@ class FPM::Package::CPAN < FPM::Package
339
345
  data = search_response.body
340
346
  release_metadata = JSON.parse(data)
341
347
 
342
- download_url = release_metadata['hits']['hits'][0]['fields']['download_url']
348
+ download_url = release_metadata['hits']['hits'][0]['_source']['download_url']
343
349
  download_path = URI.parse(download_url).path
344
350
  tarball = File.basename(download_path)
345
351
 
@@ -365,25 +371,81 @@ class FPM::Package::CPAN < FPM::Package
365
371
  return build_path(tarball)
366
372
  end # def download
367
373
 
368
- def search(package)
369
- logger.info("Asking metacpan about a module", :module => package)
370
- metacpan_url = "https://fastapi.metacpan.org/v1/module/" + package
374
+ def search_module(module_name, version=nil)
375
+ logger.info("Asking metacpan about a module", :module => module_name, :version => version)
376
+ metacpan_api_url = "#{attributes[:cpan_metacpan_api_url]}/v1/module/_search"
377
+ metacpan_api_query = <<-EOS
378
+ {
379
+ "query": {
380
+ "bool": {
381
+ "must": [
382
+ { "term": { "module.name": "#{module_name}" } },
383
+ { "term": { "maturity": "released" } },
384
+ { "term": { #{version.nil? ? '"status": "latest"' : '"module.version": "' + version + '"'} } }
385
+ ]
386
+ }
387
+ },
388
+ "size": 1
389
+ }
390
+ EOS
371
391
  begin
372
- response = httpfetch(metacpan_url)
392
+ response = httppost(metacpan_api_url, metacpan_api_query)
373
393
  rescue Net::HTTPServerException => e
374
394
  #logger.error("metacpan query failed.", :error => response.status_line,
375
395
  #:module => package, :url => metacpan_url)
376
396
  logger.error("metacpan query failed.", :error => e.message,
377
- :module => package, :url => metacpan_url)
397
+ :module => module_name, :version => version, :url => metacpan_api_url)
378
398
  raise FPM::InvalidPackageConfiguration, "metacpan query failed"
379
399
  end
380
400
 
381
401
  #data = ""
382
402
  #response.read_body { |c| p c; data << c }
383
403
  data = response.body
384
- metadata = JSON.parse(data)
404
+ metadata = JSON.parse(data)['hits']['hits'][0]['_source']
385
405
  return metadata
386
- end # def metadata
406
+ end # def search
407
+
408
+ def search_provided_modules(distribution, version)
409
+ logger.info("Asking metacpan about a releases provided modules",
410
+ :distribution => distribution,
411
+ :version => version)
412
+ metacpan_api_url = "#{attributes[:cpan_metacpan_api_url]}/v1/module/_search?_source=module.name,module.version"
413
+ metacpan_api_query = <<-EOS
414
+ {
415
+ "query": {
416
+ "bool": {
417
+ "filter": [
418
+ { "exists": { "field": "module" } },
419
+ { "term": { "release": "#{distribution}-#{version}" } }
420
+ ]
421
+ }
422
+ },
423
+ "sort": [ { "module.name": { "order": "asc" } } ],
424
+ "size": 5000
425
+ }
426
+ EOS
427
+
428
+ begin
429
+ response = httppost(metacpan_api_url, metacpan_api_query)
430
+ rescue Net::HTTPServerException => e
431
+ logger.error("metacpan release query failed.", :error => e.message,
432
+ :url => metacpan_api_url)
433
+ raise FPM::InvalidPackageConfiguration, "metacpan release query failed"
434
+ end
435
+ query_hits = JSON.parse(response.body)['hits']['hits']
436
+
437
+ provided_modules = []
438
+ query_hits.each do |h|
439
+ h["_source"]["module"].each do |m|
440
+ module_name = m["name"]
441
+ module_version = m["version"]
442
+ Array(module_name).zip(Array(module_version)).each do |name, version|
443
+ provided_modules << cap_name(name) + (version ? " = #{version}" : "")
444
+ end
445
+ end
446
+ end
447
+ return provided_modules
448
+ end # def search_provided_modules
387
449
 
388
450
  def cap_name(name)
389
451
  return "perl(" + name.gsub("-", "::") + ")"
@@ -411,7 +473,7 @@ class FPM::Package::CPAN < FPM::Package
411
473
  when Net::HTTPRedirection; return httpfetch(response["location"])
412
474
  else; response.error!
413
475
  end
414
- end
476
+ end # def httpfetch
415
477
 
416
478
  def httppost(url, body)
417
479
  uri = URI.parse(url)
@@ -428,7 +490,7 @@ class FPM::Package::CPAN < FPM::Package
428
490
  when Net::HTTPRedirection; return httppost(response["location"])
429
491
  else; response.error!
430
492
  end
431
- end
493
+ end # def httppost
432
494
 
433
495
  public(:input)
434
- end # class FPM::Package::NPM
496
+ end # class FPM::Package::CPAN
@@ -542,6 +542,20 @@ class FPM::Package::Deb < FPM::Package
542
542
  raise FPM::InvalidPackageConfiguration, "#{name}: tar is insufficient to support source_date_epoch."
543
543
  end
544
544
 
545
+ systemd_file_extensions = [
546
+ ".service",
547
+ ".socket",
548
+ ".device",
549
+ ".mount",
550
+ ".automount",
551
+ ".swap",
552
+ ".target",
553
+ ".path",
554
+ ".timer",
555
+ ".slice",
556
+ ".scope",
557
+ ]
558
+
545
559
  attributes[:deb_systemd] = []
546
560
  attributes.fetch(:deb_systemd_list, []).each do |systemd|
547
561
  name = File.basename(systemd)
@@ -549,10 +563,16 @@ class FPM::Package::Deb < FPM::Package
549
563
 
550
564
  name_with_extension = if extname.empty?
551
565
  "#{name}.service"
552
- elsif [".service", ".timer"].include?(extname)
566
+ elsif systemd_file_extensions.include?(extname)
553
567
  name
554
568
  else
555
- raise FPM::InvalidPackageConfiguration, "Invalid systemd unit file extension: #{extname}. Expected .service or .timer, or no extension."
569
+ # Mutating the array is fine as we raise directly after, and the array will be re-initialised next time
570
+ # this method is called. If this branch is changed in the future so as not to diverge, care should be
571
+ # taken to ensure that this mutated version of the array is only used for generating the error message.
572
+ systemd_file_extensions[-1] = systemd_file_extensions[-1].prepend("or ")
573
+ possible_extensions_str = systemd_file_extensions.join(", ")
574
+ raise FPM::InvalidPackageConfiguration,
575
+ "Invalid systemd unit file extension: #{extname}. Expected one of: #{possible_extensions_str}"
556
576
  end
557
577
 
558
578
  dest_systemd = staging_path(File.join(attributes[:deb_systemd_path], "#{name_with_extension}"))
@@ -767,10 +787,19 @@ class FPM::Package::Deb < FPM::Package
767
787
  self.provides = self.provides.reject { |p| p.empty? }
768
788
 
769
789
  if origin == FPM::Package::CPAN
790
+
791
+ # By default, we'd prefer to name Debian-targeted Perl packages using the
792
+ # same naming scheme that Debian itself uses, which is usually something
793
+ # like "lib<module-name-hyphenated>-perl", such as libregexp-common-perl
794
+ #
795
+ logger.info("Changing package name to match Debian's typical libmodule-name-perl style")
796
+ self.name = "lib#{self.name.sub(/^perl-/, "")}-perl"
797
+
770
798
  # The fpm cpan code presents dependencies and provides fields as perl(ModuleName)
771
799
  # so we'll need to convert them to something debian supports.
772
800
 
773
- # Replace perl(ModuleName) > 1.0 with Debian-style perl-ModuleName (> 1.0)
801
+ # Replace perl(Module::Name) > 1.0 with Debian-style libmodule-name-perl (> 1.0)
802
+ # per: https://www.debian.org/doc/packaging-manuals/perl-policy/ch-module_packages.html
774
803
  perldepfix = lambda do |dep|
775
804
  m = dep.match(/perl\((?<name>[A-Za-z0-9_:]+)\)\s*(?<op>.*$)/)
776
805
  if m.nil?
@@ -781,7 +810,7 @@ class FPM::Package::Deb < FPM::Package
781
810
  modulename = m["name"].gsub("::", "-")
782
811
 
783
812
  # Fix any upper-casing or other naming concerns Debian has about packages
784
- name = "#{attributes[:cpan_package_name_prefix]}-#{modulename}"
813
+ name = "lib#{modulename}-perl"
785
814
 
786
815
  if m["op"].empty?
787
816
  name
@@ -792,9 +821,12 @@ class FPM::Package::Deb < FPM::Package
792
821
  end
793
822
  end
794
823
 
795
- rejects = [ "perl(vars)", "perl(warnings)", "perl(strict)", "perl(Config)" ]
824
+ rejects = attributes[:rejects].map do |skip|
825
+ "perl(#{skip})"
826
+ end
796
827
  self.dependencies = self.dependencies.reject do |dep|
797
828
  # Reject non-module Perl dependencies like 'vars' and 'warnings'
829
+ dep, _ = dep.split(' ') # strip potential version component
798
830
  rejects.include?(dep)
799
831
  end.collect(&perldepfix).collect(&method(:fix_dependency))
800
832
 
@@ -1174,7 +1206,7 @@ class FPM::Package::Deb < FPM::Package
1174
1206
  end
1175
1207
  upstarts.each do |upstart|
1176
1208
  name = File.basename(upstart, ".upstart")
1177
- upstartscript = "etc/init/#{name}.conf"
1209
+ upstartscript = "/etc/init/#{name}.conf"
1178
1210
  logger.debug("Add conf file declaration for upstart script", :script => upstartscript)
1179
1211
  allconfigs << upstartscript[1..-1]
1180
1212
  end
@@ -381,7 +381,7 @@ class FPM::Package::Gem < FPM::Package
381
381
  logger.debug("Gem %s did not have changelog with recognized name" % [name])
382
382
  # FIXME: check rubygems.org?
383
383
  end
384
- end # detect_source_date_from_changelog
384
+ end # def detect_source_date_from_changelog
385
385
 
386
386
  public(:input, :output)
387
387
  end # class FPM::Package::Gem
@@ -2,6 +2,7 @@
2
2
 
3
3
  #import pkg_resources
4
4
  import packaging.requirements
5
+ import packaging.markers
5
6
  import json
6
7
  import sys
7
8
 
@@ -11,10 +12,9 @@ import sys
11
12
  # Process environment markers, if any, and produce a list of requirements for the current environment.
12
13
  def evaluate_requirements(fd):
13
14
  all_requirements = [packaging.requirements.Requirement(line) for line in sys.stdin]
14
-
15
+ default_env = packaging.markers.default_environment()
15
16
  for req in all_requirements:
16
- # XXX: Note: marker.evaluate() can be given a dict() containing environment values to overwrite
17
- if req.marker is None or req.marker.evaluate():
17
+ if req.marker is None or req.marker.evaluate(environment=default_env):
18
18
  if len(req.specifier) > 0:
19
19
  for spec in req.specifier:
20
20
  yield "%s%s" % (req.name, spec)
@@ -119,7 +119,7 @@ class FPM::Package::Python < FPM::Package
119
119
  headers[field] = []
120
120
  end
121
121
 
122
- while !s.eos? and !s.scan("\n") do
122
+ while !s.eos? and !s.scan(/\n/) do
123
123
  # Field is non-space up, but excluding the colon
124
124
  field = s.scan(/[^\s:]+/)
125
125
 
@@ -148,11 +148,11 @@ class FPM::Package::Python < FPM::Package
148
148
  # Per Python core-metadata spec:
149
149
  # > Changed in version 2.1: This field may be specified in the message body instead.
150
150
  #return PythonMetadata.new(headers, s.string[s.pos ...])
151
- return headers, s.string[s.pos ... ]
151
+ return headers, s.string[s.pos .. -1]
152
152
  elsif headers["Metadata-Version"].to_f >= 2.0
153
153
  # dnspython v1.15.0 has a description body and Metadata-Version 2.0
154
154
  # this seems out of spec, but let's accept it anyway.
155
- return headers, s.string[s.pos ... ]
155
+ return headers, s.string[s.pos .. -1]
156
156
  else
157
157
  raise "After reading METADATA headers, extra data is in the file but was not expected. This may be a bug in fpm."
158
158
  end
@@ -166,7 +166,7 @@ class FPM::Package::Python < FPM::Package
166
166
  puts "---"
167
167
  puts input
168
168
  puts "==="
169
- puts input[s.pointer...]
169
+ puts input[s.pointer..-1]
170
170
  puts "---"
171
171
  raise e
172
172
  end # self.parse
@@ -453,7 +453,7 @@ class FPM::Package::Python < FPM::Package
453
453
 
454
454
  safesystem(*setup_cmd)
455
455
 
456
- files = ::Dir.entries(target).filter { |entry| entry =~ /\.(whl|tgz|tar\.gz|zip)$/ }
456
+ files = ::Dir.entries(target).select { |entry| entry =~ /\.(whl|tgz|tar\.gz|zip)$/ }
457
457
  if files.length != 1
458
458
  raise "Unexpected directory layout after `pip download ...`. This might be an fpm bug? The directory contains these files: #{files.inspect}"
459
459
  end
@@ -605,7 +605,7 @@ class FPM::Package::Python < FPM::Package
605
605
  flags = [ "--root", staging_path ]
606
606
  flags += [ "--prefix", prefix ] if !attributes[:prefix].nil?
607
607
 
608
- safesystem(*attributes[:python_pip], "install", "--no-deps", *flags, path)
608
+ safesystem(*attributes[:python_pip], "install", "--no-deps", "--ignore-installed", *flags, path)
609
609
  end # def install_to_staging
610
610
 
611
611
  public(:input)
data/lib/fpm/package.rb CHANGED
@@ -226,7 +226,7 @@ class FPM::Package
226
226
  def converted_from(origin)
227
227
  # nothing to do by default. Subclasses may implement this.
228
228
  # See the RPM package class for an example.
229
- end # def converted
229
+ end # def converted_from
230
230
 
231
231
  # Add a new source to this package.
232
232
  # The exact behavior depends on the kind of package being managed.
@@ -324,7 +324,7 @@ class FPM::Package
324
324
 
325
325
  def template_dir
326
326
  File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "templates"))
327
- end
327
+ end # def template_dir
328
328
 
329
329
  def template(path)
330
330
  template_path = File.join(template_dir, path)
@@ -437,7 +437,7 @@ class FPM::Package
437
437
 
438
438
  help = "(#{type} only) #{help}"
439
439
  @options << [flag, param, help, options, block]
440
- end # def options
440
+ end # def option
441
441
 
442
442
  # Apply the options for this package on the clamp command
443
443
  #
@@ -510,7 +510,7 @@ class FPM::Package
510
510
  File.chmod(0755, out)
511
511
  end
512
512
  end
513
- end
513
+ end # def write_scripts
514
514
 
515
515
  # Get the contents of the script by a given name.
516
516
  #
@@ -547,7 +547,7 @@ class FPM::Package
547
547
  else
548
548
  @provides = value
549
549
  end
550
- end
550
+ end # def provides=
551
551
 
552
552
  # General public API
553
553
  public(:type, :initialize, :convert, :input, :output, :to_s, :cleanup, :files,
data/lib/fpm/util.rb CHANGED
@@ -38,7 +38,7 @@ module FPM::Util
38
38
  shell = ENV["SHELL"]
39
39
  return "/bin/sh" if shell.nil? || shell.empty?
40
40
  return shell
41
- end
41
+ end # def default_shell
42
42
 
43
43
  ############################################################################
44
44
  # execmd([env,] cmd [,opts])
@@ -250,7 +250,7 @@ module FPM::Util
250
250
  end
251
251
  end
252
252
  # If no combination of ar and options omits timestamps, fall back to default.
253
- @@ar_cmd = ["ar", "-qc"]
253
+ @@ar_cmd = ["ar", "-qcS"]
254
254
  FileUtils.rm_f([testarchive, emptyfile])
255
255
  return @@ar_cmd
256
256
  end # def ar_cmd
@@ -259,7 +259,7 @@ module FPM::Util
259
259
  def ar_cmd_deterministic?
260
260
  ar_cmd if not defined? @@ar_cmd_deterministic
261
261
  return @@ar_cmd_deterministic
262
- end
262
+ end # def ar_cmd_deterministic?
263
263
 
264
264
  # Get the recommended 'tar' command for this platform.
265
265
  def tar_cmd
@@ -306,7 +306,7 @@ module FPM::Util
306
306
  def tar_cmd_supports_sort_names_and_set_mtime?
307
307
  tar_cmd if not defined? @@tar_cmd_deterministic
308
308
  return @@tar_cmd_deterministic
309
- end
309
+ end # def tar_cmd_supports_sort_names_and_set_mtime?
310
310
 
311
311
  def copy_metadata(source, destination)
312
312
  source_stat = File::lstat(source)
@@ -452,7 +452,7 @@ module FPM::Util
452
452
  # Ruby 3.1.0 and newer
453
453
  return ERB.new(template_code, trim_mode: "-")
454
454
  end
455
- end
455
+ end # def erbnew
456
456
  end # module FPM::Util
457
457
 
458
458
  require 'fpm/util/tar_writer'
data/lib/fpm/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module FPM
2
- VERSION = "1.17.0"
2
+ VERSION = "1.18.0"
3
3
  end
data/templates/sh.erb CHANGED
@@ -137,7 +137,7 @@ function save_environment(){
137
137
 
138
138
  # just piping env to a file doesn't quote the variables. This does
139
139
  # filter out multiline junk, _, and functions. _ is a readonly variable.
140
- env | grep -v "^_=" | grep -v "^[^=(]*()=" | egrep "^[^ ]+=" | while read ENVVAR ; do
140
+ env | grep -v "^_=" | grep -v "^[^=(]*()=" | grep -v "^BASH_FUNC_.*%%=" |egrep "^[^ ]+=" | while read ENVVAR ; do
141
141
  local NAME=${ENVVAR%%=*}
142
142
  # sed is to preserve variable values with dollars (for escaped variables or $() style command replacement),
143
143
  # and command replacement backticks
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fpm
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.17.0
4
+ version: 1.18.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jordan Sissel
8
+ autorequire:
8
9
  bindir: bin
9
10
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
11
+ date: 2026-08-26 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: cabin
@@ -197,7 +198,6 @@ files:
197
198
  - lib/fpm/package/pleaserun.rb
198
199
  - lib/fpm/package/puppet.rb
199
200
  - lib/fpm/package/pyfpm/__init__.py
200
- - lib/fpm/package/pyfpm/get_metadata.py
201
201
  - lib/fpm/package/pyfpm/parse_requires.py
202
202
  - lib/fpm/package/python.rb
203
203
  - lib/fpm/package/rpm.rb
@@ -238,6 +238,7 @@ homepage: https://github.com/jordansissel/fpm
238
238
  licenses:
239
239
  - MIT-like
240
240
  metadata: {}
241
+ post_install_message:
241
242
  rdoc_options: []
242
243
  require_paths:
243
244
  - lib
@@ -253,7 +254,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
253
254
  - !ruby/object:Gem::Version
254
255
  version: '0'
255
256
  requirements: []
256
- rubygems_version: 3.6.7
257
+ rubygems_version: 3.0.3.1
258
+ signing_key:
257
259
  specification_version: 4
258
260
  summary: fpm - package building and mangling
259
261
  test_files: []
@@ -1,115 +0,0 @@
1
- from distutils.core import Command
2
- import os
3
- import sys
4
- import pkg_resources
5
- try:
6
- import json
7
- except ImportError:
8
- import simplejson as json
9
-
10
- PY3 = sys.version_info[0] == 3
11
-
12
- if PY3:
13
- def u(s):
14
- return s
15
- else:
16
- def u(s):
17
- if isinstance(u, unicode):
18
- return u
19
- return s.decode('utf-8')
20
-
21
-
22
- # Note, the last time I coded python daily was at Google, so it's entirely
23
- # possible some of my techniques below are outdated or bad.
24
- # If you have fixes, let me know.
25
-
26
-
27
- class get_metadata(Command):
28
- description = "get package metadata"
29
- user_options = [
30
- ('load-requirements-txt', 'l',
31
- "load dependencies from requirements.txt"),
32
- ("output=", "o", "output destination for metadata json")
33
- ]
34
- boolean_options = ['load-requirements-txt']
35
-
36
- def initialize_options(self):
37
- self.load_requirements_txt = False
38
- self.cwd = None
39
- self.output = None
40
-
41
- def finalize_options(self):
42
- self.cwd = os.getcwd()
43
- self.requirements_txt = os.path.join(self.cwd, "requirements.txt")
44
- # make sure we have a requirements.txt
45
- if self.load_requirements_txt:
46
- self.load_requirements_txt = os.path.exists(self.requirements_txt)
47
-
48
- def process_dep(self, dep):
49
- deps = []
50
- if hasattr(dep, 'marker') and dep.marker:
51
- # PEP0508 marker present
52
- if not dep.marker.evaluate():
53
- return deps
54
-
55
- if dep.specs:
56
- for operator, version in dep.specs:
57
- deps.append("%s %s %s" % (dep.project_name,
58
- operator, version))
59
- else:
60
- deps.append(dep.project_name)
61
-
62
- return deps
63
-
64
- def run(self):
65
- data = {
66
- "name": self.distribution.get_name(),
67
- "version": self.distribution.get_version(),
68
- "author": u("%s <%s>") % (
69
- u(self.distribution.get_author()),
70
- u(self.distribution.get_author_email()),
71
- ),
72
- "description": self.distribution.get_description(),
73
- "license": self.distribution.get_license(),
74
- "url": self.distribution.get_url(),
75
- }
76
-
77
- if self.distribution.has_ext_modules():
78
- data["architecture"] = "native"
79
- else:
80
- data["architecture"] = "all"
81
-
82
- final_deps = []
83
-
84
- if self.load_requirements_txt:
85
- requirement = open(self.requirements_txt).readlines()
86
- for dep in pkg_resources.parse_requirements(requirement):
87
- final_deps.extend(self.process_dep(dep))
88
- else:
89
- if getattr(self.distribution, 'install_requires', None):
90
- for dep in pkg_resources.parse_requirements(
91
- self.distribution.install_requires):
92
- final_deps.extend(self.process_dep(dep))
93
- if getattr(self.distribution, 'extras_require', None):
94
- for dep in pkg_resources.parse_requirements(
95
- v for k, v in self.distribution.extras_require.items()
96
- if k.startswith(':') and pkg_resources.evaluate_marker(k[1:])):
97
- final_deps.extend(self.process_dep(dep))
98
-
99
- data["dependencies"] = final_deps
100
-
101
- output = open(self.output, "w")
102
- if hasattr(json, 'dumps'):
103
- def default_to_str(obj):
104
- """ Fall back to using __str__ if possible """
105
- # This checks if the class of obj defines __str__ itself,
106
- # so we don't fall back to an inherited __str__ method.
107
- if "__str__" in type(obj).__dict__:
108
- return str(obj)
109
- return json.JSONEncoder.default(self, obj)
110
-
111
- output.write(json.dumps(data, indent=2, default=default_to_str))
112
- else:
113
- # For Python 2.5 and Debian's python-json
114
- output.write(json.write(data))
115
- output.close()