diff options
Diffstat (limited to 'lib/bundler/cli/gem.rb')
-rw-r--r-- | lib/bundler/cli/gem.rb | 194 |
1 files changed, 153 insertions, 41 deletions
diff --git a/lib/bundler/cli/gem.rb b/lib/bundler/cli/gem.rb index 5b3d9c332e..b6571d0e86 100644 --- a/lib/bundler/cli/gem.rb +++ b/lib/bundler/cli/gem.rb @@ -11,11 +11,11 @@ module Bundler class CLI::Gem TEST_FRAMEWORK_VERSIONS = { "rspec" => "3.0", - "minitest" => "5.0", + "minitest" => "5.16", "test-unit" => "3.0", }.freeze - attr_reader :options, :gem_name, :thor, :name, :target + attr_reader :options, :gem_name, :thor, :name, :target, :extension def initialize(options, gem_name, thor) @options = options @@ -28,7 +28,10 @@ module Bundler @name = @gem_name @target = SharedHelpers.pwd.join(gem_name) - validate_ext_name if options[:ext] + @extension = options[:ext] + + validate_ext_name if @extension + validate_rust_builder_rubygems_version if @extension == "rust" end def run @@ -38,29 +41,40 @@ module Bundler namespaced_path = name.tr("-", "/") constant_name = name.gsub(/-[_-]*(?![_-]|$)/) { "::" }.gsub(/([_-]+|(::)|^)(.|$)/) { $2.to_s + $3.upcase } constant_array = constant_name.split("::") + minitest_constant_name = constant_array.clone.tap {|a| a[-1] = "Test#{a[-1]}" }.join("::") # Foo::Bar => Foo::TestBar use_git = Bundler.git_present? && options[:git] git_author_name = use_git ? `git config user.name`.chomp : "" - github_username = use_git ? `git config github.user`.chomp : "" + git_username = use_git ? `git config github.user`.chomp : "" git_user_email = use_git ? `git config user.email`.chomp : "" + github_username = if options[:github_username].nil? + git_username + elsif options[:github_username] == false + "" + else + options[:github_username] + end + config = { - :name => name, - :underscored_name => underscored_name, - :namespaced_path => namespaced_path, - :makefile_path => "#{underscored_name}/#{underscored_name}", - :constant_name => constant_name, - :constant_array => constant_array, - :author => git_author_name.empty? ? "TODO: Write your name" : git_author_name, - :email => git_user_email.empty? ? "TODO: Write your email address" : git_user_email, - :test => options[:test], - :ext => options[:ext], - :exe => options[:exe], - :bundler_version => bundler_dependency_version, - :git => use_git, - :github_username => github_username.empty? ? "[USERNAME]" : github_username, - :required_ruby_version => Gem.ruby_version < Gem::Version.new("2.4.a") ? "2.3.0" : "2.4.0", + name: name, + underscored_name: underscored_name, + namespaced_path: namespaced_path, + makefile_path: "#{underscored_name}/#{underscored_name}", + constant_name: constant_name, + constant_array: constant_array, + author: git_author_name.empty? ? "TODO: Write your name" : git_author_name, + email: git_user_email.empty? ? "TODO: Write your email address" : git_user_email, + test: options[:test], + ext: extension, + exe: options[:exe], + bundler_version: bundler_dependency_version, + git: use_git, + github_username: github_username.empty? ? "[USERNAME]" : github_username, + required_ruby_version: required_ruby_version, + rust_builder_required_rubygems_version: rust_builder_required_rubygems_version, + minitest_constant_name: minitest_constant_name, } ensure_safe_gem_name(name, constant_array) @@ -68,6 +82,7 @@ module Bundler "#{Bundler.preferred_gemfile_name}.tt" => Bundler.preferred_gemfile_name, "lib/newgem.rb.tt" => "lib/#{namespaced_path}.rb", "lib/newgem/version.rb.tt" => "lib/#{namespaced_path}/version.rb", + "sig/newgem.rbs.tt" => "sig/#{namespaced_path}.rbs", "newgem.gemspec.tt" => "#{name}.gemspec", "Rakefile.tt" => "Rakefile", "README.md.tt" => "README.md", @@ -95,9 +110,17 @@ module Bundler ) config[:test_task] = :spec when "minitest" + # Generate path for minitest target file (FileList["test/**/test_*.rb"]) + # foo => test/test_foo.rb + # foo-bar => test/foo/test_bar.rb + # foo_bar => test/test_foo_bar.rb + paths = namespaced_path.rpartition("/") + paths[2] = "test_#{paths[2]}" + minitest_namespaced_path = paths.join("") + templates.merge!( "test/minitest/test_helper.rb.tt" => "test/test_helper.rb", - "test/minitest/test_newgem.rb.tt" => "test/test_#{namespaced_path}.rb" + "test/minitest/test_newgem.rb.tt" => "test/#{minitest_namespaced_path}.rb" ) config[:test_task] = :test when "test-unit" @@ -113,12 +136,13 @@ module Bundler case config[:ci] when "github" templates.merge!("github/workflows/main.yml.tt" => ".github/workflows/main.yml") - when "travis" - templates.merge!("travis.yml.tt" => ".travis.yml") + config[:ci_config_path] = ".github " when "gitlab" templates.merge!("gitlab-ci.yml.tt" => ".gitlab-ci.yml") + config[:ci_config_path] = ".gitlab-ci.yml " when "circle" templates.merge!("circleci/config.yml.tt" => ".circleci/config.yml") + config[:ci_config_path] = ".circleci " end if ask_and_set(:mit, "Do you want to license your code permissively under the MIT license?", @@ -155,35 +179,46 @@ module Bundler templates.merge!("CHANGELOG.md.tt" => "CHANGELOG.md") end - if ask_and_set(:rubocop, "Do you want to add rubocop as a dependency for gems you generate?", - "RuboCop is a static code analyzer that has out-of-the-box rules for many " \ - "of the guidelines in the community style guide. " \ - "For more information, see the RuboCop docs (https://docs.rubocop.org/en/stable/) " \ - "and the Ruby Style Guides (https://github.com/rubocop-hq/ruby-style-guide).") - config[:rubocop] = true - config[:rubocop_version] = Gem.ruby_version < Gem::Version.new("2.4.a") ? "0.81.0" : "1.7" + config[:linter] = ask_and_set_linter + case config[:linter] + when "rubocop" + config[:linter_version] = rubocop_version Bundler.ui.info "RuboCop enabled in config" templates.merge!("rubocop.yml.tt" => ".rubocop.yml") + when "standard" + config[:linter_version] = standard_version + Bundler.ui.info "Standard enabled in config" + templates.merge!("standard.yml.tt" => ".standard.yml") end templates.merge!("exe/newgem.tt" => "exe/#{name}") if config[:exe] - if options[:ext] + if extension == "c" templates.merge!( - "ext/newgem/extconf.rb.tt" => "ext/#{name}/extconf.rb", + "ext/newgem/extconf-c.rb.tt" => "ext/#{name}/extconf.rb", "ext/newgem/newgem.h.tt" => "ext/#{name}/#{underscored_name}.h", "ext/newgem/newgem.c.tt" => "ext/#{name}/#{underscored_name}.c" ) end - if File.exist?(target) && !File.directory?(target) + if extension == "rust" + templates.merge!( + "Cargo.toml.tt" => "Cargo.toml", + "ext/newgem/Cargo.toml.tt" => "ext/#{name}/Cargo.toml", + "ext/newgem/extconf-rust.rb.tt" => "ext/#{name}/extconf.rb", + "ext/newgem/src/lib.rs.tt" => "ext/#{name}/src/lib.rs", + ) + end + + if target.exist? && !target.directory? Bundler.ui.error "Couldn't create a new gem named `#{gem_name}` because there's an existing file named `#{gem_name}`." exit Bundler::BundlerError.all_errors[Bundler::GenericSystemCallError] end if use_git Bundler.ui.info "Initializing git repo in #{target}" - `git init #{target}` + require "shellwords" + `git init #{target.to_s.shellescape}` config[:git_default_branch] = File.read("#{target}/.git/HEAD").split("/").last.chomp end @@ -200,9 +235,7 @@ module Bundler end if use_git - Dir.chdir(target) do - `git add .` - end + IO.popen(%w[git add .], { chdir: target }, &:read) end # Open gemspec in editor @@ -249,7 +282,7 @@ module Bundler Bundler.ui.info hint_text("test") result = Bundler.ui.ask "Enter a test framework. rspec/minitest/test-unit/(none):" - if result =~ /rspec|minitest|test-unit/ + if /rspec|minitest|test-unit/.match?(result) test_framework = result else test_framework = false @@ -285,12 +318,11 @@ module Bundler "* CircleCI: https://circleci.com/\n" \ "* GitHub Actions: https://github.com/features/actions\n" \ "* GitLab CI: https://docs.gitlab.com/ee/ci/\n" \ - "* Travis CI: https://travis-ci.org/\n" \ "\n" Bundler.ui.info hint_text("ci") - result = Bundler.ui.ask "Enter a CI service. github/travis/gitlab/circle/(none):" - if result =~ /github|travis|gitlab|circle/ + result = Bundler.ui.ask "Enter a CI service. github/gitlab/circle/(none):" + if /github|gitlab|circle/.match?(result) ci_template = result else ci_template = false @@ -308,6 +340,63 @@ module Bundler ci_template end + def ask_and_set_linter + linter_template = options[:linter] || Bundler.settings["gem.linter"] + linter_template = deprecated_rubocop_option if linter_template.nil? + + if linter_template.to_s.empty? + Bundler.ui.confirm "Do you want to add a code linter and formatter to your gem? " \ + "Supported Linters:\n" \ + "* RuboCop: https://rubocop.org\n" \ + "* Standard: https://github.com/standardrb/standard\n" \ + "\n" + Bundler.ui.info hint_text("linter") + + result = Bundler.ui.ask "Enter a linter. rubocop/standard/(none):" + if /rubocop|standard/.match?(result) + linter_template = result + else + linter_template = false + end + end + + if Bundler.settings["gem.linter"].nil? + Bundler.settings.set_global("gem.linter", linter_template) + end + + # Once gem.linter safely set, unset the deprecated gem.rubocop + unless Bundler.settings["gem.rubocop"].nil? + Bundler.settings.set_global("gem.rubocop", nil) + end + + if options[:linter] == Bundler.settings["gem.linter"] + Bundler.ui.info "#{options[:linter]} is already configured, ignoring --linter flag." + end + + linter_template + end + + def deprecated_rubocop_option + if !options[:rubocop].nil? + if options[:rubocop] + Bundler::SharedHelpers.major_deprecation 2, + "--rubocop is deprecated, use --linter=rubocop", + removed_message: "--rubocop has been removed, use --linter=rubocop" + "rubocop" + else + Bundler::SharedHelpers.major_deprecation 2, + "--no-rubocop is deprecated, use --linter", + removed_message: "--no-rubocop has been removed, use --linter" + false + end + elsif !Bundler.settings["gem.rubocop"].nil? + Bundler::SharedHelpers.major_deprecation 2, + "config gem.rubocop is deprecated; we've updated your config to use gem.linter instead", + removed_message: "config gem.rubocop has been removed; we've updated your config to use gem.linter instead" + Bundler.settings["gem.rubocop"] ? "rubocop" : false + end + end + def bundler_dependency_version v = Gem::Version.new(Bundler::VERSION) req = v.segments[0..1] @@ -316,7 +405,7 @@ module Bundler end def ensure_safe_gem_name(name, constant_array) - if name =~ /^\d/ + if /^\d/.match?(name) Bundler.ui.error "Invalid gem name #{name} Please give a name which does not start with numbers." exit 1 end @@ -341,5 +430,28 @@ module Bundler def open_editor(editor, file) thor.run(%(#{editor} "#{file}")) end + + def rust_builder_required_rubygems_version + "3.3.11" + end + + def required_ruby_version + "3.0.0" + end + + def rubocop_version + "1.21" + end + + def standard_version + "1.3" + end + + def validate_rust_builder_rubygems_version + if Gem::Version.new(rust_builder_required_rubygems_version) > Gem.rubygems_version + Bundler.ui.error "Your RubyGems version (#{Gem.rubygems_version}) is too old to build Rust extension. Please update your RubyGems using `gem update --system` or any other way and try again." + exit 1 + end + end end end |