1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/spec/bundler/bundler/gem_helper_spec.rb
2021-04-15 15:36:15 +09:00

435 lines
14 KiB
Ruby

# frozen_string_literal: true
require "rake"
require "bundler/gem_helper"
RSpec.describe Bundler::GemHelper do
let(:app_name) { "lorem__ipsum" }
let(:app_path) { bundled_app app_name }
let(:app_gemspec_path) { app_path.join("#{app_name}.gemspec") }
before(:each) do
global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__COC" => "false", "BUNDLE_GEM__LINTER" => "false",
"BUNDLE_GEM__CI" => "false", "BUNDLE_GEM__CHANGELOG" => "false"
bundle "gem #{app_name}"
prepare_gemspec(app_gemspec_path)
end
context "determining gemspec" do
subject { Bundler::GemHelper.new(app_path) }
context "fails" do
it "when there is no gemspec" do
FileUtils.rm app_gemspec_path
expect { subject }.to raise_error(/Unable to determine name/)
end
it "when there are two gemspecs and the name isn't specified" do
FileUtils.touch app_path.join("#{app_name}-2.gemspec")
expect { subject }.to raise_error(/Unable to determine name/)
end
end
context "interpolates the name" do
it "when there is only one gemspec" do
expect(subject.gemspec.name).to eq(app_name)
end
it "for a hidden gemspec" do
FileUtils.mv app_gemspec_path, app_path.join(".gemspec")
expect(subject.gemspec.name).to eq(app_name)
end
end
it "handles namespaces and converts them to CamelCase" do
bundle "gem #{app_name}-foo_bar"
underscore_path = bundled_app "#{app_name}-foo_bar"
lib = underscore_path.join("lib/#{app_name}/foo_bar.rb").read
expect(lib).to include("module LoremIpsum")
expect(lib).to include("module FooBar")
end
end
context "gem management" do
def mock_confirm_message(message)
expect(Bundler.ui).to receive(:confirm).with(message)
end
def mock_build_message(name, version)
message = "#{name} #{version} built to pkg/#{name}-#{version}.gem."
mock_confirm_message message
end
def mock_checksum_message(name, version)
message = "#{name} #{version} checksum written to checksums/#{name}-#{version}.gem.sha512."
mock_confirm_message message
end
subject! { Bundler::GemHelper.new(app_path) }
let(:app_version) { "0.1.0" }
let(:app_gem_dir) { app_path.join("pkg") }
let(:app_gem_path) { app_gem_dir.join("#{app_name}-#{app_version}.gem") }
let(:app_sha_path) { app_path.join("checksums", "#{app_name}-#{app_version}.gem.sha512") }
let(:app_gemspec_content) { File.read(app_gemspec_path) }
before(:each) do
content = app_gemspec_content.gsub("TODO: ", "")
content.sub!(/homepage\s+= ".*"/, 'homepage = ""')
content.gsub!(/spec\.metadata.+\n/, "")
File.open(app_gemspec_path, "w") {|file| file << content }
end
it "uses a shell UI for output" do
expect(Bundler.ui).to be_a(Bundler::UI::Shell)
end
describe "#install" do
let!(:rake_application) { Rake.application }
before(:each) do
Rake.application = Rake::Application.new
end
after(:each) do
Rake.application = rake_application
end
context "defines Rake tasks" do
let(:task_names) do
%w[build install release release:guard_clean
release:source_control_push release:rubygem_push]
end
context "before installation" do
it "raises an error with appropriate message" do
task_names.each do |name|
skip "Rake::FileTask '#{name}' exists" if File.exist?(name)
expect { Rake.application[name] }.
to raise_error(/^Don't know how to build task '#{name}'/)
end
end
end
context "after installation" do
before do
subject.install
end
it "adds Rake tasks successfully" do
task_names.each do |name|
expect { Rake.application[name] }.not_to raise_error
expect(Rake.application[name]).to be_instance_of Rake::Task
end
end
it "provides a way to access the gemspec object" do
expect(subject.gemspec.name).to eq(app_name)
end
end
end
end
describe "#build_gem" do
context "when build failed" do
it "raises an error with appropriate message" do
# break the gemspec by adding back the TODOs
File.open(app_gemspec_path, "w") {|file| file << app_gemspec_content }
expect { subject.build_gem }.to raise_error(/TODO/)
end
end
context "when build was successful" do
it "creates .gem file" do
mock_build_message app_name, app_version
subject.build_gem
expect(app_gem_path).to exist
end
end
context "when building in the current working directory" do
it "creates .gem file" do
mock_build_message app_name, app_version
Dir.chdir app_path do
Bundler::GemHelper.new.build_gem
end
expect(app_gem_path).to exist
end
end
context "when building in a location relative to the current working directory" do
it "creates .gem file" do
mock_build_message app_name, app_version
Dir.chdir File.dirname(app_path) do
Bundler::GemHelper.new(File.basename(app_path)).build_gem
end
expect(app_gem_path).to exist
end
end
end
describe "#build_checksum" do
context "when build was successful" do
it "creates .sha512 file" do
mock_build_message app_name, app_version
mock_checksum_message app_name, app_version
subject.build_checksum
expect(app_sha_path).to exist
end
end
context "when building in the current working directory" do
it "creates a .sha512 file" do
mock_build_message app_name, app_version
mock_checksum_message app_name, app_version
Dir.chdir app_path do
Bundler::GemHelper.new.build_checksum
end
expect(app_sha_path).to exist
end
end
context "when building in a location relative to the current working directory" do
it "creates a .sha512 file" do
mock_build_message app_name, app_version
mock_checksum_message app_name, app_version
Dir.chdir File.dirname(app_path) do
Bundler::GemHelper.new(File.basename(app_path)).build_checksum
end
expect(app_sha_path).to exist
end
end
end
describe "#install_gem" do
context "when installation was successful" do
it "gem is installed" do
mock_build_message app_name, app_version
mock_confirm_message "#{app_name} (#{app_version}) installed."
subject.install_gem(nil, :local)
expect(app_gem_path).to exist
gem_command :list
expect(out).to include("#{app_name} (#{app_version})")
end
end
context "when installation fails" do
it "raises an error with appropriate message" do
# create empty gem file in order to simulate install failure
allow(subject).to receive(:build_gem) do
FileUtils.mkdir_p(app_gem_dir)
FileUtils.touch app_gem_path
app_gem_path
end
expect { subject.install_gem }.to raise_error(/Couldn't install gem/)
end
end
end
describe "rake release" do
let!(:rake_application) { Rake.application }
before(:each) do
Rake.application = Rake::Application.new
subject.install
end
after(:each) do
Rake.application = rake_application
end
before do
sys_exec("git init", :dir => app_path)
sys_exec("git config user.email \"you@example.com\"", :dir => app_path)
sys_exec("git config user.name \"name\"", :dir => app_path)
sys_exec("git config commit.gpgsign false", :dir => app_path)
sys_exec("git config push.default simple", :dir => app_path)
# silence messages
allow(Bundler.ui).to receive(:confirm)
allow(Bundler.ui).to receive(:error)
end
context "fails" do
it "when there are unstaged files" do
expect { Rake.application["release"].invoke }.
to raise_error("There are files that need to be committed first.")
end
it "when there are uncommitted files" do
sys_exec("git add .", :dir => app_path)
expect { Rake.application["release"].invoke }.
to raise_error("There are files that need to be committed first.")
end
it "when there is no git remote" do
sys_exec("git commit -a -m \"initial commit\"", :dir => app_path)
expect { Rake.application["release"].invoke }.to raise_error(RuntimeError)
end
end
context "succeeds" do
let(:repo) { build_git("foo", :bare => true) }
before do
sys_exec("git remote add origin #{file_uri_for(repo.path)}", :dir => app_path)
sys_exec('git commit -a -m "initial commit"', :dir => app_path)
end
context "on releasing" do
before do
mock_build_message app_name, app_version
mock_confirm_message "Tagged v#{app_version}."
mock_confirm_message "Pushed git commits and release tag."
sys_exec("git push -u origin master", :dir => app_path)
end
it "calls rubygem_push with proper arguments" do
expect(subject).to receive(:rubygem_push).with(app_gem_path.to_s)
Rake.application["release"].invoke
end
it "uses Kernel.system" do
cmd = gem_bin.shellsplit
expect(Kernel).to receive(:system).with(*cmd, "push", app_gem_path.to_s, "--host", "http://example.org").and_return(true)
Rake.application["release"].invoke
end
it "also works when releasing from an ambiguous reference" do
# Create a branch with the same name as the tag
sys_exec("git checkout -b v#{app_version}", :dir => app_path)
sys_exec("git push -u origin v#{app_version}", :dir => app_path)
expect(subject).to receive(:rubygem_push).with(app_gem_path.to_s)
Rake.application["release"].invoke
end
it "also works with releasing from a branch not yet pushed" do
sys_exec("git checkout -b module_function", :dir => app_path)
expect(subject).to receive(:rubygem_push).with(app_gem_path.to_s)
Rake.application["release"].invoke
end
end
context "on releasing with a custom tag prefix" do
before do
Bundler::GemHelper.tag_prefix = "foo-"
mock_build_message app_name, app_version
mock_confirm_message "Pushed git commits and release tag."
sys_exec("git push -u origin master", :dir => app_path)
expect(subject).to receive(:rubygem_push).with(app_gem_path.to_s)
end
it "prepends the custom prefix to the tag" do
mock_confirm_message "Tagged foo-v#{app_version}."
Rake.application["release"].invoke
end
end
it "even if tag already exists" do
mock_build_message app_name, app_version
mock_confirm_message "Tag v#{app_version} has already been created."
expect(subject).to receive(:rubygem_push).with(app_gem_path.to_s)
sys_exec("git tag -a -m \"Version #{app_version}\" v#{app_version}", :dir => app_path)
Rake.application["release"].invoke
end
end
end
describe "release:rubygem_push" do
let!(:rake_application) { Rake.application }
before(:each) do
Rake.application = Rake::Application.new
subject.install
allow(subject).to receive(:sh_with_input)
end
after(:each) do
Rake.application = rake_application
end
before do
sys_exec("git init", :dir => app_path)
sys_exec("git config user.email \"you@example.com\"", :dir => app_path)
sys_exec("git config user.name \"name\"", :dir => app_path)
sys_exec("git config push.gpgsign simple", :dir => app_path)
# silence messages
allow(Bundler.ui).to receive(:confirm)
allow(Bundler.ui).to receive(:error)
credentials = double("credentials", "file?" => true)
allow(Bundler.user_home).to receive(:join).
with(".gem/credentials").and_return(credentials)
end
describe "success messaging" do
context "No allowed_push_host set" do
before do
allow(subject).to receive(:allowed_push_host).and_return(nil)
end
around do |example|
orig_host = ENV["RUBYGEMS_HOST"]
ENV["RUBYGEMS_HOST"] = rubygems_host_env
example.run
ENV["RUBYGEMS_HOST"] = orig_host
end
context "RUBYGEMS_HOST env var is set" do
let(:rubygems_host_env) { "https://custom.env.gemhost.com" }
it "should report successful push to the host from the environment" do
mock_confirm_message "Pushed #{app_name} #{app_version} to #{rubygems_host_env}"
Rake.application["release:rubygem_push"].invoke
end
end
context "RUBYGEMS_HOST env var is not set" do
let(:rubygems_host_env) { nil }
it "should report successful push to rubygems.org" do
mock_confirm_message "Pushed #{app_name} #{app_version} to rubygems.org"
Rake.application["release:rubygem_push"].invoke
end
end
context "RUBYGEMS_HOST env var is an empty string" do
let(:rubygems_host_env) { "" }
it "should report successful push to rubygems.org" do
mock_confirm_message "Pushed #{app_name} #{app_version} to rubygems.org"
Rake.application["release:rubygem_push"].invoke
end
end
end
context "allowed_push_host set in gemspec" do
before do
allow(subject).to receive(:allowed_push_host).and_return("https://my.gemhost.com")
end
it "should report successful push to the allowed gem host" do
mock_confirm_message "Pushed #{app_name} #{app_version} to https://my.gemhost.com"
Rake.application["release:rubygem_push"].invoke
end
end
end
end
end
end