From 49093c353382c095a56706ad46ab297e61047374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Beauz=C3=A9e-Luyssen?= Date: Thu, 23 Nov 2023 17:08:22 +0100 Subject: [PATCH 01/12] deb: add a --compression-level option Fix #2006 --- lib/fpm/package/deb.rb | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/lib/fpm/package/deb.rb b/lib/fpm/package/deb.rb index 4d97dfb0cc..5c80badad0 100644 --- a/lib/fpm/package/deb.rb +++ b/lib/fpm/package/deb.rb @@ -79,6 +79,19 @@ class FPM::Package::Deb < FPM::Package value end + option "--compression-level", "[0-9]", "Select a compression level. 0 is none or minimal. 9 is max compression.", + # Specify which compression level to use on the compressor backend, when building a package + :default => nil do |value| + valint = value.to_i + # if self.attributes[:deb_compression].nil? + # raise "Can't specify a compression level with compression disabled" + # end + unless value =~ /^\d$/ && valint >= 0 && valint <= 9 + raise "Invalid compression level '#{value}'. Valid values are integers between 0 and 9 inclusive." + end + valint + end + option "--dist", "DIST-TAG", "Set the deb distribution.", :default => "unstable" # Take care about the case when we want custom control file but still use fpm ... @@ -642,18 +655,24 @@ def output(output_path) datatar = build_path("data.tar.gz") controltar = build_path("control.tar.gz") compression_flags = ["-z"] + # gnu tar obeys GZIP environment variable with options for gzip; -n = forget original filename and date + compressor_options = {"GZIP" => "-#{self.attributes[:deb_compression_level] || 9}" + + "#{'n' if tar_cmd_supports_sort_names_and_set_mtime? and not attributes[:source_date_epoch].nil?}"} when "bzip2" datatar = build_path("data.tar.bz2") controltar = build_path("control.tar.gz") compression_flags = ["-j"] + compressor_options = {"BZIP" => "-#{self.attributes[:deb_compression_level] || 9}"} when "xz" datatar = build_path("data.tar.xz") controltar = build_path("control.tar.xz") compression_flags = ["-J"] + compressor_options = {"XZ_OPT" => "-#{self.attributes[:deb_compression_level] || 3}"} when "none" datatar = build_path("data.tar") controltar = build_path("control.tar") compression_flags = [] + compressor_options = {} else raise FPM::InvalidPackageConfiguration, "Unknown compression type '#{self.attributes[:deb_compression]}'" @@ -662,9 +681,8 @@ def output(output_path) if tar_cmd_supports_sort_names_and_set_mtime? and not attributes[:source_date_epoch].nil? # Use gnu tar options to force deterministic file order and timestamp args += ["--sort=name", ("--mtime=@%s" % attributes[:source_date_epoch])] - # gnu tar obeys GZIP environment variable with options for gzip; -n = forget original filename and date - args.unshift({"GZIP" => "-9n"}) end + args.unshift(compressor_options) safesystem(*args) # pack up the .deb, which is just an 'ar' archive with 3 files @@ -721,7 +739,7 @@ def converted_from(origin) else # Also replace '::' in the perl module name with '-' modulename = m["name"].gsub("::", "-") - + # Fix any upper-casing or other naming concerns Debian has about packages name = "#{attributes[:cpan_package_name_prefix]}-#{modulename}" @@ -934,12 +952,17 @@ def write_control_tarball when "gz", "bzip2", nil controltar = "control.tar.gz" compression_flags = ["-z"] + # gnu tar obeys GZIP environment variable with options for gzip; -n = forget original filename and date + compressor_options = {"GZIP" => "-#{self.attributes[:deb_compression_level] || 9}" + + "#{'n' if tar_cmd_supports_sort_names_and_set_mtime? and not attributes[:source_date_epoch].nil?}"} when "xz" controltar = "control.tar.xz" compression_flags = ["-J"] + compressor_options = {"XZ_OPT" => "-#{self.attributes[:deb_compression_level] || 3}"} when "none" controltar = "control.tar" compression_flags = [] + compressor_options = {} else raise FPM::InvalidPackageConfiguration, "Unknown compression type '#{self.attributes[:deb_compression]}'" @@ -954,9 +977,8 @@ def write_control_tarball if tar_cmd_supports_sort_names_and_set_mtime? and not attributes[:source_date_epoch].nil? # Force deterministic file order and timestamp args += ["--sort=name", ("--mtime=@%s" % attributes[:source_date_epoch])] - # gnu tar obeys GZIP environment variable with options for gzip; -n = forget original filename and date - args.unshift({"GZIP" => "-9n"}) end + args.unshift(compressor_options) safesystem(*args) end From c9d749a3a793bda82527d232da815dc2c1b8c7e4 Mon Sep 17 00:00:00 2001 From: Gordon Bleux <33967640+UiP9AV6Y@users.noreply.github.com> Date: Thu, 14 Dec 2023 21:21:33 +0100 Subject: [PATCH 02/12] rpm: generate changelog if none is provided `rpmlint` considers a lack of a changelog an error. this is a similar behaviour to the DEB packager, where a generic default changelog is generated unless one is provided explicitly. --- lib/fpm/package/rpm.rb | 18 +++++++++++++++++ spec/fpm/package/rpm_spec.rb | 39 ++++++++++++++++++++++++++++++++++++ templates/rpm.erb | 2 +- 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/lib/fpm/package/rpm.rb b/lib/fpm/package/rpm.rb index a925256561..0159c7e121 100644 --- a/lib/fpm/package/rpm.rb +++ b/lib/fpm/package/rpm.rb @@ -275,6 +275,24 @@ def iteration return @iteration ? @iteration : 1 end # def iteration + # Generate a generic changelog or return an existing definition + def changelog + if attributes[:rpm_changelog] + return attributes[:rpm_changelog] + end + + reldate = if attributes[:source_date_epoch].nil? + Time.now() + else + Time.at(attributes[:source_date_epoch].to_i) + end + changed = reldate.strftime("%a %b %_e %Y") + changev = "#{version}-#{iteration}" + changev += "%{?dist}" if attributes[:rpm_dist] + + "* #{changed} #{maintainer} - #{changev}\n- Package created with FPM\n" + end + # See FPM::Package#converted_from def converted_from(origin) if origin == FPM::Package::Gem diff --git a/spec/fpm/package/rpm_spec.rb b/spec/fpm/package/rpm_spec.rb index 77d62abc9d..015a801f4a 100644 --- a/spec/fpm/package/rpm_spec.rb +++ b/spec/fpm/package/rpm_spec.rb @@ -484,6 +484,45 @@ def subject.render_template; @rpmspec = template("rpm.erb").result(binding); end File.unlink(@target) end end # dist + + context "changelog" do + it "should generate a changelog in the release" do + subject.name = "example" + subject.attributes[:rpm_dist] = 'rhel' + subject.version = "1.2.3" + subject.maintainer = "Spec Test " + @target = Stud::Temporary.pathname + + # Write RPM + subject.output(@target) + + @rpm = ::RPM::File.new(@target) + insist { @rpm.tags[:changelogname] } == [ "Spec Test - 1.2.3-1.rhel" ] + insist { @rpm.tags[:changelogtext] } == [ "- Package created with FPM" ] + + File.unlink(@target) + end + + it "should have the changelog in the release" do + subject.name = "example" + subject.attributes[:rpm_changelog] = < - 1.0-1 +- First example package +CHANGELOG + subject.version = "1.0" + @target = Stud::Temporary.pathname + + # Write RPM + subject.output(@target) + + @rpm = ::RPM::File.new(@target) + insist { @rpm.tags[:changelogtime] } == [ 1464696000 ] + insist { @rpm.tags[:changelogname] } == [ "Example Maintainers - 1.0-1" ] + insist { @rpm.tags[:changelogtext] } == [ "- First example package" ] + + File.unlink(@target) + end + end # changelog end # #output describe "prefix attribute" do diff --git a/templates/rpm.erb b/templates/rpm.erb index 0e851f26d9..584038f636 100644 --- a/templates/rpm.erb +++ b/templates/rpm.erb @@ -260,4 +260,4 @@ fi <% end -%> %changelog -<%= attributes[:rpm_changelog] %> +<%= changelog %> From 3b2b258020892926f362666647599f77a5d30be2 Mon Sep 17 00:00:00 2001 From: Jordan Stopford Date: Fri, 8 Mar 2024 10:37:34 +0000 Subject: [PATCH 03/12] Fix for #1627 When packaging a deb sometimes the architecture comes through as part of the provides (not sure why) but this causes problems with the deb package as it seems to only ever expect one set of round brackets which is the version indicator --- lib/fpm/package/deb.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/fpm/package/deb.rb b/lib/fpm/package/deb.rb index 4d97dfb0cc..cfa6c12d4f 100644 --- a/lib/fpm/package/deb.rb +++ b/lib/fpm/package/deb.rb @@ -262,6 +262,9 @@ def architecture when "noarch" # Debian calls noarch "all" @architecture = "all" + when "ppc64le" + # Debian calls ppc64le "ppc64el" + @architecture = "ppc64el" end return @architecture end # def architecture @@ -707,6 +710,9 @@ def converted_from(origin) self.provides = self.provides.collect do |provides| fix_provides(provides) end.flatten + # If an invalid provides field was found i.e. mypackage(arch) then fix_provides will blank it + # Make sure we remove this blank here + self.provides = self.provides.reject { |p| p.empty? } if origin == FPM::Package::CPAN # The fpm cpan code presents dependencies and provides fields as perl(ModuleName) @@ -900,6 +906,11 @@ def fix_provides(provides) provides = provides.gsub("_", "-") end + if provides.include?("(") and !provides.include?("(=") + logger.warn("Blanking 'provides' field '#{provides}' because it's invalid") + provides = "" + end + if m = provides.match(/^([A-Za-z0-9_-]+)\s*=\s*(\d+.*$)/) logger.warn("Replacing 'provides' entry #{provides} with syntax 'name (= version)'") provides = "#{m[1]} (= #{m[2]})" From ae8205261a2b71161981eeb7cf9f56a9b8523bd2 Mon Sep 17 00:00:00 2001 From: Jordan Stopford Date: Tue, 23 Apr 2024 16:01:42 +0100 Subject: [PATCH 04/12] Fix for #1627 When packaging a deb sometimes a path to a binary comes into the requires field i.e. /bin/sh but this isn't valid for control files. Strip this out --- lib/fpm/package/deb.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/fpm/package/deb.rb b/lib/fpm/package/deb.rb index cfa6c12d4f..e7c8f7c54e 100644 --- a/lib/fpm/package/deb.rb +++ b/lib/fpm/package/deb.rb @@ -911,6 +911,11 @@ def fix_provides(provides) provides = "" end + if provides.start_with?("/") + logger.warn("Blanking 'provides' field '#{provides}' because it's invalid") + provides = "" + end + if m = provides.match(/^([A-Za-z0-9_-]+)\s*=\s*(\d+.*$)/) logger.warn("Replacing 'provides' entry #{provides} with syntax 'name (= version)'") provides = "#{m[1]} (= #{m[2]})" From 20aa78c72c24d1cde7e8c9ed6392b67668585e44 Mon Sep 17 00:00:00 2001 From: Jordan Stopford Date: Tue, 23 Apr 2024 16:33:39 +0100 Subject: [PATCH 05/12] Fix for #1627 When packaging a deb sometimes a path to a binary comes into the requires field i.e. /bin/sh but this isn't valid for control files. Strip this out --- lib/fpm/package/deb.rb | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/fpm/package/deb.rb b/lib/fpm/package/deb.rb index e7c8f7c54e..3e5eb4b5a9 100644 --- a/lib/fpm/package/deb.rb +++ b/lib/fpm/package/deb.rb @@ -707,6 +707,11 @@ def converted_from(origin) self.dependencies = self.dependencies.collect do |dep| fix_dependency(dep) end.flatten + + # If an invalid depends field was found i.e. /bin.sh then fix_depends will blank it + # Make sure we remove this blank here + self.dependencies = self.dependencies.reject { |p| p.empty? } + self.provides = self.provides.collect do |provides| fix_provides(provides) end.flatten @@ -813,6 +818,12 @@ def fix_dependency(dep) end end + if dep.start_with?("/") + logger.warn("Blanking 'dependency' field '#{dep}' because it's invalid") + dep = "" + return dep + end + name_re = /^[^ \(]+/ name = dep[name_re] if name =~ /[A-Z]/ @@ -911,11 +922,6 @@ def fix_provides(provides) provides = "" end - if provides.start_with?("/") - logger.warn("Blanking 'provides' field '#{provides}' because it's invalid") - provides = "" - end - if m = provides.match(/^([A-Za-z0-9_-]+)\s*=\s*(\d+.*$)/) logger.warn("Replacing 'provides' entry #{provides} with syntax 'name (= version)'") provides = "#{m[1]} (= #{m[2]})" From eb56972540bd25674fe68b1638d06e933930cce6 Mon Sep 17 00:00:00 2001 From: Jordan Stopford Date: Tue, 23 Apr 2024 17:30:48 +0100 Subject: [PATCH 06/12] Fix for #1627 Strip rpmlib dependencies --- lib/fpm/package/deb.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/fpm/package/deb.rb b/lib/fpm/package/deb.rb index 3e5eb4b5a9..57bb6984cd 100644 --- a/lib/fpm/package/deb.rb +++ b/lib/fpm/package/deb.rb @@ -824,6 +824,12 @@ def fix_dependency(dep) return dep end + if dep.include?("rpmlib") + logger.warn("Blanking 'dependency' field '#{dep}' because it's invalid") + dep = "" + return dep + end + name_re = /^[^ \(]+/ name = dep[name_re] if name =~ /[A-Z]/ From 5960c8c4a7ec3758792416dea0ec3bae47fb2f35 Mon Sep 17 00:00:00 2001 From: Kristof Willaert Date: Thu, 16 May 2024 18:13:54 +0200 Subject: [PATCH 07/12] Escape the pylib path when using it in a shell command --- lib/fpm/package/python.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/fpm/package/python.rb b/lib/fpm/package/python.rb index 5754a053d4..04533ddcaf 100644 --- a/lib/fpm/package/python.rb +++ b/lib/fpm/package/python.rb @@ -79,7 +79,7 @@ class FPM::Package::Python < FPM::Package option "--setup-py-arguments", "setup_py_argument", "Arbitrary argument(s) to be passed to setup.py", :multivalued => true, :attribute_name => :python_setup_py_arguments, - :default => [] + :default => [] option "--internal-pip", :flag, "Use the pip module within python to install modules - aka 'python -m pip'. This is the recommended usage since Python 3.4 (2014) instead of invoking the 'pip' script", :attribute_name => :python_internal_pip, @@ -230,7 +230,7 @@ def load_package_info(setup_py) output = ::Dir.chdir(setup_dir) do tmp = build_path("metadata.json") - setup_cmd = "env PYTHONPATH=#{pylib}:$PYTHONPATH #{attributes[:python_bin]} " \ + setup_cmd = "env PYTHONPATH=#{pylib.shellescape}:$PYTHONPATH #{attributes[:python_bin]} " \ "setup.py --command-packages=pyfpm get_metadata --output=#{tmp}" if attributes[:python_obey_requirements_txt?] From 5092c5f659e856f017bf1b7204db1cee8ec2ea72 Mon Sep 17 00:00:00 2001 From: Reinier Schoof Date: Wed, 26 Jun 2024 15:54:02 +0200 Subject: [PATCH 08/12] Add option --deb-systemd-path This option will allow you to override the path within the staging directory systemd service files are moved to in debian packages. On some systems systemd should not go in the hardcoded /lib/systemd/system but in /usr/lib/systemd/system instead. Many systems have the both symlinked, but not all so assuming /lib/systemd/system can be dangerous. Signed-off-by: Reinier Schoof --- lib/fpm/package/deb.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/fpm/package/deb.rb b/lib/fpm/package/deb.rb index 4d97dfb0cc..71cccd84ff 100644 --- a/lib/fpm/package/deb.rb +++ b/lib/fpm/package/deb.rb @@ -210,6 +210,11 @@ class FPM::Package::Deb < FPM::Package next File.expand_path(file) end + option "--systemd-path", "FILEPATH", "Relative path to the systemd service directory", + :default => "lib/systemd/system" do |file| + next file.gsub(/^\/*/, '') + end + option "--systemd-enable", :flag , "Enable service on install or upgrade", :default => false option "--systemd-auto-start", :flag , "Start service after install or upgrade", :default => false @@ -518,7 +523,7 @@ def output(output_path) attributes[:deb_systemd] = [] attributes.fetch(:deb_systemd_list, []).each do |systemd| name = File.basename(systemd, ".service") - dest_systemd = staging_path("lib/systemd/system/#{name}.service") + dest_systemd = staging_path(File.join(attributes[:deb_systemd_path], "#{name}.service")) mkdir_p(File.dirname(dest_systemd)) FileUtils.cp(systemd, dest_systemd) File.chmod(0644, dest_systemd) @@ -628,7 +633,7 @@ def output(output_path) attributes.fetch(:deb_systemd_list, []).each do |systemd| name = File.basename(systemd, ".service") - dest_systemd = staging_path("lib/systemd/system/#{name}.service") + dest_systemd = staging_path(File.join(attributes[:deb_systemd_path], "#{name}.service")) mkdir_p(File.dirname(dest_systemd)) FileUtils.cp(systemd, dest_systemd) File.chmod(0644, dest_systemd) From e7903804af11623c258ab95384c03fc7c41ebfc9 Mon Sep 17 00:00:00 2001 From: Kevin Duret Date: Fri, 6 Sep 2024 09:22:28 +0200 Subject: [PATCH 09/12] fix(rpm): replace perl dependency by perl-interpreter --- lib/fpm/package/rpm.rb | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/fpm/package/rpm.rb b/lib/fpm/package/rpm.rb index a925256561..5e79e5c3ac 100644 --- a/lib/fpm/package/rpm.rb +++ b/lib/fpm/package/rpm.rb @@ -277,7 +277,19 @@ def iteration # See FPM::Package#converted_from def converted_from(origin) - if origin == FPM::Package::Gem + if origin == FPM::Package::CPAN + fixed_deps = [] + self.dependencies.collect do |dep| + # RPM package "perl" is a metapackage which install all the Perl bits and core modules, then gcc... + # this must be replaced by perl-interpreter + if name=/^perl([\s<>=].*)$/.match(dep) + fixed_deps.push("perl-interpreter#{name[1]}") + else + fixed_deps.push(dep) + end + end + self.dependencies = fixed_deps + elsif origin == FPM::Package::Gem fixed_deps = [] self.dependencies.collect do |dep| # Gem dependency operator "~>" is not compatible with rpm. Translate any found. From 790f53c766ff9f8ac507fc7221afcabfbc07537d Mon Sep 17 00:00:00 2001 From: Matt Ezell Date: Mon, 23 Sep 2024 10:33:57 -0400 Subject: [PATCH 10/12] Support pip download returning a zip file Signed-off-by: Matt Ezell --- lib/fpm/package/python.rb | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/fpm/package/python.rb b/lib/fpm/package/python.rb index 5754a053d4..7e0dfac1d3 100644 --- a/lib/fpm/package/python.rb +++ b/lib/fpm/package/python.rb @@ -171,13 +171,19 @@ def download_if_necessary(package, version=nil) # behind a directory with the Python package extracted and ready to be used. # For example, `pip download ... Django` puts `Django-4.0.4.tar.tz` into the build_path directory. # If we expect `pip` to leave an unknown-named file in the `build_path` directory, let's check for - # a single file and unpack it. I don't know if it will /always/ be a .tar.gz though. - files = ::Dir.glob(File.join(build_path, "*.tar.gz")) + # a single file and unpack it. + files = ::Dir.glob(File.join(build_path, "*.{tar.gz,zip}")) if files.length != 1 raise "Unexpected directory layout after `pip download ...`. This might be an fpm bug? The directory is #{build_path}" end - safesystem("tar", "-zxf", files[0], "-C", target) + if files[0].end_with?("tar.gz") + safesystem("tar", "-zxf", files[0], "-C", target) + elsif files[0].end_with?("zip") + safesystem("unzip", files[0], "-d", target) + else + raise "Unexpected file format after `pip download ...`. This might be an fpm bug? The file is #{files[0]}" + end else # no pip, use easy_install logger.debug("no pip, defaulting to easy_install", :easy_install => attributes[:python_easyinstall]) From d30e641bb81cbde833c48bfb57fd60b2e4f9cc25 Mon Sep 17 00:00:00 2001 From: Jordan Sissel Date: Sat, 7 Dec 2024 23:00:04 -0800 Subject: [PATCH 11/12] Update changelog --- CHANGELOG.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e78787dad0..20179bfc21 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,18 @@ Release Notes and Change Log ============================ +Unreleased (...) +^^^^^^^^^^^^^^^^ + +This is a stub of changes since the last release. +* https://github.com/jordansissel/fpm/pull/2068 (`#2068`_; Matt Ezell, `#2074`_; hussainbani) +* https://github.com/jordansissel/fpm/pull/2009 (`#2009`_; Ștefan Rusu) +* https://github.com/jordansissel/fpm/pull/2082 (`#2082`_; Wayne Heaney) +* https://github.com/jordansissel/fpm/pull/2064 (`#2064`_; David Newhall II) +* https://github.com/jordansissel/fpm/pull/2067 (`#2067`_; Jordan Sissel) +* https://github.com/jordansissel/fpm/pull/2065 (`#2065`_; phillipp) + + 1.15.1 (January 31, 2023) ^^^^^^^^^^^^^^^^^^^^^^^^^ * Ruby 3.2.0 now supported. This fixes error 'undefined method exists? for File' '(`#1981`_, `#1988`_; Nicholas Hubbard, romulasry) From 512f4a9eb3295106abc903ac4424e1323f766e51 Mon Sep 17 00:00:00 2001 From: Jordan Sissel Date: Sat, 7 Dec 2024 23:28:42 -0800 Subject: [PATCH 12/12] Fix syntax I broke when doing a merge conflict --- lib/fpm/package/deb.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/fpm/package/deb.rb b/lib/fpm/package/deb.rb index c61094730a..c996bfcb10 100644 --- a/lib/fpm/package/deb.rb +++ b/lib/fpm/package/deb.rb @@ -696,7 +696,7 @@ def output(output_path) datatar = build_path("data.tar.zst") controltar = build_path("control.tar.zst") compression_flags = ["-I zstd"] - compressor_options = {"ZSTD_CLEVEL", "-#{self.attributes[:deb_compression_level] || 3}"} + compressor_options = {"ZSTD_CLEVEL" => "-#{self.attributes[:deb_compression_level] || 3}"} when "none" datatar = build_path("data.tar") controltar = build_path("control.tar") @@ -991,7 +991,7 @@ def write_control_tarball when "zst" controltar = "control.tar.zst" compression_flags = ["-I zstd"] - compressor_options = {"ZSTD_CLEVEL", "-#{self.attributes[:deb_compression_level] || 3}"} + compressor_options = {"ZSTD_CLEVEL" => "-#{self.attributes[:deb_compression_level] || 3}"} when "none" controltar = "control.tar" compression_flags = []