spec/unit/packagers/rpm_spec.rb (439 lines of code) (raw):
require "spec_helper"
module Omnibus
describe Packager::RPM do
let(:project) do
Project.new.tap do |project|
project.name("project")
project.homepage("https://example.com")
project.install_dir("/opt/project")
project.build_version("1.2.3")
project.build_iteration("2")
project.maintainer("Chef Software")
project.replace("old-project")
project.license(project_license) if project_license
end
end
subject { described_class.new(project) }
let(:project_license) { nil }
let(:project_root) { File.join(tmp_path, "project/root") }
let(:package_dir) { File.join(tmp_path, "package/dir") }
let(:staging_dir) { File.join(tmp_path, "staging/dir") }
let(:architecture) { "x86_64" }
before do
Config.project_root(project_root)
Config.package_dir(package_dir)
allow(subject).to receive(:staging_dir).and_return(staging_dir)
create_directory(staging_dir)
create_directory("#{staging_dir}/BUILD")
create_directory("#{staging_dir}/RPMS")
create_directory("#{staging_dir}/SRPMS")
create_directory("#{staging_dir}/SOURCES")
create_directory("#{staging_dir}/SPECS")
stub_ohai(platform: "redhat", version: "6") do |data|
data["kernel"]["machine"] = architecture
end
end
describe "#signing_passphrase" do
it "is a DSL method" do
expect(subject).to have_exposed_method(:signing_passphrase)
end
it "has a no default value" do
expect(subject.signing_passphrase).to be(nil)
end
end
describe "#vendor" do
it "is a DSL method" do
expect(subject).to have_exposed_method(:vendor)
end
it "has a default value" do
expect(subject.vendor).to eq("Omnibus <omnibus@getchef.com>")
end
it "must be a string" do
expect { subject.vendor(Object.new) }.to raise_error(InvalidValue)
end
end
describe "#license" do
it "is a DSL method" do
expect(subject).to have_exposed_method(:license)
end
it "has a default value" do
expect(subject.license).to eq("Unspecified")
end
it "must be a string" do
expect { subject.license(Object.new) }.to raise_error(InvalidValue)
end
context "with project license" do
let(:project_license) { "custom-license" }
it "uses project license" do
expect(subject.license).to eq("custom-license")
end
end
end
describe "#priority" do
it "is a DSL method" do
expect(subject).to have_exposed_method(:priority)
end
it "has a default value" do
expect(subject.priority).to eq("extra")
end
it "must be a string" do
expect { subject.priority(Object.new) }.to raise_error(InvalidValue)
end
end
describe "#category" do
it "is a DSL method" do
expect(subject).to have_exposed_method(:category)
end
it "has a default value" do
expect(subject.category).to eq("default")
end
it "must be a string" do
expect { subject.category(Object.new) }.to raise_error(InvalidValue)
end
end
describe "#dist_tag" do
it "is a DSL method" do
expect(subject).to have_exposed_method(:dist_tag)
end
it "has a default value" do
expect(subject.dist_tag).to eq(".el6")
end
end
describe "#compression_type" do
it "is a DSL method" do
expect(subject).to have_exposed_method(:compression_type)
end
it "has a default value" do
expect(subject.compression_type).to eq(:gzip)
end
it "must be a symbol" do
expect { subject.compression_type(Object.new) }.to raise_error(InvalidValue)
end
end
describe "#compression_level" do
it "is a DSL method" do
expect(subject).to have_exposed_method(:compression_level)
end
it "has a default value" do
expect(subject.compression_level).to eq(9)
end
it "must be an integer" do
expect { subject.compression_level(Object.new) }.to raise_error(InvalidValue)
end
end
describe "#id" do
it "is :rpm" do
expect(subject.id).to eq(:rpm)
end
end
describe "#package_name" do
context "when dist_tag is enabled" do
before do
allow(subject).to receive(:safe_architecture).and_return("x86_64")
end
it "includes the name, version, and build iteration" do
expect(subject.package_name).to eq("project-1.2.3-2.el6.x86_64.rpm")
end
end
context "when dist_tag is disabled" do
before do
allow(subject).to receive(:dist_tag).and_return(false)
end
it "excludes dist tag" do
expect(subject.package_name).to eq("project-1.2.3-2.x86_64.rpm")
end
end
end
describe "#build_dir" do
it "is nested inside the staging_dir" do
expect(subject.build_dir).to eq("#{staging_dir}/BUILD")
end
end
describe "#write_rpm_spec" do
before do
allow(subject).to receive(:safe_architecture).and_return("x86_64")
end
let(:spec_file) { "#{staging_dir}/SPECS/project-1.2.3-2.el6.x86_64.rpm.spec" }
it "generates the file" do
subject.write_rpm_spec
expect(spec_file).to be_a_file
end
it "has the correct content" do
subject.write_rpm_spec
contents = File.read(spec_file)
expect(contents).to include("Name: project")
expect(contents).to include("Version: 1.2.3")
expect(contents).to include("Release: 2.el6")
expect(contents).to include("Summary: The full stack of project")
expect(contents).to include("AutoReqProv: no")
expect(contents).to include("BuildRoot: %buildroot")
expect(contents).to include("Prefix: /")
expect(contents).to include("Group: default")
expect(contents).to include("License: Unspecified")
expect(contents).to include("Vendor: Omnibus <omnibus@getchef.com>")
expect(contents).to include("URL: https://example.com")
expect(contents).to include("Packager: Chef Software")
expect(contents).to include("Obsoletes: old-project")
expect(contents).to include("_binary_payload w9T.gzdio")
end
context "when RPM compression type xz is configured" do
before do
subject.compression_type(:xz)
end
it "has the correct binary_payload line" do
subject.write_rpm_spec
contents = File.read(spec_file)
expect(contents).to include("_binary_payload w9T.xzdio")
end
context "when RPM compression level is also configured" do
before do
subject.compression_level(6)
end
it "has the correct binary_payload line" do
subject.write_rpm_spec
contents = File.read(spec_file)
expect(contents).to include("_binary_payload w6T.xzdio")
end
end
end
context "when RPM compression type bzip2 is configured" do
before do
subject.compression_type(:bzip2)
end
it "has the correct binary_payload line" do
subject.write_rpm_spec
contents = File.read(spec_file)
expect(contents).to include("_binary_payload w9T.bzdio")
end
context "when RPM compression level is also configured" do
before do
subject.compression_level(6)
end
it "has the correct binary_payload line" do
subject.write_rpm_spec
contents = File.read(spec_file)
expect(contents).to include("_binary_payload w6T.bzdio")
end
end
end
context "when scripts are given" do
let(:scripts) { %w{ pre post preun postun verifyscript pretrans posttrans } }
before do
scripts.each do |script_name|
create_file("#{project_root}/package-scripts/project/#{script_name}") do
"Contents of #{script_name}"
end
end
end
it "writes the scripts into the spec" do
subject.write_rpm_spec
contents = File.read(spec_file)
scripts.each do |script_name|
expect(contents).to include("%#{script_name}")
expect(contents).to include("Contents of #{script_name}")
end
end
end
context "when scripts with default omnibus naming are given" do
let(:default_scripts) { %w{ preinst postinst prerm postrm } }
before do
default_scripts.each do |default_name|
create_file("#{project_root}/package-scripts/project/#{default_name}") do
"Contents of #{default_name}"
end
end
end
it "writes the scripts into the spec" do
subject.write_rpm_spec
contents = File.read(spec_file)
default_scripts.each do |script_name|
mapped_name = Packager::RPM::SCRIPT_MAP[script_name.to_sym]
expect(contents).to include("%#{mapped_name}")
expect(contents).to include("Contents of #{script_name}")
end
end
end
context "when files and directories are present" do
before do
create_file("#{staging_dir}/BUILD/.file1")
create_file("#{staging_dir}/BUILD/file2")
create_directory("#{staging_dir}/BUILD/.dir1")
create_directory("#{staging_dir}/BUILD/dir2")
create_directory("#{staging_dir}/BUILD/dir3 space")
end
it "writes them into the spec" do
subject.write_rpm_spec
contents = File.read(spec_file)
expect(contents).to include("%dir /.dir1")
expect(contents).to include("/.file1")
expect(contents).to include("%dir /dir2")
expect(contents).to include("/file2")
expect(contents).to include("%dir \"/dir3 space\"")
end
end
context "when leaf directories owned by the filesystem package are present" do
before do
create_directory("#{staging_dir}/BUILD/usr/lib")
create_directory("#{staging_dir}/BUILD/opt")
create_file("#{staging_dir}/BUILD/opt/thing")
end
it "is written into the spec with ownership and permissions" do
subject.write_rpm_spec
contents = File.read(spec_file)
expect(contents).to include("%dir %attr(0755,root,root) /opt")
expect(contents).to include("%dir %attr(0555,root,root) /usr/lib")
end
end
context "when dist_tag is disabled" do
let(:spec_file) { "#{staging_dir}/SPECS/project-1.2.3-2.x86_64.rpm.spec" }
before do
allow(subject).to receive(:dist_tag).and_return(false)
end
it "has the correct release" do
subject.write_rpm_spec
contents = File.read(spec_file)
expect(contents).to include("Release: 2")
end
end
end
describe "#create_rpm_file" do
before do
allow(subject).to receive(:shellout!)
allow(Dir).to receive(:chdir) { |_, &b| b.call }
end
it "logs a message" do
output = capture_logging { subject.create_rpm_file }
expect(output).to include("Creating .rpm file")
end
it "uses the correct command" do
expect(subject).to receive(:shellout!)
.with(/rpmbuild --target #{architecture} -bb --buildroot/)
subject.create_rpm_file
end
context "when RPM signing is enabled" do
before do
subject.signing_passphrase("foobar")
allow(Dir).to receive(:mktmpdir).and_return(tmp_path)
end
it "signs the rpm" do
expect(subject).to receive(:shellout!)
.with(/sign\-rpm/, kind_of(Hash))
subject.create_rpm_file
end
end
end
describe "#spec_file" do
before do
allow(subject).to receive(:package_name).and_return("package_name")
end
it "includes the package_name" do
expect(subject.spec_file).to eq("#{staging_dir}/SPECS/package_name.spec")
end
end
describe "#rpm_file" do
before do
allow(subject).to receive(:package_name).and_return("package_name.rpm")
end
it "includes the package_name rpm" do
expect(subject.rpm_file).to eq("#{staging_dir}/RPMS/#{architecture}/package_name.rpm")
end
end
describe "#rpm_safe" do
it "adds quotes when required" do
expect(subject.rpm_safe("file path")).to eq('"file path"')
end
it "escapes [" do
expect(subject.rpm_safe("[foo")).to eq('[\\[]foo')
end
it "escapes *" do
expect(subject.rpm_safe("*foo")).to eq("[*]foo")
end
it "escapes ?" do
expect(subject.rpm_safe("?foo")).to eq("[?]foo")
end
it "escapes %" do
expect(subject.rpm_safe("%foo")).to eq("[%]foo")
end
end
describe "#safe_base_package_name" do
context 'when the project name is "safe"' do
it "returns the value without logging a message" do
expect(subject.safe_base_package_name).to eq("project")
expect(subject).to_not receive(:log)
end
end
context "when the project name has invalid characters" do
before { project.name("Pro$ject123.for-realz_2") }
it "returns the value while logging a message" do
output = capture_logging do
expect(subject.safe_base_package_name).to eq("pro-ject123.for-realz-2")
end
expect(output).to include("The `name' component of RPM package names can only include")
end
end
end
describe "#safe_build_iteration" do
it "returns the build iternation" do
expect(subject.safe_build_iteration).to eq(project.build_iteration)
end
end
describe "#safe_version" do
context 'when the project build_version is "safe"' do
it "returns the value without logging a message" do
expect(subject.safe_version).to eq("1.2.3")
expect(subject).to_not receive(:log)
end
end
context "when the project build_version has dashes" do
before { project.build_version("1.2-rc.1") }
it "returns the value while logging a message" do
output = capture_logging do
expect(subject.safe_version).to eq("1.2~rc.1")
end
expect(output).to include("Tildes hold special significance in the RPM package versions.")
end
end
context "when the project build_version has invalid characters" do
before { project.build_version("1.2-pre_alpha.##__2") }
it "returns the value while logging a message" do
output = capture_logging do
expect(subject.safe_version).to eq("1.2~pre_alpha._2")
end
expect(output).to include("The `version' component of RPM package names can only include")
end
end
end
describe "#safe_architecture" do
before do
stub_ohai(platform: "redhat", version: "6") do |data|
data["kernel"]["machine"] = "i386"
end
end
it "returns the value from Ohai" do
expect(subject.safe_architecture).to eq("i386")
end
context "when i686" do
before do
stub_ohai(platform: "redhat", version: "6") do |data|
data["kernel"]["machine"] = "i686"
end
end
it "returns i386" do
expect(subject.safe_architecture).to eq("i386")
end
end
context "on Pidora" do
before do
# There's no Pidora in Fauxhai :(
stub_ohai(platform: "fedora", version: "32") do |data|
data["platform"] = "pidora"
data["platform_version"] = "32"
data["kernel"]["machine"] = "armv6l"
end
end
it "returns armv6hl" do
expect(subject.safe_architecture).to eq("armv6hl")
end
end
end
end
end