require "spec_helper"
module Omnibus
describe Packager::PKG do
let(:project) do
Project.new.tap do |project|
project.name("project-full-name")
project.homepage("https://example.com")
project.install_dir("/opt/project-full-name")
project.build_version("1.2.3")
project.build_iteration("2")
project.maintainer("Chef Software")
end
end
subject { described_class.new(project) }
let(:project_root) { File.join(tmp_path, "project-full-name/root") }
let(:package_dir) { File.join(tmp_path, "package/dir") }
let(:staging_dir) { File.join(tmp_path, "staging/dir") }
before do
subject.identifier("com.getchef.project-full-name")
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}/Scripts")
end
describe "DSL" do
it "exposes :identifier" do
expect(subject).to have_exposed_method(:identifier)
end
it "exposes :signing_identity" do
expect(subject).to have_exposed_method(:signing_identity)
end
end
describe "#id" do
it "is :pkg" do
expect(subject.id).to eq(:pkg)
end
end
describe "#package_name" do
it "includes the name, version, and build iteration" do
expect(subject.package_name).to eq("project-full-name-1.2.3-2.pkg")
end
end
describe "#resources_dir" do
it "is nested inside the staging_dir" do
expect(subject.resources_dir).to eq("#{staging_dir}/Resources")
end
end
describe "#scripts_dir" do
it "is nested inside the staging_dir" do
expect(subject.scripts_dir).to eq("#{staging_dir}/Scripts")
end
end
describe "#write_scripts" do
context "when scripts are given" do
let(:scripts) { %w{ preinstall postinstall } }
before do
scripts.each do |script_name|
create_file("#{project_root}/package-scripts/project-full-name/#{script_name}") do
"Contents of #{script_name}"
end
end
end
it "writes the scripts into scripts staging dir" do
subject.write_scripts
scripts.each do |script_name|
script_file = "#{staging_dir}/Scripts/#{script_name}"
contents = File.read(script_file)
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 } }
before do
default_scripts.each do |script_name|
create_file("#{project_root}/package-scripts/project-full-name/#{script_name}") do
"Contents of #{script_name}"
end
end
end
it "writes the scripts into scripts staging dir" do
subject.write_scripts
default_scripts.each do |script_name|
mapped_name = Packager::PKG::SCRIPT_MAP[script_name.to_sym]
script_file = "#{staging_dir}/Scripts/#{mapped_name}"
contents = File.read(script_file)
expect(contents).to include("Contents of #{script_name}")
end
end
end
end
describe "#build_component_pkg" do
it "executes the pkgbuild command" do
expect(subject).to receive(:shellout!).with <<-EOH.gsub(/^ {10}/, "")
pkgbuild \\
--identifier "com.getchef.project-full-name" \\
--version "1.2.3" \\
--scripts "#{staging_dir}/Scripts" \\
--root "/opt/project-full-name" \\
--install-location "/opt/project-full-name" \\
"project-full-name-core.pkg"
EOH
subject.build_component_pkg
end
end
describe "#write_distribution_file" do
it "generates the file" do
subject.write_distribution_file
expect("#{staging_dir}/Distribution").to be_a_file
end
it "has the correct content" do
subject.write_distribution_file
contents = File.read("#{staging_dir}/Distribution")
expect(contents).to include('')
expect(contents).to include('')
expect(contents).to include("project-full-name-core.pkg")
end
end
describe "#build_product_pkg" do
context "when pkg signing is disabled" do
it "generates the distribution and runs productbuild" do
expect(subject).to receive(:shellout!).with <<-EOH.gsub(/^ {12}/, "")
productbuild \\
--distribution "#{staging_dir}/Distribution" \\
--resources "#{staging_dir}/Resources" \\
"#{package_dir}/project-full-name-1.2.3-2.pkg"
EOH
subject.build_product_pkg
end
end
context "when pkg signing is enabled" do
before do
subject.signing_identity("My Special Identity")
end
it "includes the signing parameters in the product build command" do
expect(subject).to receive(:shellout!).with <<-EOH.gsub(/^ {12}/, "")
productbuild \\
--distribution "#{staging_dir}/Distribution" \\
--resources "#{staging_dir}/Resources" \\
--sign "My Special Identity" \\
"#{package_dir}/project-full-name-1.2.3-2.pkg"
EOH
subject.build_product_pkg
end
end
context "when the identifier isn't specified by the project" do
before do
subject.identifier(nil)
project.name("$Project#")
end
it "uses com.example.PROJECT_NAME as the identifier" do
expect(subject.safe_identifier).to eq("test.chefsoftware.pkg.project")
end
end
end
describe "#component_pkg" do
it "returns the project name with -core.pkg" do
expect(subject.component_pkg).to eq("project-full-name-core.pkg")
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-full-name")
expect(subject).to_not receive(:log)
end
end
context "when the project name has invalid characters" do
before { project.name("$Project123.for-realz_2") }
it "returns the value while logging a message" do
output = capture_logging do
expect(subject.safe_base_package_name).to eq("project123forrealz2")
end
expect(output).to include("The `name' component of Mac package names can only include")
end
end
end
describe "#safe_identifier" do
context "when Project#identifier is given" do
before { subject.identifier("com.apple.project") }
it "is used" do
expect(subject.safe_identifier).to eq("com.apple.project")
end
end
context "when no value in project is given" do
before { subject.identifier(nil) }
it "is interpreted" do
expect(subject.safe_identifier).to eq("test.chefsoftware.pkg.project-full-name")
end
end
context 'when interpolated values are "unsafe"' do
before do
project.name("$Project123.for-realz_2")
project.maintainer("This is SPARTA!")
subject.identifier(nil)
end
it 'uses the "safe" values' do
expect(subject.safe_identifier).to eq("test.thisissparta.pkg.project123forrealz2")
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 invalid characters" do
before { project.build_version("1.2$alpha.##__2") }
it "returns the value while logging a message" do
output = capture_logging do
expect(subject.safe_version).to eq("1.2-alpha.-2")
end
expect(output).to include("The `version' component of Mac package names can only include")
end
end
end
end
end