Merge pull request #7762 from MLH-Fellowship/license

License
This commit is contained in:
Mike McQuaid 2020-07-01 09:27:51 +01:00 committed by GitHub
commit 8c8f2df85f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 5530 additions and 10 deletions

View File

@ -114,6 +114,9 @@ jobs:
- name: Run brew man
run: brew man --fail-if-changed
- name: Check for outdated license data
run: brew update-license-data --fail-if-changed
- name: Run brew tests
run: |
# brew tests doesn't like world writable directories

View File

@ -211,6 +211,8 @@ module Homebrew
puts "From: #{Formatter.url(github_info(f))}"
puts "License: #{f.license}" if f.license
unless f.deps.empty?
ohai "Dependencies"
%w[build required recommended optional].map do |type|

File diff suppressed because it is too large Load Diff

View File

@ -12,6 +12,7 @@ require "date"
require "missing_formula"
require "digest"
require "cli/parser"
require "json"
module Homebrew
module_function
@ -109,7 +110,9 @@ module Homebrew
# Check style in a single batch run up front for performance
style_results = Style.check_style_json(style_files, options) if style_files
# load licenses
spdx = HOMEBREW_LIBRARY_PATH/"data/spdx.json"
spdx_data = JSON.parse(spdx.read)
new_formula_problem_lines = []
audit_formulae.sort.each do |f|
only = only_cops ? ["style"] : args.only
@ -120,6 +123,7 @@ module Homebrew
git: git,
only: only,
except: args.except,
spdx_data: spdx_data,
}
options[:style_offenses] = style_results.file_offenses(f.path) if style_results
options[:display_cop_names] = args.display_cop_names?
@ -215,6 +219,7 @@ module Homebrew
@new_formula_problems = []
@text = FormulaText.new(formula.path)
@specs = %w[stable devel head].map { |s| formula.send(s) }.compact
@spdx_data = options[:spdx_data]
end
def audit_style
@ -327,6 +332,27 @@ module Homebrew
openssl@1.1
].freeze
def audit_license
if formula.license.present?
if @spdx_data["licenses"].any? { |lic| lic["licenseId"] == formula.license }
return unless @online
user, repo = get_repo_data(%r{https?://github\.com/([^/]+)/([^/]+)/?.*}) if @new_formula
return if user.blank?
github_license = GitHub.get_repo_license(user, repo)
return if github_license && (github_license == formula.license)
problem "License mismatch - GitHub license is: #{github_license}, "\
"but Formulae license states: #{formula.license}."
else
problem "#{formula.license} is not a standard SPDX license."
end
elsif @new_formula
problem "No license specified for package."
end
end
def audit_deps
@specs.each do |spec|
# Check for things we don't like to depend on.
@ -502,8 +528,9 @@ module Homebrew
end
def audit_github_repository
user, repo = get_repo_data(%r{https?://github\.com/([^/]+)/([^/]+)/?.*})
return if user.nil?
user, repo = get_repo_data(%r{https?://github\.com/([^/]+)/([^/]+)/?.*}) if @new_formula
return if user.blank?
warning = SharedAudits.github(user, repo)
return if warning.nil?
@ -512,8 +539,8 @@ module Homebrew
end
def audit_gitlab_repository
user, repo = get_repo_data(%r{https?://gitlab\.com/([^/]+)/([^/]+)/?.*})
return if user.nil?
user, repo = get_repo_data(%r{https?://gitlab\.com/([^/]+)/([^/]+)/?.*}) if @new_formula
return if user.blank?
warning = SharedAudits.gitlab(user, repo)
return if warning.nil?
@ -522,8 +549,8 @@ module Homebrew
end
def audit_bitbucket_repository
user, repo = get_repo_data(%r{https?://bitbucket\.org/([^/]+)/([^/]+)/?.*})
return if user.nil?
user, repo = get_repo_data(%r{https?://bitbucket\.org/([^/]+)/([^/]+)/?.*}) if @new_formula
return if user.blank?
warning = SharedAudits.bitbucket(user, repo)
return if warning.nil?
@ -534,7 +561,6 @@ module Homebrew
def get_repo_data(regex)
return unless @core_tap
return unless @online
return unless @new_formula
_, user, repo = *regex.match(formula.stable.url) if formula.stable
_, user, repo = *regex.match(formula.homepage) unless user

View File

@ -47,6 +47,8 @@ module Homebrew
description: "Explicitly set the <name> of the new formula."
flag "--set-version=",
description: "Explicitly set the <version> of the new formula."
flag "--set-license=",
description: "Explicitly set the <license> of the new formula."
flag "--tap=",
description: "Generate the new formula within the given tap, specified as <user>`/`<repo>."
switch :force
@ -68,11 +70,13 @@ module Homebrew
version = args.set_version
name = args.set_name
license = args.set_license
tap = args.tap
fc = FormulaCreator.new
fc.name = name
fc.version = version
fc.license = license
fc.tap = Tap.fetch(tap || "homebrew/core")
raise TapUnavailableError, tap unless fc.tap.installed?

View File

@ -0,0 +1,39 @@
# frozen_string_literal: true
require "commands"
require "cli/parser"
require "json"
require "net/http"
require "open-uri"
module Homebrew
module_function
SPDX_PATH = (HOMEBREW_LIBRARY_PATH/"data/spdx.json").freeze
SPDX_DATA_URL = "https://raw.githubusercontent.com/spdx/license-list-data/HEAD/json/licenses.json"
def update_license_data_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS
`update_license_data` <cmd>
Update SPDX license data in the Homebrew repository.
EOS
switch "--fail-if-changed",
description: "Return a failing status code if current license data's version is different from " \
"the upstream. This can be used to notify CI when the SPDX license data is out of date."
max_named 0
end
end
def update_license_data
update_license_data_args.parse
ohai "Updating SPDX license data..."
curl_download(SPDX_DATA_URL, to: SPDX_PATH)
return unless args.fail_if_changed?
system("git diff --stat --exit-code #{SPDX_PATH}")
end
end

View File

@ -351,6 +351,9 @@ class Formula
# @see .desc=
delegate desc: :"self.class"
# The SPDX ID of the software license.
delegate license: :"self.class"
# The homepage for the software.
# @method homepage
# @see .homepage=
@ -1687,6 +1690,7 @@ class Formula
"aliases" => aliases.sort,
"versioned_formulae" => versioned_formulae.map(&:name),
"desc" => desc,
"license" => license,
"homepage" => homepage,
"versions" => {
"stable" => stable&.version&.to_s,
@ -2211,6 +2215,13 @@ class Formula
# <pre>desc "Example formula"</pre>
attr_rw :desc
# @!attribute [w]
# The SPDX ID of the open-source license that the formula uses.
# Shows when running `brew info`.
#
# <pre>license "BSD-2-Clause"</pre>
attr_rw :license
# @!attribute [w] homepage
# The homepage for the software. Used by users to get more information
# about the software and Homebrew maintainers as a point of contact for

View File

@ -6,7 +6,7 @@ require "erb"
module Homebrew
class FormulaCreator
attr_reader :url, :sha256, :desc, :homepage
attr_accessor :name, :version, :tap, :path, :mode
attr_accessor :name, :version, :tap, :path, :mode, :license
def url=(url)
@url = url
@ -100,6 +100,7 @@ module Homebrew
<% end %>
sha256 "#{sha256}"
<% end %>
license "#{license}"
<% if mode == :cmake %>
depends_on "cmake" => :build

View File

@ -24,7 +24,8 @@ module RuboCop
[{ name: :mirror, type: :method_call }],
[{ name: :version, type: :method_call }],
[{ name: :sha256, type: :method_call }],
[{ name: :revision, type: :method_call }],
[{ name: :license, type: :method_call }],
[{ name: :revision, type: :method_call }],
[{ name: :version_scheme, type: :method_call }],
[{ name: :head, type: :method_call }],
[{ name: :stable, type: :block_call }],

View File

@ -79,6 +79,92 @@ module Homebrew
end
end
describe "#audit_license" do
let(:spdx_data) {
JSON.parse Pathname(File.join(File.dirname(__FILE__), "../../data/spdx.json")).read
}
let(:custom_spdx_id) { "zzz" }
let(:standard_mismatch_spdx_id) { "0BSD" }
it "does not check if the formula is not a new formula" do
fa = formula_auditor "foo", <<~RUBY, spdx_data: spdx_data, new_formula: false
class Foo < Formula
url "https://brew.sh/foo-1.0.tgz"
license ""
end
RUBY
fa.audit_license
expect(fa.problems).to be_empty
end
it "detects no license info" do
fa = formula_auditor "foo", <<~RUBY, spdx_data: spdx_data, new_formula: true
class Foo < Formula
url "https://brew.sh/foo-1.0.tgz"
license ""
end
RUBY
fa.audit_license
expect(fa.problems.first).to match "No license specified for package."
end
it "detects if license is not a standard spdx-id" do
fa = formula_auditor "foo", <<~RUBY, spdx_data: spdx_data, new_formula: true
class Foo < Formula
url "https://brew.sh/foo-1.0.tgz"
license "#{custom_spdx_id}"
end
RUBY
fa.audit_license
expect(fa.problems.first).to match "#{custom_spdx_id} is not a standard SPDX license."
end
it "verifies that a license info is a standard spdx id" do
fa = formula_auditor "foo", <<~RUBY, spdx_data: spdx_data, new_formula: true
class Foo < Formula
url "https://brew.sh/foo-1.0.tgz"
license "0BSD"
end
RUBY
fa.audit_license
expect(fa.problems).to be_empty
end
it "checks online and verifies that a standard license id is the same "\
"as what is indicated on its Github repo" do
fa = formula_auditor "cask", <<~RUBY, spdx_data: spdx_data, online: true, core_tap: true, new_formula: true
class Cask < Formula
url "https://github.com/cask/cask/archive/v0.8.4.tar.gz"
head "https://github.com/cask/cask.git"
license "GPL-3.0"
end
RUBY
fa.audit_license
expect(fa.problems).to be_empty
end
it "checks online and detects that a formula-specified license is not "\
"the same as what is indicated on its Github repository" do
fa = formula_auditor "cask", <<~RUBY, online: true, spdx_data: spdx_data, core_tap: true, new_formula: true
class Cask < Formula
url "https://github.com/cask/cask/archive/v0.8.4.tar.gz"
head "https://github.com/cask/cask.git"
license "#{standard_mismatch_spdx_id}"
end
RUBY
fa.audit_license
expect(fa.problems.first).to match "License mismatch - GitHub license is: GPL-3.0, "\
"but Formulae license states: #{standard_mismatch_spdx_id}."
end
end
describe "#audit_file" do
specify "no issue" do
fa = formula_auditor "foo", <<~RUBY

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
require "cmd/shared_examples/args_parse"
describe "Homebrew.update_license_data_args" do
it_behaves_like "parseable arguments"
end

View File

@ -19,6 +19,18 @@ describe RuboCop::Cop::FormulaAudit::ComponentsOrder do
RUBY
end
it "When license precedes sha256" do
expect_offense(<<~RUBY)
class Foo < Formula
homepage "https://brew.sh"
url "https://brew.sh/foo-1.0.tgz"
license "0BSD"
sha256 "samplesha256"
^^^^^^^^^^^^^^^^^^^^^ `sha256` (line 5) should be put before `license` (line 4)
end
RUBY
end
it "When `bottle` precedes `livecheck`" do
expect_offense(<<~RUBY)
class Foo < Formula

View File

@ -474,6 +474,15 @@ module GitHub
open_api(url, scopes: ["admin:org", "user"], data: data, request_method: "POST")
end
def get_repo_license(user, repo)
response = GitHub.open_api("#{GitHub::API_URL}/repos/#{user}/#{repo}/license")
return unless response.key?("license")
response["license"]["spdx_id"]
rescue GitHub::HTTPNotFoundError
nil
end
def api_errors
[GitHub::AuthenticationFailedError, GitHub::HTTPNotFoundError,
GitHub::RateLimitExceededError, GitHub::Error, JSON::ParserError].freeze

View File

@ -86,6 +86,7 @@ unpin
untap
up
update
update-license-data
update-report
update-reset
update-test

View File

@ -793,6 +793,8 @@ a simple example. For the complete API, see:
Explicitly set the *`name`* of the new formula.
* `--set-version`:
Explicitly set the *`version`* of the new formula.
* `--set-license`:
Explicitly set the *`license`* of the new formula.
* `--tap`:
Generate the new formula within the given tap, specified as *`user`*`/`*`repo`*.
@ -1028,6 +1030,13 @@ directory.
* `-g`, `--git`:
Initialise a Git repository in the unpacked source. This is useful for creating patches for the software.
### `update_license_data` *`cmd`*
Update SPDX license data in the Homebrew repository.
* `--fail-if-changed`:
Return a failing status code if current license data's version is different from the upstream. This can be used to notify CI when the SPDX license data is out of date.
### `update-test` [*`options`*]
Run a test of `brew update` with a new repository clone. If no options are

View File

@ -1047,6 +1047,10 @@ Explicitly set the \fIname\fR of the new formula\.
Explicitly set the \fIversion\fR of the new formula\.
.
.TP
\fB\-\-set\-license\fR
Explicitly set the \fIlicense\fR of the new formula\.
.
.TP
\fB\-\-tap\fR
Generate the new formula within the given tap, specified as \fIuser\fR\fB/\fR\fIrepo\fR\.
.
@ -1331,6 +1335,13 @@ Patches for \fIformula\fR will be applied to the unpacked source\.
\fB\-g\fR, \fB\-\-git\fR
Initialise a Git repository in the unpacked source\. This is useful for creating patches for the software\.
.
.SS "\fBupdate_license_data\fR \fIcmd\fR"
Update SPDX license data in the Homebrew repository\.
.
.TP
\fB\-\-fail\-if\-changed\fR
Return a failing status code if current license data\'s version is different from the upstream\. This can be used to notify CI when the SPDX license data is out of date\.
.
.SS "\fBupdate\-test\fR [\fIoptions\fR]"
Run a test of \fBbrew update\fR with a new repository clone\. If no options are passed, use \fBorigin/master\fR as the start commit\.
.