From 8e9c31ba8e52afc212a8e40c32c852b18cf3989b Mon Sep 17 00:00:00 2001 From: "Garen J. Torikian" Date: Wed, 30 Sep 2020 17:16:24 +0100 Subject: [PATCH] Add a `bundle fund` command --- Manifest.txt | 1 + bundler/lib/bundler/cli.rb | 8 +++++ bundler/lib/bundler/cli/fund.rb | 36 +++++++++++++++++++ bundler/lib/bundler/cli/info.rb | 1 + bundler/lib/bundler/definition.rb | 24 ++++++------- bundler/spec/commands/fund_spec.rb | 55 ++++++++++++++++++++++++++++++ bundler/spec/commands/info_spec.rb | 1 + bundler/spec/support/builders.rb | 11 ++++++ 8 files changed, 125 insertions(+), 12 deletions(-) create mode 100644 bundler/lib/bundler/cli/fund.rb create mode 100644 bundler/spec/commands/fund_spec.rb diff --git a/Manifest.txt b/Manifest.txt index 09b2ff09487d..7e413102301d 100644 --- a/Manifest.txt +++ b/Manifest.txt @@ -32,6 +32,7 @@ bundler/lib/bundler/cli/config.rb bundler/lib/bundler/cli/console.rb bundler/lib/bundler/cli/doctor.rb bundler/lib/bundler/cli/exec.rb +bundler/lib/bundler/cli/fund.rb bundler/lib/bundler/cli/gem.rb bundler/lib/bundler/cli/info.rb bundler/lib/bundler/cli/init.rb diff --git a/bundler/lib/bundler/cli.rb b/bundler/lib/bundler/cli.rb index 8d30001bd480..b4196621e5f7 100644 --- a/bundler/lib/bundler/cli.rb +++ b/bundler/lib/bundler/cli.rb @@ -439,6 +439,14 @@ def outdated(*gems) Outdated.new(options, gems).run end + desc "fund [OPTIONS]", "Lists information about gems seeking funding assistance" + method_option "group", :aliases => "-g", :type => :array, :banner => + "Fetch funding information for a specific group" + def fund + require_relative "cli/fund" + Fund.new(options).run + end + desc "cache [OPTIONS]", "Locks and then caches all of the gems into vendor/cache" method_option "all", :type => :boolean, :default => Bundler.feature_flag.cache_all?, diff --git a/bundler/lib/bundler/cli/fund.rb b/bundler/lib/bundler/cli/fund.rb new file mode 100644 index 000000000000..52db5aef68ae --- /dev/null +++ b/bundler/lib/bundler/cli/fund.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module Bundler + class CLI::Fund + attr_reader :options + + def initialize(options) + @options = options + end + + def run + Bundler.definition.validate_runtime! + + groups = Array(options[:group]).map(&:to_sym) + + deps = if groups.any? + Bundler.definition.dependencies_for(groups) + else + Bundler.definition.current_dependencies + end + + fund_info = deps.each_with_object([]) do |dep, arr| + spec = Bundler.definition.specs[dep.name].first + if spec.metadata.key?("funding_uri") + arr << "* #{spec.name} (#{spec.version})\n Funding: #{spec.metadata["funding_uri"]}" + end + end + + if fund_info.empty? + Bundler.ui.info "None of the installed gems you directly depend on are looking for funding." + else + Bundler.ui.info fund_info.join("\n") + end + end + end +end diff --git a/bundler/lib/bundler/cli/info.rb b/bundler/lib/bundler/cli/info.rb index 68b2d8035420..3111b64a33af 100644 --- a/bundler/lib/bundler/cli/info.rb +++ b/bundler/lib/bundler/cli/info.rb @@ -60,6 +60,7 @@ def print_gem_info(spec) gem_info << "\tHomepage: #{spec.homepage}\n" if spec.homepage gem_info << "\tDocumentation: #{metadata["documentation_uri"]}\n" if metadata.key?("documentation_uri") gem_info << "\tSource Code: #{metadata["source_code_uri"]}\n" if metadata.key?("source_code_uri") + gem_info << "\tFunding: #{metadata["funding_uri"]}\n" if metadata.key?("funding_uri") gem_info << "\tWiki: #{metadata["wiki_uri"]}\n" if metadata.key?("wiki_uri") gem_info << "\tChangelog: #{metadata["changelog_uri"]}\n" if metadata.key?("changelog_uri") gem_info << "\tBug Tracker: #{metadata["bug_tracker_uri"]}\n" if metadata.key?("bug_tracker_uri") diff --git a/bundler/lib/bundler/definition.rb b/bundler/lib/bundler/definition.rb index c464650592af..fc35f31be16c 100644 --- a/bundler/lib/bundler/definition.rb +++ b/bundler/lib/bundler/definition.rb @@ -233,6 +233,12 @@ def requested_specs end end + def requested_dependencies + groups = requested_groups + groups.map!(&:to_sym) + dependencies_for(groups) + end + def current_dependencies dependencies.select do |d| d.should_include? && !d.gem_platforms(@platforms).empty? @@ -244,6 +250,12 @@ def specs_for(groups) specs.for(expand_dependencies(deps)) end + def dependencies_for(groups) + current_dependencies.reject do |d| + (d.groups & groups).empty? + end + end + # Resolve all the dependencies specified in Gemfile. It ensures that # dependencies that have been already resolved via locked file and are fresh # are reused when resolving dependencies @@ -898,18 +910,6 @@ def expand_dependency_with_platforms(dep, platforms) end end - def dependencies_for(groups) - current_dependencies.reject do |d| - (d.groups & groups).empty? - end - end - - def requested_dependencies - groups = requested_groups - groups.map!(&:to_sym) - dependencies_for(groups) - end - def source_requirements # Load all specs from remote sources index diff --git a/bundler/spec/commands/fund_spec.rb b/bundler/spec/commands/fund_spec.rb new file mode 100644 index 000000000000..ee3aff3a29dc --- /dev/null +++ b/bundler/spec/commands/fund_spec.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +RSpec.describe "bundle fund" do + it "prints fund information for all gems in the bundle" do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem 'has_metadata' + gem 'has_funding' + gem 'rack-obama' + G + + bundle "fund" + + expect(out).to include("* has_metadata (1.0)\n Funding: https://example.com/has_metadata/funding") + expect(out).to include("* has_funding (1.2.3)\n Funding: https://example.com/has_funding/funding") + expect(out).to_not include("rack-obama") + end + + it "does not consider fund information for gem dependencies" do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem 'gem_with_dependent_funding' + G + + bundle "fund" + + expect(out).to_not include("* has_funding (1.2.3)\n Funding: https://example.com/has_funding/funding") + expect(out).to_not include("gem_with_dependent_funding") + end + + it "prints message if none of the gems have fund information" do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem 'rack-obama' + G + + bundle "fund" + + expect(out).to include("None of the installed gems you directly depend on are looking for funding.") + end + + describe "with --group option" do + it "prints fund message for only specified group gems" do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem 'has_metadata', :group => :development + gem 'has_funding' + G + + bundle "fund --group development" + expect(out).to include("* has_metadata (1.0)\n Funding: https://example.com/has_metadata/funding") + expect(out).to_not include("* has_funding (1.2.3)\n Funding: https://example.com/has_funding/funding") + end + end +end diff --git a/bundler/spec/commands/info_spec.rb b/bundler/spec/commands/info_spec.rb index 9286e6824a2d..eec9c773bcf2 100644 --- a/bundler/spec/commands/info_spec.rb +++ b/bundler/spec/commands/info_spec.rb @@ -66,6 +66,7 @@ \tHomepage: http://example.com \tDocumentation: https://www.example.info/gems/bestgemever/0.0.1 \tSource Code: https://example.com/user/bestgemever +\tFunding: https://example.com/has_metadata/funding \tWiki: https://example.com/user/bestgemever/wiki \tChangelog: https://example.com/user/bestgemever/CHANGELOG.md \tBug Tracker: https://example.com/user/bestgemever/issues diff --git a/bundler/spec/support/builders.rb b/bundler/spec/support/builders.rb index e8481f98a377..a1770759a9df 100644 --- a/bundler/spec/support/builders.rb +++ b/bundler/spec/support/builders.rb @@ -322,10 +322,21 @@ def __pry__ "documentation_uri" => "https://www.example.info/gems/bestgemever/0.0.1", "homepage_uri" => "https://bestgemever.example.io", "mailing_list_uri" => "https://groups.example.com/bestgemever", + "funding_uri" => "https://example.com/has_metadata/funding", "source_code_uri" => "https://example.com/user/bestgemever", "wiki_uri" => "https://example.com/user/bestgemever/wiki", } end + + build_gem "has_funding", "1.2.3" do |s| + s.metadata = { + "funding_uri" => "https://example.com/has_funding/funding", + } + end + + build_gem "gem_with_dependent_funding", "1.0" do |s| + s.add_dependency "has_funding" + end end end