diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index cf1b784e8b5e5c5c7ca1a9351350975e7267f96d..93436967962824aa8ed07201c9ad797f97332d7f 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -2232,7 +2232,18 @@ class Formula # <pre>license all_of: ["MIT", "GPL-2.0-only"]</pre> # <pre>license "GPL-2.0-only" => { with: "LLVM-exception" }</pre> # <pre>license :public_domain</pre> - attr_rw :license + def license(args = nil) + if args.nil? + @licenses + else + if args.is_a? Array + # TODO: enable for next major/minor release + # odeprecated "`license [...]`", "`license any_of: [...]`" + args = { any_of: args } + end + @licenses = args + end + end # @!attribute [w] homepage # The homepage for the software. Used by users to get more information diff --git a/Library/Homebrew/rubocops/lines.rb b/Library/Homebrew/rubocops/lines.rb index 66d601e9b0f1c5732c9107e6df70632b4907090b..a29092b4250b354c00f425eb2df1864b79de5fa9 100644 --- a/Library/Homebrew/rubocops/lines.rb +++ b/Library/Homebrew/rubocops/lines.rb @@ -254,6 +254,38 @@ module RuboCop end end + class LicenseArrays < FormulaCop + def audit_formula(_node, _class_node, _parent_class_node, body_node) + license_node = find_node_method_by_name(body_node, :license) + return unless license_node + + license = parameters(license_node).first + return unless license.array_type? + + problem "Use `license any_of: #{license.source}` instead of `license #{license.source}`" + end + + def autocorrect(node) + lambda do |corrector| + corrector.replace(node.source_range, "license any_of: #{parameters(node).first.source}") + end + end + end + + class Licenses < FormulaCop + def audit_formula(_node, _class_node, _parent_class_node, body_node) + license_node = find_node_method_by_name(body_node, :license) + return unless license_node + + license = parameters(license_node).first + return unless license.hash_type? + return unless license.each_descendant(:hash).count.positive? + return if license.source.include?("\n") + + problem "Split nested license declarations onto multiple lines" + end + end + class Miscellaneous < FormulaCop def audit_formula(_node, _class_node, _parent_class_node, body_node) # FileUtils is included in Formula diff --git a/Library/Homebrew/test/rubocops/lines_spec.rb b/Library/Homebrew/test/rubocops/lines_spec.rb index 404617524b843b4badcffd736c3fb2f09c6a9284..17b253764102118bd9e614647c9ca910bfe285f7 100644 --- a/Library/Homebrew/test/rubocops/lines_spec.rb +++ b/Library/Homebrew/test/rubocops/lines_spec.rb @@ -579,6 +579,134 @@ describe RuboCop::Cop::FormulaAudit::ShellVariables do end end +describe RuboCop::Cop::FormulaAudit::LicenseArrays do + subject(:cop) { described_class.new } + + context "When auditing licenses" do + it "allow license strings" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + license "MIT" + end + RUBY + end + + it "allow license symbols" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + license :public_domain + end + RUBY + end + + it "allow license hashes" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + license any_of: ["MIT", "0BSD"] + end + RUBY + end + + it "require using :any_of instead of a license array" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + license ["MIT", "0BSD"] + ^^^^^^^^^^^^^^^^^^^^^^^ Use `license any_of: ["MIT", "0BSD"]` instead of `license ["MIT", "0BSD"]` + end + RUBY + end + + it "corrects license arrays to hash with :any_of" do + source = <<~RUBY + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + license ["MIT", "0BSD"] + end + RUBY + + corrected_source = <<~RUBY + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + license any_of: ["MIT", "0BSD"] + end + RUBY + + new_source = autocorrect_source(source) + expect(new_source).to eq(corrected_source) + end + end +end + +describe RuboCop::Cop::FormulaAudit::Licenses do + subject(:cop) { described_class.new } + + context "When auditing licenses" do + it "allow license strings" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + license "MIT" + end + RUBY + end + + it "allow license symbols" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + license :public_domain + end + RUBY + end + + it "allow license hashes" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + license any_of: ["MIT", "0BSD"] + end + RUBY + end + + it "allow multiline nested license hashes" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + license any_of: [ + "MIT", + all_of: ["0BSD", "Zlib"], + ] + end + RUBY + end + + it "require multiple lines for nested license hashes" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + license any_of: ["MIT", all_of: ["0BSD", "Zlib"]] + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Split nested license declarations onto multiple lines + end + RUBY + end + end +end + describe RuboCop::Cop::FormulaAudit::Miscellaneous do subject(:cop) { described_class.new } diff --git a/Library/Homebrew/test/utils/spdx_spec.rb b/Library/Homebrew/test/utils/spdx_spec.rb index bf216a60dc4cf898be6304ac313f2bcec198600e..9593f119ce7bd976c05fe9e48b47c676db820b31 100644 --- a/Library/Homebrew/test/utils/spdx_spec.rb +++ b/Library/Homebrew/test/utils/spdx_spec.rb @@ -167,10 +167,6 @@ describe SPDX do expect(described_class.license_expression_to_string(any_of: ["MIT", "EPL-1.0+"])).to eq "MIT or EPL-1.0+" end - it "treats array as any_of:" do - expect(described_class.license_expression_to_string(["MIT", "EPL-1.0+"])).to eq "MIT or EPL-1.0+" - end - it "returns license and exception" do license_expression = { "MIT" => { with: "LLVM-exception" } } expect(described_class.license_expression_to_string(license_expression)).to eq "MIT with LLVM-exception" @@ -237,7 +233,6 @@ describe SPDX do } } let(:any_of_license) { { any_of: ["MIT", "0BSD"] } } - let(:license_array) { ["MIT", "0BSD"] } let(:all_of_license) { { all_of: ["MIT", "0BSD"] } } let(:nested_licenses) { { @@ -278,14 +273,6 @@ describe SPDX do expect(described_class.licenses_forbid_installation?(any_of_license, multiple_forbidden)).to eq true end - it "allows installation when one of the array licenses is allowed" do - expect(described_class.licenses_forbid_installation?(license_array, mit_forbidden)).to eq false - end - - it "forbids installation when none of the array licenses are allowed" do - expect(described_class.licenses_forbid_installation?(license_array, multiple_forbidden)).to eq true - end - it "forbids installation when one of the all_of licenses is allowed" do expect(described_class.licenses_forbid_installation?(all_of_license, mit_forbidden)).to eq true end diff --git a/Library/Homebrew/utils/spdx.rb b/Library/Homebrew/utils/spdx.rb index dda9d811d890fdbc20bed8fef957faa88d7db565..7ca5a5f8ab7d126c5d9dea9a073e643638467276 100644 --- a/Library/Homebrew/utils/spdx.rb +++ b/Library/Homebrew/utils/spdx.rb @@ -83,8 +83,7 @@ module SPDX license_expression when :public_domain "Public Domain" - when Hash, Array - license_expression = { any_of: license_expression } if license_expression.is_a? Array + when Hash expressions = [] if license_expression.keys.length == 1 @@ -135,8 +134,7 @@ module SPDX case license_expression when String, Symbol forbidden_licenses_include? license_expression.to_s, forbidden_licenses - when Hash, Array - license_expression = { any_of: license_expression } if license_expression.is_a? Array + when Hash key = license_expression.keys.first case key when :any_of