Merge pull request #8260 from Rylan12/allow-AND-and-WITH-in-licenses
audit: allow AND and WITH in licenses
This commit is contained in:
commit
778bd543c7
2
.github/workflows/spdx.yml
vendored
2
.github/workflows/spdx.yml
vendored
@ -26,7 +26,7 @@ jobs:
|
||||
run: |
|
||||
cd "$GITHUB_WORKSPACE/Library/Homebrew"
|
||||
if brew update-license-data --commit --fail-if-not-changed; then
|
||||
SPDX_VERSION=$(jq -er .licenseListVersion data/spdx.json)
|
||||
SPDX_VERSION=$(jq -er .licenseListVersion data/spdx/spdx_licenses.json)
|
||||
if ! git ls-remote --exit-code --heads origin "spdx-$SPDX_VERSION"; then
|
||||
git checkout -b "spdx-$SPDX_VERSION"
|
||||
git push origin "spdx-$SPDX_VERSION"
|
||||
|
||||
@ -68,6 +68,10 @@ Style/HashTransformKeys:
|
||||
Style/HashTransformValues:
|
||||
Enabled: true
|
||||
|
||||
# Allow for license expressions
|
||||
Style/HashAsLastArrayItem:
|
||||
Enabled: false
|
||||
|
||||
# Enabled now LineLength is lowish.
|
||||
Style/IfUnlessModifier:
|
||||
Enabled: true
|
||||
|
||||
@ -61,6 +61,8 @@ Metrics/MethodLength:
|
||||
Metrics/ModuleLength:
|
||||
Enabled: true
|
||||
Max: 600
|
||||
Exclude:
|
||||
- 'test/**/*'
|
||||
Metrics/PerceivedComplexity:
|
||||
Enabled: true
|
||||
Max: 90
|
||||
@ -143,3 +145,9 @@ Style/GuardClause:
|
||||
# so many of these in formulae but none in here
|
||||
Style/StringConcatenation:
|
||||
Enabled: true
|
||||
|
||||
# don't want this for formulae but re-enabled for Library/Homebrew
|
||||
Style/HashAsLastArrayItem:
|
||||
Enabled: true
|
||||
Exclude:
|
||||
- 'test/utils/spdx_spec.rb'
|
||||
|
||||
@ -8,6 +8,7 @@ require "formula"
|
||||
require "keg"
|
||||
require "tab"
|
||||
require "json"
|
||||
require "utils/spdx"
|
||||
|
||||
module Homebrew
|
||||
module_function
|
||||
@ -211,13 +212,7 @@ module Homebrew
|
||||
|
||||
puts "From: #{Formatter.url(github_info(f))}"
|
||||
|
||||
if f.license.present?
|
||||
licenses = f.license
|
||||
.map(&:to_s)
|
||||
.join(", ")
|
||||
.sub("public_domain", "Public Domain")
|
||||
puts "License: #{licenses}"
|
||||
end
|
||||
puts "License: #{SPDX.license_expression_to_string f.license}" if f.license.present?
|
||||
|
||||
unless f.deps.empty?
|
||||
ohai "Dependencies"
|
||||
|
||||
@ -29,6 +29,9 @@ HOMEBREW_LIBRARY = Pathname.new(get_env_or_raise("HOMEBREW_LIBRARY")).freeze
|
||||
# Where shim scripts for various build and SCM tools are stored
|
||||
HOMEBREW_SHIMS_PATH = (HOMEBREW_LIBRARY/"Homebrew/shims").freeze
|
||||
|
||||
# Where external data that has been incorporated into Homebrew is stored
|
||||
HOMEBREW_DATA_PATH = (HOMEBREW_LIBRARY/"Homebrew/data").freeze
|
||||
|
||||
# Where we store symlinks to currently linked kegs
|
||||
HOMEBREW_LINKED_KEGS = (HOMEBREW_PREFIX/"var/homebrew/linked").freeze
|
||||
|
||||
|
||||
466
Library/Homebrew/data/spdx/spdx_exceptions.json
Normal file
466
Library/Homebrew/data/spdx/spdx_exceptions.json
Normal file
@ -0,0 +1,466 @@
|
||||
{
|
||||
"licenseListVersion": "3.10",
|
||||
"releaseDate": "2020-08-03",
|
||||
"exceptions": [
|
||||
{
|
||||
"reference": "./GCC-exception-2.0.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/GCC-exception-2.0.json",
|
||||
"referenceNumber": "1",
|
||||
"name": "GCC Runtime Library exception 2.0",
|
||||
"seeAlso": [
|
||||
"https://gcc.gnu.org/git/?p\u003dgcc.git;a\u003dblob;f\u003dgcc/libgcc1.c;h\u003d762f5143fc6eed57b6797c82710f3538aa52b40b;hb\u003dcb143a3ce4fb417c68f5fa2691a1b1b1053dfba9#l10"
|
||||
],
|
||||
"licenseExceptionId": "GCC-exception-2.0"
|
||||
},
|
||||
{
|
||||
"reference": "./openvpn-openssl-exception.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/openvpn-openssl-exception.json",
|
||||
"referenceNumber": "2",
|
||||
"name": "OpenVPN OpenSSL Exception",
|
||||
"seeAlso": [
|
||||
"http://openvpn.net/index.php/license.html"
|
||||
],
|
||||
"licenseExceptionId": "openvpn-openssl-exception"
|
||||
},
|
||||
{
|
||||
"reference": "./Nokia-Qt-exception-1.1.html",
|
||||
"isDeprecatedLicenseId": true,
|
||||
"detailsUrl": "http://spdx.org/licenses/Nokia-Qt-exception-1.1.json",
|
||||
"referenceNumber": "3",
|
||||
"name": "Nokia Qt LGPL exception 1.1",
|
||||
"seeAlso": [
|
||||
"https://www.keepassx.org/dev/projects/keepassx/repository/revisions/b8dfb9cc4d5133e0f09cd7533d15a4f1c19a40f2/entry/LICENSE.NOKIA-LGPL-EXCEPTION"
|
||||
],
|
||||
"licenseExceptionId": "Nokia-Qt-exception-1.1"
|
||||
},
|
||||
{
|
||||
"reference": "./GPL-3.0-linking-exception.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/GPL-3.0-linking-exception.json",
|
||||
"referenceNumber": "4",
|
||||
"name": "GPL-3.0 Linking Exception",
|
||||
"seeAlso": [
|
||||
"https://www.gnu.org/licenses/gpl-faq.en.html#GPLIncompatibleLibs"
|
||||
],
|
||||
"licenseExceptionId": "GPL-3.0-linking-exception"
|
||||
},
|
||||
{
|
||||
"reference": "./Fawkes-Runtime-exception.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/Fawkes-Runtime-exception.json",
|
||||
"referenceNumber": "5",
|
||||
"name": "Fawkes Runtime Exception",
|
||||
"seeAlso": [
|
||||
"http://www.fawkesrobotics.org/about/license/"
|
||||
],
|
||||
"licenseExceptionId": "Fawkes-Runtime-exception"
|
||||
},
|
||||
{
|
||||
"reference": "./u-boot-exception-2.0.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/u-boot-exception-2.0.json",
|
||||
"referenceNumber": "6",
|
||||
"name": "U-Boot exception 2.0",
|
||||
"seeAlso": [
|
||||
"http://git.denx.de/?p\u003du-boot.git;a\u003dblob;f\u003dLicenses/Exceptions"
|
||||
],
|
||||
"licenseExceptionId": "u-boot-exception-2.0"
|
||||
},
|
||||
{
|
||||
"reference": "./PS-or-PDF-font-exception-20170817.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/PS-or-PDF-font-exception-20170817.json",
|
||||
"referenceNumber": "7",
|
||||
"name": "PS/PDF font exception (2017-08-17)",
|
||||
"seeAlso": [
|
||||
"https://github.com/ArtifexSoftware/urw-base35-fonts/blob/65962e27febc3883a17e651cdb23e783668c996f/LICENSE"
|
||||
],
|
||||
"licenseExceptionId": "PS-or-PDF-font-exception-20170817"
|
||||
},
|
||||
{
|
||||
"reference": "./gnu-javamail-exception.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/gnu-javamail-exception.json",
|
||||
"referenceNumber": "8",
|
||||
"name": "GNU JavaMail exception",
|
||||
"seeAlso": [
|
||||
"http://www.gnu.org/software/classpathx/javamail/javamail.html"
|
||||
],
|
||||
"licenseExceptionId": "gnu-javamail-exception"
|
||||
},
|
||||
{
|
||||
"reference": "./LGPL-3.0-linking-exception.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/LGPL-3.0-linking-exception.json",
|
||||
"referenceNumber": "9",
|
||||
"name": "LGPL-3.0 Linking Exception",
|
||||
"seeAlso": [
|
||||
"https://raw.githubusercontent.com/go-xmlpath/xmlpath/v2/LICENSE",
|
||||
"https://github.com/goamz/goamz/blob/master/LICENSE",
|
||||
"https://github.com/juju/errors/blob/master/LICENSE"
|
||||
],
|
||||
"licenseExceptionId": "LGPL-3.0-linking-exception"
|
||||
},
|
||||
{
|
||||
"reference": "./DigiRule-FOSS-exception.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/DigiRule-FOSS-exception.json",
|
||||
"referenceNumber": "10",
|
||||
"name": "DigiRule FOSS License Exception",
|
||||
"seeAlso": [
|
||||
"http://www.digirulesolutions.com/drupal/foss"
|
||||
],
|
||||
"licenseExceptionId": "DigiRule-FOSS-exception"
|
||||
},
|
||||
{
|
||||
"reference": "./LLVM-exception.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/LLVM-exception.json",
|
||||
"referenceNumber": "11",
|
||||
"name": "LLVM Exception",
|
||||
"seeAlso": [
|
||||
"http://llvm.org/foundation/relicensing/LICENSE.txt"
|
||||
],
|
||||
"licenseExceptionId": "LLVM-exception"
|
||||
},
|
||||
{
|
||||
"reference": "./Linux-syscall-note.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/Linux-syscall-note.json",
|
||||
"referenceNumber": "12",
|
||||
"name": "Linux Syscall Note",
|
||||
"seeAlso": [
|
||||
"https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/COPYING"
|
||||
],
|
||||
"licenseExceptionId": "Linux-syscall-note"
|
||||
},
|
||||
{
|
||||
"reference": "./GPL-3.0-linking-source-exception.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/GPL-3.0-linking-source-exception.json",
|
||||
"referenceNumber": "13",
|
||||
"name": "GPL-3.0 Linking Exception (with Corresponding Source)",
|
||||
"seeAlso": [
|
||||
"https://www.gnu.org/licenses/gpl-faq.en.html#GPLIncompatibleLibs",
|
||||
"https://github.com/mirror/wget/blob/master/src/http.c#L20"
|
||||
],
|
||||
"licenseExceptionId": "GPL-3.0-linking-source-exception"
|
||||
},
|
||||
{
|
||||
"reference": "./Qwt-exception-1.0.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/Qwt-exception-1.0.json",
|
||||
"referenceNumber": "14",
|
||||
"name": "Qwt exception 1.0",
|
||||
"seeAlso": [
|
||||
"http://qwt.sourceforge.net/qwtlicense.html"
|
||||
],
|
||||
"licenseExceptionId": "Qwt-exception-1.0"
|
||||
},
|
||||
{
|
||||
"reference": "./389-exception.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/389-exception.json",
|
||||
"referenceNumber": "15",
|
||||
"name": "389 Directory Server Exception",
|
||||
"seeAlso": [
|
||||
"http://directory.fedoraproject.org/wiki/GPL_Exception_License_Text"
|
||||
],
|
||||
"licenseExceptionId": "389-exception"
|
||||
},
|
||||
{
|
||||
"reference": "./mif-exception.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/mif-exception.json",
|
||||
"referenceNumber": "16",
|
||||
"name": "Macros and Inline Functions Exception",
|
||||
"seeAlso": [
|
||||
"http://www.scs.stanford.edu/histar/src/lib/cppsup/exception",
|
||||
"http://dev.bertos.org/doxygen/",
|
||||
"https://www.threadingbuildingblocks.org/licensing"
|
||||
],
|
||||
"licenseExceptionId": "mif-exception"
|
||||
},
|
||||
{
|
||||
"reference": "./eCos-exception-2.0.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/eCos-exception-2.0.json",
|
||||
"referenceNumber": "17",
|
||||
"name": "eCos exception 2.0",
|
||||
"seeAlso": [
|
||||
"http://ecos.sourceware.org/license-overview.html"
|
||||
],
|
||||
"licenseExceptionId": "eCos-exception-2.0"
|
||||
},
|
||||
{
|
||||
"reference": "./CLISP-exception-2.0.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/CLISP-exception-2.0.json",
|
||||
"referenceNumber": "18",
|
||||
"name": "CLISP exception 2.0",
|
||||
"seeAlso": [
|
||||
"http://sourceforge.net/p/clisp/clisp/ci/default/tree/COPYRIGHT"
|
||||
],
|
||||
"licenseExceptionId": "CLISP-exception-2.0"
|
||||
},
|
||||
{
|
||||
"reference": "./Bison-exception-2.2.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/Bison-exception-2.2.json",
|
||||
"referenceNumber": "19",
|
||||
"name": "Bison exception 2.2",
|
||||
"seeAlso": [
|
||||
"http://git.savannah.gnu.org/cgit/bison.git/tree/data/yacc.c?id\u003d193d7c7054ba7197b0789e14965b739162319b5e#n141"
|
||||
],
|
||||
"licenseExceptionId": "Bison-exception-2.2"
|
||||
},
|
||||
{
|
||||
"reference": "./Libtool-exception.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/Libtool-exception.json",
|
||||
"referenceNumber": "20",
|
||||
"name": "Libtool Exception",
|
||||
"seeAlso": [
|
||||
"http://git.savannah.gnu.org/cgit/libtool.git/tree/m4/libtool.m4"
|
||||
],
|
||||
"licenseExceptionId": "Libtool-exception"
|
||||
},
|
||||
{
|
||||
"reference": "./LZMA-exception.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/LZMA-exception.json",
|
||||
"referenceNumber": "21",
|
||||
"name": "LZMA exception",
|
||||
"seeAlso": [
|
||||
"http://nsis.sourceforge.net/Docs/AppendixI.html#I.6"
|
||||
],
|
||||
"licenseExceptionId": "LZMA-exception"
|
||||
},
|
||||
{
|
||||
"reference": "./OpenJDK-assembly-exception-1.0.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/OpenJDK-assembly-exception-1.0.json",
|
||||
"referenceNumber": "22",
|
||||
"name": "OpenJDK Assembly exception 1.0",
|
||||
"seeAlso": [
|
||||
"http://openjdk.java.net/legal/assembly-exception.html"
|
||||
],
|
||||
"licenseExceptionId": "OpenJDK-assembly-exception-1.0"
|
||||
},
|
||||
{
|
||||
"reference": "./Font-exception-2.0.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/Font-exception-2.0.json",
|
||||
"referenceNumber": "23",
|
||||
"name": "Font exception 2.0",
|
||||
"seeAlso": [
|
||||
"http://www.gnu.org/licenses/gpl-faq.html#FontException"
|
||||
],
|
||||
"licenseExceptionId": "Font-exception-2.0"
|
||||
},
|
||||
{
|
||||
"reference": "./OCaml-LGPL-linking-exception.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/OCaml-LGPL-linking-exception.json",
|
||||
"referenceNumber": "24",
|
||||
"name": "OCaml LGPL Linking Exception",
|
||||
"seeAlso": [
|
||||
"https://caml.inria.fr/ocaml/license.en.html"
|
||||
],
|
||||
"licenseExceptionId": "OCaml-LGPL-linking-exception"
|
||||
},
|
||||
{
|
||||
"reference": "./GCC-exception-3.1.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/GCC-exception-3.1.json",
|
||||
"referenceNumber": "25",
|
||||
"name": "GCC Runtime Library exception 3.1",
|
||||
"seeAlso": [
|
||||
"http://www.gnu.org/licenses/gcc-exception-3.1.html"
|
||||
],
|
||||
"licenseExceptionId": "GCC-exception-3.1"
|
||||
},
|
||||
{
|
||||
"reference": "./Bootloader-exception.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/Bootloader-exception.json",
|
||||
"referenceNumber": "26",
|
||||
"name": "Bootloader Distribution Exception",
|
||||
"seeAlso": [
|
||||
"https://github.com/pyinstaller/pyinstaller/blob/develop/COPYING.txt"
|
||||
],
|
||||
"licenseExceptionId": "Bootloader-exception"
|
||||
},
|
||||
{
|
||||
"reference": "./SHL-2.0.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/SHL-2.0.json",
|
||||
"referenceNumber": "27",
|
||||
"name": "Solderpad Hardware License v2.0",
|
||||
"seeAlso": [
|
||||
"https://solderpad.org/licenses/SHL-2.0/"
|
||||
],
|
||||
"licenseExceptionId": "SHL-2.0"
|
||||
},
|
||||
{
|
||||
"reference": "./Classpath-exception-2.0.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/Classpath-exception-2.0.json",
|
||||
"referenceNumber": "28",
|
||||
"name": "Classpath exception 2.0",
|
||||
"seeAlso": [
|
||||
"http://www.gnu.org/software/classpath/license.html",
|
||||
"https://fedoraproject.org/wiki/Licensing/GPL_Classpath_Exception"
|
||||
],
|
||||
"licenseExceptionId": "Classpath-exception-2.0"
|
||||
},
|
||||
{
|
||||
"reference": "./Swift-exception.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/Swift-exception.json",
|
||||
"referenceNumber": "29",
|
||||
"name": "Swift Exception",
|
||||
"seeAlso": [
|
||||
"https://swift.org/LICENSE.txt",
|
||||
"https://github.com/apple/swift-package-manager/blob/7ab2275f447a5eb37497ed63a9340f8a6d1e488b/LICENSE.txt#L205"
|
||||
],
|
||||
"licenseExceptionId": "Swift-exception"
|
||||
},
|
||||
{
|
||||
"reference": "./Autoconf-exception-2.0.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/Autoconf-exception-2.0.json",
|
||||
"referenceNumber": "30",
|
||||
"name": "Autoconf exception 2.0",
|
||||
"seeAlso": [
|
||||
"http://ac-archive.sourceforge.net/doc/copyright.html",
|
||||
"http://ftp.gnu.org/gnu/autoconf/autoconf-2.59.tar.gz"
|
||||
],
|
||||
"licenseExceptionId": "Autoconf-exception-2.0"
|
||||
},
|
||||
{
|
||||
"reference": "./FLTK-exception.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/FLTK-exception.json",
|
||||
"referenceNumber": "31",
|
||||
"name": "FLTK exception",
|
||||
"seeAlso": [
|
||||
"http://www.fltk.org/COPYING.php"
|
||||
],
|
||||
"licenseExceptionId": "FLTK-exception"
|
||||
},
|
||||
{
|
||||
"reference": "./freertos-exception-2.0.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/freertos-exception-2.0.json",
|
||||
"referenceNumber": "32",
|
||||
"name": "FreeRTOS Exception 2.0",
|
||||
"seeAlso": [
|
||||
"https://web.archive.org/web/20060809182744/http://www.freertos.org/a00114.html"
|
||||
],
|
||||
"licenseExceptionId": "freertos-exception-2.0"
|
||||
},
|
||||
{
|
||||
"reference": "./Universal-FOSS-exception-1.0.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/Universal-FOSS-exception-1.0.json",
|
||||
"referenceNumber": "33",
|
||||
"name": "Universal FOSS Exception, Version 1.0",
|
||||
"seeAlso": [
|
||||
"https://oss.oracle.com/licenses/universal-foss-exception/"
|
||||
],
|
||||
"licenseExceptionId": "Universal-FOSS-exception-1.0"
|
||||
},
|
||||
{
|
||||
"reference": "./WxWindows-exception-3.1.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/WxWindows-exception-3.1.json",
|
||||
"referenceNumber": "34",
|
||||
"name": "WxWindows Library Exception 3.1",
|
||||
"seeAlso": [
|
||||
"http://www.opensource.org/licenses/WXwindows"
|
||||
],
|
||||
"licenseExceptionId": "WxWindows-exception-3.1"
|
||||
},
|
||||
{
|
||||
"reference": "./OCCT-exception-1.0.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/OCCT-exception-1.0.json",
|
||||
"referenceNumber": "35",
|
||||
"name": "Open CASCADE Exception 1.0",
|
||||
"seeAlso": [
|
||||
"http://www.opencascade.com/content/licensing"
|
||||
],
|
||||
"licenseExceptionId": "OCCT-exception-1.0"
|
||||
},
|
||||
{
|
||||
"reference": "./Autoconf-exception-3.0.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/Autoconf-exception-3.0.json",
|
||||
"referenceNumber": "36",
|
||||
"name": "Autoconf exception 3.0",
|
||||
"seeAlso": [
|
||||
"http://www.gnu.org/licenses/autoconf-exception-3.0.html"
|
||||
],
|
||||
"licenseExceptionId": "Autoconf-exception-3.0"
|
||||
},
|
||||
{
|
||||
"reference": "./i2p-gpl-java-exception.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/i2p-gpl-java-exception.json",
|
||||
"referenceNumber": "37",
|
||||
"name": "i2p GPL+Java Exception",
|
||||
"seeAlso": [
|
||||
"http://geti2p.net/en/get-involved/develop/licenses#java_exception"
|
||||
],
|
||||
"licenseExceptionId": "i2p-gpl-java-exception"
|
||||
},
|
||||
{
|
||||
"reference": "./GPL-CC-1.0.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/GPL-CC-1.0.json",
|
||||
"referenceNumber": "38",
|
||||
"name": "GPL Cooperation Commitment 1.0",
|
||||
"seeAlso": [
|
||||
"https://github.com/gplcc/gplcc/blob/master/Project/COMMITMENT",
|
||||
"https://gplcc.github.io/gplcc/Project/README-PROJECT.html"
|
||||
],
|
||||
"licenseExceptionId": "GPL-CC-1.0"
|
||||
},
|
||||
{
|
||||
"reference": "./Qt-LGPL-exception-1.1.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/Qt-LGPL-exception-1.1.json",
|
||||
"referenceNumber": "39",
|
||||
"name": "Qt LGPL exception 1.1",
|
||||
"seeAlso": [
|
||||
"http://code.qt.io/cgit/qt/qtbase.git/tree/LGPL_EXCEPTION.txt"
|
||||
],
|
||||
"licenseExceptionId": "Qt-LGPL-exception-1.1"
|
||||
},
|
||||
{
|
||||
"reference": "./SHL-2.1.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/SHL-2.1.json",
|
||||
"referenceNumber": "40",
|
||||
"name": "Solderpad Hardware License v2.1",
|
||||
"seeAlso": [
|
||||
"https://solderpad.org/licenses/SHL-2.1/"
|
||||
],
|
||||
"licenseExceptionId": "SHL-2.1"
|
||||
},
|
||||
{
|
||||
"reference": "./Qt-GPL-exception-1.0.html",
|
||||
"isDeprecatedLicenseId": false,
|
||||
"detailsUrl": "http://spdx.org/licenses/Qt-GPL-exception-1.0.json",
|
||||
"referenceNumber": "41",
|
||||
"name": "Qt GPL exception 1.0",
|
||||
"seeAlso": [
|
||||
"http://code.qt.io/cgit/qt/qtbase.git/tree/LICENSE.GPL3-EXCEPT"
|
||||
],
|
||||
"licenseExceptionId": "Qt-GPL-exception-1.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -119,7 +119,8 @@ 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_data = SPDX.spdx_data
|
||||
spdx_license_data = SPDX.license_data
|
||||
spdx_exception_data = SPDX.exception_data
|
||||
new_formula_problem_lines = []
|
||||
audit_formulae.sort.each do |f|
|
||||
only = only_cops ? ["style"] : args.only
|
||||
@ -130,7 +131,8 @@ module Homebrew
|
||||
git: git,
|
||||
only: only,
|
||||
except: args.except,
|
||||
spdx_data: spdx_data,
|
||||
spdx_license_data: spdx_license_data,
|
||||
spdx_exception_data: spdx_exception_data,
|
||||
}
|
||||
options[:style_offenses] = style_results.file_offenses(f.path) if style_results
|
||||
options[:display_cop_names] = args.display_cop_names?
|
||||
@ -228,7 +230,8 @@ 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]
|
||||
@spdx_license_data = options[:spdx_license_data]
|
||||
@spdx_exception_data = options[:spdx_exception_data]
|
||||
end
|
||||
|
||||
def audit_style
|
||||
@ -357,43 +360,49 @@ module Homebrew
|
||||
|
||||
def audit_license
|
||||
if formula.license.present?
|
||||
non_standard_licenses = formula.license.map do |license|
|
||||
next if license == :public_domain
|
||||
next if @spdx_data["licenses"].any? { |spdx| spdx["licenseId"] == license }
|
||||
|
||||
license
|
||||
end.compact
|
||||
licenses, exceptions = SPDX.parse_license_expression formula.license
|
||||
|
||||
non_standard_licenses = licenses.reject { |license| SPDX.valid_license? license }
|
||||
if non_standard_licenses.present?
|
||||
problem "Formula #{formula.name} contains non-standard SPDX licenses: #{non_standard_licenses}."
|
||||
problem <<~EOS
|
||||
Formula #{formula.name} contains non-standard SPDX licenses: #{non_standard_licenses}.
|
||||
For a list of valid licenses check: #{Formatter.url("https://spdx.org/licenses/")}
|
||||
EOS
|
||||
end
|
||||
|
||||
if @strict
|
||||
deprecated_licenses = formula.license.map do |license|
|
||||
next if license == :public_domain
|
||||
next if @spdx_data["licenses"].any? do |spdx|
|
||||
spdx["licenseId"] == license && !spdx["isDeprecatedLicenseId"]
|
||||
deprecated_licenses = licenses.select do |license|
|
||||
SPDX.deprecated_license? license
|
||||
end
|
||||
|
||||
license
|
||||
end.compact
|
||||
|
||||
if deprecated_licenses.present?
|
||||
problem "Formula #{formula.name} contains deprecated SPDX licenses: #{deprecated_licenses}."
|
||||
problem <<~EOS
|
||||
Formula #{formula.name} contains deprecated SPDX licenses: #{deprecated_licenses}.
|
||||
You may need to add `-only` or `-or-later` for GNU licenses (e.g. `GPL`, `LGPL`, `AGPL`, `GFDL`).
|
||||
For a list of valid licenses check: #{Formatter.url("https://spdx.org/licenses/")}
|
||||
EOS
|
||||
end
|
||||
end
|
||||
|
||||
invalid_exceptions = exceptions.reject { |exception| SPDX.valid_license_exception? exception }
|
||||
if invalid_exceptions.present?
|
||||
problem <<~EOS
|
||||
Formula #{formula.name} contains invalid or deprecated SPDX license exceptions: #{invalid_exceptions}.
|
||||
For a list of valid license exceptions check:
|
||||
#{Formatter.url("https://spdx.org/licenses/exceptions-index.html/")}
|
||||
EOS
|
||||
end
|
||||
|
||||
return unless @online
|
||||
|
||||
user, repo = get_repo_data(%r{https?://github\.com/([^/]+)/([^/]+)/?.*})
|
||||
return if user.blank?
|
||||
|
||||
github_license = GitHub.get_repo_license(user, repo)
|
||||
return if github_license && (formula.license + ["NOASSERTION"]).include?(github_license)
|
||||
return if PERMITTED_LICENSE_MISMATCHES[github_license]&.any? { |license| formula.license.include? license }
|
||||
return if github_license && (licenses + ["NOASSERTION"]).include?(github_license)
|
||||
return if PERMITTED_LICENSE_MISMATCHES[github_license]&.any? { |license| licenses.include? license }
|
||||
return if PERMITTED_FORMULA_LICENSE_MISMATCHES[formula.name] == formula.version
|
||||
|
||||
problem "Formula license #{formula.license} does not match GitHub license #{Array(github_license)}."
|
||||
problem "Formula license #{licenses} does not match GitHub license #{Array(github_license)}."
|
||||
|
||||
elsif @new_formula && @core_tap
|
||||
problem "Formulae in homebrew/core must specify a license."
|
||||
|
||||
@ -28,13 +28,13 @@ module Homebrew
|
||||
|
||||
SPDX.download_latest_license_data!
|
||||
|
||||
Homebrew.failed = system("git", "diff", "--stat", "--exit-code", SPDX::JSON_PATH) if args.fail_if_not_changed?
|
||||
Homebrew.failed = system("git", "diff", "--stat", "--exit-code", SPDX::DATA_PATH) if args.fail_if_not_changed?
|
||||
|
||||
return unless args.commit?
|
||||
|
||||
ohai "git add"
|
||||
safe_system "git", "add", SPDX::JSON_PATH
|
||||
safe_system "git", "add", SPDX::DATA_PATH
|
||||
ohai "git commit"
|
||||
system "git", "commit", "--message", "data/spdx.json: update to #{SPDX.latest_tag}"
|
||||
system "git", "commit", "--message", "spdx license data: update to #{SPDX.latest_tag}"
|
||||
end
|
||||
end
|
||||
|
||||
@ -2219,18 +2219,20 @@ class Formula
|
||||
# @!attribute [w]
|
||||
# The SPDX ID of the open-source license that the formula uses.
|
||||
# Shows when running `brew info`.
|
||||
# Multiple licenses means that the software is licensed under multiple licenses.
|
||||
# Do not use multiple licenses if e.g. different parts are under different licenses.
|
||||
# Use `:any`, `:all` or `:with` to describe complex license expressions.
|
||||
# `:any` should be used when the user can choose which license to use.
|
||||
# `:all` should be used when the user must use all licenses.
|
||||
# `:with` should be used to specify a valid SPDX exception.
|
||||
# Add `+` to an identifier to indicate that the formulae can be
|
||||
# licensed under later versions of the same license.
|
||||
# @see https://spdx.github.io/spdx-spec/appendix-IV-SPDX-license-expressions/ SPDX license expression guide
|
||||
# <pre>license "BSD-2-Clause"</pre>
|
||||
# <pre>license ["MIT", "GPL-2.0"]</pre>
|
||||
# <pre>license "EPL-1.0+"</pre>
|
||||
# <pre>license any_of: ["MIT", "GPL-2.0-only"]</pre>
|
||||
# <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>
|
||||
def license(args = nil)
|
||||
if args.nil?
|
||||
@licenses
|
||||
else
|
||||
@licenses = Array(args)
|
||||
end
|
||||
end
|
||||
attr_rw :license
|
||||
|
||||
# @!attribute [w] homepage
|
||||
# The homepage for the software. Used by users to get more information
|
||||
|
||||
@ -19,6 +19,7 @@ require "messages"
|
||||
require "cask/cask_loader"
|
||||
require "cmd/install"
|
||||
require "find"
|
||||
require "utils/spdx"
|
||||
|
||||
class FormulaInstaller
|
||||
include FormulaCellarChecks
|
||||
@ -1130,24 +1131,29 @@ class FormulaInstaller
|
||||
.to_s
|
||||
.sub("Public Domain", "public_domain")
|
||||
.split(" ")
|
||||
.to_h do |license|
|
||||
[license, SPDX.license_version_info(license)]
|
||||
end
|
||||
|
||||
return if forbidden_licenses.blank?
|
||||
|
||||
compute_dependencies.each do |dep, _|
|
||||
next if @ignore_deps
|
||||
|
||||
dep_f = dep.to_formula
|
||||
next unless dep_f.license.all? { |license| forbidden_licenses.include?(license.to_s) }
|
||||
next unless SPDX.licenses_forbid_installation? dep_f.license, forbidden_licenses
|
||||
|
||||
raise CannotInstallFormulaError, <<~EOS
|
||||
The installation of #{formula.name} has a dependency on #{dep.name} where all its licenses are forbidden: #{dep_f.license}.
|
||||
The installation of #{formula.name} has a dependency on #{dep.name} where all its licenses are forbidden:
|
||||
#{SPDX.license_expression_to_string dep_f.license}.
|
||||
EOS
|
||||
end
|
||||
return if @only_deps
|
||||
|
||||
return unless formula.license.all? { |license| forbidden_licenses.include?(license.to_s) }
|
||||
return unless SPDX.licenses_forbid_installation? formula.license, forbidden_licenses
|
||||
|
||||
raise CannotInstallFormulaError, <<~EOS
|
||||
#{formula.name}'s licenses are all forbidden: #{formula.license}.
|
||||
#{formula.name}'s licenses are all forbidden: #{SPDX.license_expression_to_string formula.license}.
|
||||
EOS
|
||||
end
|
||||
end
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
require "dev-cmd/audit"
|
||||
require "formulary"
|
||||
require "cmd/shared_examples/args_parse"
|
||||
require "utils/spdx"
|
||||
|
||||
describe "Homebrew.audit_args" do
|
||||
it_behaves_like "parseable arguments"
|
||||
@ -80,20 +81,21 @@ module Homebrew
|
||||
end
|
||||
|
||||
describe "#audit_license" do
|
||||
let(:spdx_data) {
|
||||
JSON.parse Pathname(File.join(File.dirname(__FILE__), "../../data/spdx.json")).read
|
||||
}
|
||||
let(:spdx_license_data) { SPDX.license_data }
|
||||
let(:spdx_exception_data) { SPDX.exception_data }
|
||||
|
||||
let(:custom_spdx_id) { "zzz" }
|
||||
let(:deprecated_spdx_id) { "GPL-1.0" }
|
||||
let(:standard_mismatch_spdx_id) { "0BSD" }
|
||||
let(:license_array) { ["0BSD", "GPL-3.0"] }
|
||||
let(:license_array_mismatch) { ["0BSD", "MIT"] }
|
||||
let(:license_array_nonstandard) { ["0BSD", "zzz", "MIT"] }
|
||||
let(:license_array_deprecated) { ["0BSD", "GPL-1.0", "MIT"] }
|
||||
let(:license_all_custom_id) { 'all_of: ["MIT", "zzz"]' }
|
||||
let(:deprecated_spdx_exception) { "Nokia-Qt-exception-1.1" }
|
||||
let(:license_any) { 'any_of: ["0BSD", "GPL-3.0-only"]' }
|
||||
let(:license_any_with_plus) { 'any_of: ["0BSD+", "GPL-3.0-only"]' }
|
||||
let(:license_nested_conditions) { 'any_of: ["0BSD", { all_of: ["GPL-3.0-only", "MIT"] }]' }
|
||||
let(:license_any_mismatch) { 'any_of: ["0BSD", "MIT"]' }
|
||||
let(:license_any_nonstandard) { 'any_of: ["0BSD", "zzz", "MIT"]' }
|
||||
let(:license_any_deprecated) { 'any_of: ["0BSD", "GPL-1.0", "MIT"]' }
|
||||
|
||||
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
|
||||
fa = formula_auditor "foo", <<~RUBY, new_formula: false
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
end
|
||||
@ -104,7 +106,7 @@ module Homebrew
|
||||
end
|
||||
|
||||
it "detects no license info" do
|
||||
fa = formula_auditor "foo", <<~RUBY, spdx_data: spdx_data, new_formula: true, core_tap: true
|
||||
fa = formula_auditor "foo", <<~RUBY, spdx_license_data: spdx_license_data, new_formula: true, core_tap: true
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
end
|
||||
@ -115,19 +117,22 @@ module Homebrew
|
||||
end
|
||||
|
||||
it "detects if license is not a standard spdx-id" do
|
||||
fa = formula_auditor "foo", <<~RUBY, spdx_data: spdx_data, new_formula: true
|
||||
fa = formula_auditor "foo", <<~RUBY, spdx_license_data: spdx_license_data, new_formula: true
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
license "#{custom_spdx_id}"
|
||||
license "zzz"
|
||||
end
|
||||
RUBY
|
||||
|
||||
fa.audit_license
|
||||
expect(fa.problems.first).to match "Formula foo contains non-standard SPDX licenses: [\"zzz\"]."
|
||||
expect(fa.problems.first).to match <<~EOS
|
||||
Formula foo contains non-standard SPDX licenses: ["zzz"].
|
||||
For a list of valid licenses check: https://spdx.org/licenses/
|
||||
EOS
|
||||
end
|
||||
|
||||
it "detects if license is a deprecated spdx-id" do
|
||||
fa = formula_auditor "foo", <<~RUBY, spdx_data: spdx_data, new_formula: true, strict: true
|
||||
fa = formula_auditor "foo", <<~RUBY, spdx_license_data: spdx_license_data, new_formula: true, strict: true
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
license "#{deprecated_spdx_id}"
|
||||
@ -135,35 +140,61 @@ module Homebrew
|
||||
RUBY
|
||||
|
||||
fa.audit_license
|
||||
expect(fa.problems.first).to match "Formula foo contains deprecated SPDX licenses: [\"GPL-1.0\"]."
|
||||
expect(fa.problems.first).to match <<~EOS
|
||||
Formula foo contains deprecated SPDX licenses: ["GPL-1.0"].
|
||||
You may need to add `-only` or `-or-later` for GNU licenses (e.g. `GPL`, `LGPL`, `AGPL`, `GFDL`).
|
||||
For a list of valid licenses check: https://spdx.org/licenses/
|
||||
EOS
|
||||
end
|
||||
|
||||
it "detects if license with AND contains a non-standard spdx-id" do
|
||||
fa = formula_auditor "foo", <<~RUBY, spdx_license_data: spdx_license_data, new_formula: true
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
license #{license_all_custom_id}
|
||||
end
|
||||
RUBY
|
||||
|
||||
fa.audit_license
|
||||
expect(fa.problems.first).to match <<~EOS
|
||||
Formula foo contains non-standard SPDX licenses: ["zzz"].
|
||||
For a list of valid licenses check: https://spdx.org/licenses/
|
||||
EOS
|
||||
end
|
||||
|
||||
it "detects if license array contains a non-standard spdx-id" do
|
||||
fa = formula_auditor "foo", <<~RUBY, spdx_data: spdx_data, new_formula: true
|
||||
fa = formula_auditor "foo", <<~RUBY, spdx_license_data: spdx_license_data, new_formula: true
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
license #{license_array_nonstandard}
|
||||
license #{license_any_nonstandard}
|
||||
end
|
||||
RUBY
|
||||
|
||||
fa.audit_license
|
||||
expect(fa.problems.first).to match "Formula foo contains non-standard SPDX licenses: [\"zzz\"]."
|
||||
expect(fa.problems.first).to match <<~EOS
|
||||
Formula foo contains non-standard SPDX licenses: ["zzz"].
|
||||
For a list of valid licenses check: https://spdx.org/licenses/
|
||||
EOS
|
||||
end
|
||||
|
||||
it "detects if license array contains a deprecated spdx-id" do
|
||||
fa = formula_auditor "foo", <<~RUBY, spdx_data: spdx_data, new_formula: true, strict: true
|
||||
fa = formula_auditor "foo", <<~RUBY, spdx_license_data: spdx_license_data, new_formula: true, strict: true
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
license #{license_array_deprecated}
|
||||
license #{license_any_deprecated}
|
||||
end
|
||||
RUBY
|
||||
|
||||
fa.audit_license
|
||||
expect(fa.problems.first).to match "Formula foo contains deprecated SPDX licenses: [\"GPL-1.0\"]."
|
||||
expect(fa.problems.first).to match <<~EOS
|
||||
Formula foo contains deprecated SPDX licenses: ["GPL-1.0"].
|
||||
You may need to add `-only` or `-or-later` for GNU licenses (e.g. `GPL`, `LGPL`, `AGPL`, `GFDL`).
|
||||
For a list of valid licenses check: https://spdx.org/licenses/
|
||||
EOS
|
||||
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
|
||||
fa = formula_auditor "foo", <<~RUBY, spdx_license_data: spdx_license_data, new_formula: true
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
license "0BSD"
|
||||
@ -174,11 +205,85 @@ module Homebrew
|
||||
expect(fa.problems).to be_empty
|
||||
end
|
||||
|
||||
it "verifies that a license array contains only standard spdx id" do
|
||||
fa = formula_auditor "foo", <<~RUBY, spdx_data: spdx_data, new_formula: true
|
||||
it "verifies that a license info with plus is a standard spdx id" do
|
||||
fa = formula_auditor "foo", <<~RUBY, spdx_license_data: spdx_license_data, new_formula: true
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
license #{license_array}
|
||||
license "0BSD+"
|
||||
end
|
||||
RUBY
|
||||
|
||||
fa.audit_license
|
||||
expect(fa.problems).to be_empty
|
||||
end
|
||||
|
||||
it "allows :public_domain license" do
|
||||
fa = formula_auditor "foo", <<~RUBY, spdx_license_data: spdx_license_data, new_formula: true
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
license :public_domain
|
||||
end
|
||||
RUBY
|
||||
|
||||
fa.audit_license
|
||||
expect(fa.problems).to be_empty
|
||||
end
|
||||
|
||||
it "verifies that a license info with multiple licenses are standard spdx ids" do
|
||||
fa = formula_auditor "foo", <<~RUBY, spdx_license_data: spdx_license_data, new_formula: true
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
license any_of: ["0BSD", "MIT"]
|
||||
end
|
||||
RUBY
|
||||
|
||||
fa.audit_license
|
||||
expect(fa.problems).to be_empty
|
||||
end
|
||||
|
||||
it "verifies that a license info with exceptions are standard spdx ids" do
|
||||
formula_text = <<~RUBY
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
license "Apache-2.0" => { with: "LLVM-exception" }
|
||||
end
|
||||
RUBY
|
||||
fa = formula_auditor "foo", formula_text, new_formula: true,
|
||||
spdx_license_data: spdx_license_data, spdx_exception_data: spdx_exception_data
|
||||
|
||||
fa.audit_license
|
||||
expect(fa.problems).to be_empty
|
||||
end
|
||||
|
||||
it "verifies that a license array contains only standard spdx id" do
|
||||
fa = formula_auditor "foo", <<~RUBY, spdx_license_data: spdx_license_data, new_formula: true
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
license #{license_any}
|
||||
end
|
||||
RUBY
|
||||
|
||||
fa.audit_license
|
||||
expect(fa.problems).to be_empty
|
||||
end
|
||||
|
||||
it "verifies that a license array contains only standard spdx id with plus" do
|
||||
fa = formula_auditor "foo", <<~RUBY, spdx_license_data: spdx_license_data, new_formula: true
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
license #{license_any_with_plus}
|
||||
end
|
||||
RUBY
|
||||
|
||||
fa.audit_license
|
||||
expect(fa.problems).to be_empty
|
||||
end
|
||||
|
||||
it "verifies that a license array with AND contains only standard spdx ids" do
|
||||
fa = formula_auditor "foo", <<~RUBY, spdx_license_data: spdx_license_data, new_formula: true
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
license #{license_nested_conditions}
|
||||
end
|
||||
RUBY
|
||||
|
||||
@ -188,21 +293,93 @@ module Homebrew
|
||||
|
||||
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
|
||||
formula_text = <<~RUBY
|
||||
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 = formula_auditor "cask", formula_text, spdx_license_data: spdx_license_data,
|
||||
online: true, core_tap: true, new_formula: true
|
||||
|
||||
fa.audit_license
|
||||
expect(fa.problems).to be_empty
|
||||
end
|
||||
|
||||
it "checks online and verifies that a standard license id with AND is the same "\
|
||||
"as what is indicated on its Github repo" do
|
||||
formula_text = <<~RUBY
|
||||
class Cask < Formula
|
||||
url "https://github.com/cask/cask/archive/v0.8.4.tar.gz"
|
||||
head "https://github.com/cask/cask.git"
|
||||
license all_of: ["GPL-3.0-or-later", "MIT"]
|
||||
end
|
||||
RUBY
|
||||
fa = formula_auditor "cask", formula_text, spdx_license_data: spdx_license_data,
|
||||
online: true, core_tap: true, new_formula: true
|
||||
|
||||
fa.audit_license
|
||||
expect(fa.problems).to be_empty
|
||||
end
|
||||
|
||||
it "checks online and verifies that a standard license id with WITH is the same "\
|
||||
"as what is indicated on its Github repo" do
|
||||
formula_text = <<~RUBY
|
||||
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-or-later" => { with: "LLVM-exception" }
|
||||
end
|
||||
RUBY
|
||||
fa = formula_auditor "cask", formula_text, online: true, core_tap: true, new_formula: true,
|
||||
spdx_license_data: spdx_license_data, spdx_exception_data: spdx_exception_data
|
||||
|
||||
fa.audit_license
|
||||
expect(fa.problems).to be_empty
|
||||
end
|
||||
|
||||
it "verifies that a license exception has standard spdx ids" do
|
||||
formula_text = <<~RUBY
|
||||
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-or-later" => { with: "zzz" }
|
||||
end
|
||||
RUBY
|
||||
fa = formula_auditor "cask", formula_text, core_tap: true, new_formula: true,
|
||||
spdx_license_data: spdx_license_data, spdx_exception_data: spdx_exception_data
|
||||
|
||||
fa.audit_license
|
||||
expect(fa.problems.first).to match <<~EOS
|
||||
Formula cask contains invalid or deprecated SPDX license exceptions: ["zzz"].
|
||||
For a list of valid license exceptions check:
|
||||
https://spdx.org/licenses/exceptions-index.html/
|
||||
EOS
|
||||
end
|
||||
|
||||
it "verifies that a license exception has non-deprecated spdx ids" do
|
||||
formula_text = <<~RUBY
|
||||
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-or-later" => { with: "#{deprecated_spdx_exception}" }
|
||||
end
|
||||
RUBY
|
||||
fa = formula_auditor "cask", formula_text, core_tap: true, new_formula: true,
|
||||
spdx_license_data: spdx_license_data, spdx_exception_data: spdx_exception_data
|
||||
|
||||
fa.audit_license
|
||||
expect(fa.problems.first).to match <<~EOS
|
||||
Formula cask contains invalid or deprecated SPDX license exceptions: ["#{deprecated_spdx_exception}"].
|
||||
For a list of valid license exceptions check:
|
||||
https://spdx.org/licenses/exceptions-index.html/
|
||||
EOS
|
||||
end
|
||||
|
||||
it "checks online and verifies that a standard license id is in the same exempted license group" \
|
||||
"as what is indicated on its GitHub repo" do
|
||||
fa = formula_auditor "cask", <<~RUBY, spdx_data: spdx_data, online: true, new_formula: true
|
||||
fa = formula_auditor "cask", <<~RUBY, spdx_license_data: spdx_license_data, online: 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"
|
||||
@ -216,11 +393,11 @@ module Homebrew
|
||||
|
||||
it "checks online and verifies that a standard license array is in the same exempted license group" \
|
||||
"as what is indicated on its GitHub repo" do
|
||||
fa = formula_auditor "cask", <<~RUBY, spdx_data: spdx_data, online: true, new_formula: true
|
||||
fa = formula_auditor "cask", <<~RUBY, spdx_license_data: spdx_license_data, online: 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-or-later", "MIT"]
|
||||
license any_of: ["GPL-3.0-or-later", "MIT"]
|
||||
end
|
||||
RUBY
|
||||
|
||||
@ -230,43 +407,48 @@ module Homebrew
|
||||
|
||||
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
|
||||
formula_text = <<~RUBY
|
||||
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}"
|
||||
license "0BSD"
|
||||
end
|
||||
RUBY
|
||||
fa = formula_auditor "cask", formula_text, spdx_license_data: spdx_license_data,
|
||||
online: true, core_tap: true, new_formula: true
|
||||
|
||||
fa.audit_license
|
||||
expect(fa.problems.first).to match "Formula license #{Array(standard_mismatch_spdx_id)} "\
|
||||
"does not match GitHub license [\"GPL-3.0\"]."
|
||||
expect(fa.problems.first).to match "Formula license [\"0BSD\"] does not match GitHub license [\"GPL-3.0\"]."
|
||||
end
|
||||
|
||||
it "checks online and detects that an array of license does not contain "\
|
||||
"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
|
||||
formula_text = <<~RUBY
|
||||
class Cask < Formula
|
||||
url "https://github.com/cask/cask/archive/v0.8.4.tar.gz"
|
||||
head "https://github.com/cask/cask.git"
|
||||
license #{license_array_mismatch}
|
||||
license #{license_any_mismatch}
|
||||
end
|
||||
RUBY
|
||||
fa = formula_auditor "cask", formula_text, spdx_license_data: spdx_license_data,
|
||||
online: true, core_tap: true, new_formula: true
|
||||
|
||||
fa.audit_license
|
||||
expect(fa.problems.first).to match "Formula license #{license_array_mismatch} "\
|
||||
expect(fa.problems.first).to match "Formula license [\"0BSD\", \"MIT\"] "\
|
||||
"does not match GitHub license [\"GPL-3.0\"]."
|
||||
end
|
||||
|
||||
it "checks online and verifies that an array of license contains "\
|
||||
"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
|
||||
formula_text = <<~RUBY
|
||||
class Cask < Formula
|
||||
url "https://github.com/cask/cask/archive/v0.8.4.tar.gz"
|
||||
head "https://github.com/cask/cask.git"
|
||||
license #{license_array}
|
||||
license #{license_any}
|
||||
end
|
||||
RUBY
|
||||
fa = formula_auditor "cask", formula_text, spdx_license_data: spdx_license_data,
|
||||
online: true, core_tap: true, new_formula: true
|
||||
|
||||
fa.audit_license
|
||||
expect(fa.problems).to be_empty
|
||||
|
||||
@ -19,6 +19,9 @@ end.freeze
|
||||
# Paths pointing into the Homebrew code base that persist across test runs
|
||||
HOMEBREW_SHIMS_PATH = (HOMEBREW_LIBRARY_PATH/"shims").freeze
|
||||
|
||||
# Where external data that has been incorporated into Homebrew is stored
|
||||
HOMEBREW_DATA_PATH = (HOMEBREW_LIBRARY_PATH/"data").freeze
|
||||
|
||||
require "extend/git_repository"
|
||||
|
||||
# Paths redirected to a temporary directory and wiped at the end of the test run
|
||||
|
||||
@ -3,30 +3,337 @@
|
||||
require "utils/spdx"
|
||||
|
||||
describe SPDX do
|
||||
describe ".spdx_data" do
|
||||
describe ".license_data" do
|
||||
it "has the license list version" do
|
||||
expect(described_class.spdx_data["licenseListVersion"]).not_to eq(nil)
|
||||
expect(described_class.license_data["licenseListVersion"]).not_to eq(nil)
|
||||
end
|
||||
|
||||
it "has the release date" do
|
||||
expect(described_class.spdx_data["releaseDate"]).not_to eq(nil)
|
||||
expect(described_class.license_data["releaseDate"]).not_to eq(nil)
|
||||
end
|
||||
|
||||
it "has licenses" do
|
||||
expect(described_class.spdx_data["licenses"].length).not_to eq(0)
|
||||
expect(described_class.license_data["licenses"].length).not_to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
describe ".exception_data" do
|
||||
it "has the license list version" do
|
||||
expect(described_class.exception_data["licenseListVersion"]).not_to eq(nil)
|
||||
end
|
||||
|
||||
it "has the release date" do
|
||||
expect(described_class.exception_data["releaseDate"]).not_to eq(nil)
|
||||
end
|
||||
|
||||
it "has exceptions" do
|
||||
expect(described_class.exception_data["exceptions"].length).not_to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
describe ".download_latest_license_data!", :needs_network do
|
||||
let(:tmp_json_path) { Pathname.new("#{TEST_TMPDIR}/spdx.json") }
|
||||
let(:tmp_json_path) { Pathname.new(TEST_TMPDIR) }
|
||||
|
||||
after do
|
||||
FileUtils.rm tmp_json_path
|
||||
FileUtils.rm tmp_json_path/"spdx_licenses.json"
|
||||
FileUtils.rm tmp_json_path/"spdx_exceptions.json"
|
||||
end
|
||||
|
||||
it "downloads latest license data" do
|
||||
described_class.download_latest_license_data! to: tmp_json_path
|
||||
expect(tmp_json_path).to exist
|
||||
expect(tmp_json_path/"spdx_licenses.json").to exist
|
||||
expect(tmp_json_path/"spdx_exceptions.json").to exist
|
||||
end
|
||||
end
|
||||
|
||||
describe ".parse_license_expression" do
|
||||
it "returns a single license" do
|
||||
expect(described_class.parse_license_expression("MIT").first).to eq ["MIT"]
|
||||
end
|
||||
|
||||
it "returns a single license with plus" do
|
||||
expect(described_class.parse_license_expression("Apache-2.0+").first).to eq ["Apache-2.0+"]
|
||||
end
|
||||
|
||||
it "returns multiple licenses with :any" do
|
||||
expect(described_class.parse_license_expression(any_of: ["MIT", "0BSD"]).first).to eq ["MIT", "0BSD"]
|
||||
end
|
||||
|
||||
it "returns multiple licenses with :all" do
|
||||
expect(described_class.parse_license_expression(all_of: ["MIT", "0BSD"]).first).to eq ["MIT", "0BSD"]
|
||||
end
|
||||
|
||||
it "returns multiple licenses with plus" do
|
||||
expect(described_class.parse_license_expression(any_of: ["MIT", "EPL-1.0+"]).first).to eq ["MIT", "EPL-1.0+"]
|
||||
end
|
||||
|
||||
it "returns multiple licenses with array" do
|
||||
expect(described_class.parse_license_expression(["MIT", "EPL-1.0+"]).first).to eq ["MIT", "EPL-1.0+"]
|
||||
end
|
||||
|
||||
it "returns license and exception" do
|
||||
license_expression = { "MIT" => { with: "LLVM-exception" } }
|
||||
expect(described_class.parse_license_expression(license_expression)).to eq [["MIT"], ["LLVM-exception"]]
|
||||
end
|
||||
|
||||
it "returns licenses and exceptions for compex license expressions" do
|
||||
license_expression = { any_of: [
|
||||
"MIT",
|
||||
:public_domain,
|
||||
all_of: ["0BSD", "Zlib"],
|
||||
"curl" => { with: "LLVM-exception" },
|
||||
] }
|
||||
result = [["MIT", :public_domain, "curl", "0BSD", "Zlib"], ["LLVM-exception"]]
|
||||
expect(described_class.parse_license_expression(license_expression)).to eq result
|
||||
end
|
||||
|
||||
it "returns :public_domain" do
|
||||
expect(described_class.parse_license_expression(:public_domain).first).to eq [:public_domain]
|
||||
end
|
||||
end
|
||||
|
||||
describe ".valid_license?" do
|
||||
it "returns true for valid license identifier" do
|
||||
expect(described_class.valid_license?("MIT")).to eq true
|
||||
end
|
||||
|
||||
it "returns false for invalid license identifier" do
|
||||
expect(described_class.valid_license?("foo")).to eq false
|
||||
end
|
||||
|
||||
it "returns true for deprecated license identifier" do
|
||||
expect(described_class.valid_license?("GPL-1.0")).to eq true
|
||||
end
|
||||
|
||||
it "returns true for license identifier with plus" do
|
||||
expect(described_class.valid_license?("Apache-2.0+")).to eq true
|
||||
end
|
||||
|
||||
it "returns true for :public_domain" do
|
||||
expect(described_class.valid_license?(:public_domain)).to eq true
|
||||
end
|
||||
end
|
||||
|
||||
describe ".deprecated_license?" do
|
||||
it "returns true for deprecated license identifier" do
|
||||
expect(described_class.deprecated_license?("GPL-1.0")).to eq true
|
||||
end
|
||||
|
||||
it "returns false for non-deprecated license identifier" do
|
||||
expect(described_class.deprecated_license?("MIT")).to eq false
|
||||
end
|
||||
|
||||
it "returns false for invalid license identifier" do
|
||||
expect(described_class.deprecated_license?("foo")).to eq false
|
||||
end
|
||||
|
||||
it "returns false for :public_domain" do
|
||||
expect(described_class.deprecated_license?(:public_domain)).to eq false
|
||||
end
|
||||
end
|
||||
|
||||
describe ".valid_license_exception?" do
|
||||
it "returns true for valid license exception identifier" do
|
||||
expect(described_class.valid_license_exception?("LLVM-exception")).to eq true
|
||||
end
|
||||
|
||||
it "returns false for invalid license exception identifier" do
|
||||
expect(described_class.valid_license_exception?("foo")).to eq false
|
||||
end
|
||||
|
||||
it "returns false for deprecated license exception identifier" do
|
||||
expect(described_class.valid_license_exception?("Nokia-Qt-exception-1.1")).to eq false
|
||||
end
|
||||
end
|
||||
|
||||
describe ".license_expression_to_string" do
|
||||
it "returns a single license" do
|
||||
expect(described_class.license_expression_to_string("MIT")).to eq "MIT"
|
||||
end
|
||||
|
||||
it "returns a single license with plus" do
|
||||
expect(described_class.license_expression_to_string("Apache-2.0+")).to eq "Apache-2.0+"
|
||||
end
|
||||
|
||||
it "returns multiple licenses with :any" do
|
||||
expect(described_class.license_expression_to_string(any_of: ["MIT", "0BSD"])).to eq "MIT or 0BSD"
|
||||
end
|
||||
|
||||
it "returns multiple licenses with :all" do
|
||||
expect(described_class.license_expression_to_string(all_of: ["MIT", "0BSD"])).to eq "MIT and 0BSD"
|
||||
end
|
||||
|
||||
it "returns multiple licenses with plus" 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"
|
||||
end
|
||||
|
||||
it "returns licenses and exceptions for compex license expressions" do
|
||||
license_expression = { any_of: [
|
||||
"MIT",
|
||||
:public_domain,
|
||||
all_of: ["0BSD", "Zlib"],
|
||||
"curl" => { with: "LLVM-exception" },
|
||||
] }
|
||||
result = "MIT or Public Domain or (0BSD and Zlib) or (curl with LLVM-exception)"
|
||||
expect(described_class.license_expression_to_string(license_expression)).to eq result
|
||||
end
|
||||
|
||||
it "returns :public_domain" do
|
||||
expect(described_class.license_expression_to_string(:public_domain)).to eq "Public Domain"
|
||||
end
|
||||
end
|
||||
|
||||
describe ".license_version_info_info" do
|
||||
it "returns license without version" do
|
||||
expect(described_class.license_version_info("MIT")).to eq ["MIT"]
|
||||
end
|
||||
|
||||
it "returns :public_domain without version" do
|
||||
expect(described_class.license_version_info(:public_domain)).to eq [:public_domain]
|
||||
end
|
||||
|
||||
it "returns license with version" do
|
||||
expect(described_class.license_version_info("Apache-2.0")).to eq ["Apache", "2.0", false]
|
||||
end
|
||||
|
||||
it "returns license with version and plus" do
|
||||
expect(described_class.license_version_info("Apache-2.0+")).to eq ["Apache", "2.0", true]
|
||||
end
|
||||
|
||||
it "returns more complicated license with version" do
|
||||
expect(described_class.license_version_info("CC-BY-3.0-AT")).to eq ["CC-BY", "3.0", false]
|
||||
end
|
||||
|
||||
it "returns more complicated license with version and plus" do
|
||||
expect(described_class.license_version_info("CC-BY-3.0-AT+")).to eq ["CC-BY", "3.0", true]
|
||||
end
|
||||
|
||||
it "returns license with -only" do
|
||||
expect(described_class.license_version_info("GPL-3.0-only")).to eq ["GPL", "3.0", false]
|
||||
end
|
||||
|
||||
it "returns license with -or-later" do
|
||||
expect(described_class.license_version_info("GPL-3.0-or-later")).to eq ["GPL", "3.0", true]
|
||||
end
|
||||
end
|
||||
|
||||
describe ".licenses_forbid_installation?" do
|
||||
let(:mit_forbidden) { { "MIT" => described_class.license_version_info("MIT") } }
|
||||
let(:epl_1_forbidden) { { "EPL-1.0" => described_class.license_version_info("EPL-1.0") } }
|
||||
let(:epl_1_plus_forbidden) { { "EPL-1.0+" => described_class.license_version_info("EPL-1.0+") } }
|
||||
let(:multiple_forbidden) {
|
||||
{
|
||||
"MIT" => described_class.license_version_info("MIT"),
|
||||
"0BSD" => described_class.license_version_info("0BSD"),
|
||||
}
|
||||
}
|
||||
let(:any_of_license) { { any_of: ["MIT", "0BSD"] } }
|
||||
let(:license_array) { ["MIT", "0BSD"] }
|
||||
let(:all_of_license) { { all_of: ["MIT", "0BSD"] } }
|
||||
let(:nested_licenses) {
|
||||
{
|
||||
any_of: [
|
||||
"MIT",
|
||||
{ "MIT" => { with: "LLVM-exception" } },
|
||||
{ any_of: ["MIT", "0BSD"] },
|
||||
],
|
||||
}
|
||||
}
|
||||
let(:license_exception) { { "MIT" => { with: "LLVM-exception" } } }
|
||||
|
||||
it "allows installation with no forbidden licenses" do
|
||||
expect(described_class.licenses_forbid_installation?("MIT", {})).to eq false
|
||||
end
|
||||
|
||||
it "allows installation with non-forbidden license" do
|
||||
expect(described_class.licenses_forbid_installation?("0BSD", mit_forbidden)).to eq false
|
||||
end
|
||||
|
||||
it "forbids installation with forbidden license" do
|
||||
expect(described_class.licenses_forbid_installation?("MIT", mit_forbidden)).to eq true
|
||||
end
|
||||
|
||||
it "allows installation of later license version" do
|
||||
expect(described_class.licenses_forbid_installation?("EPL-2.0", epl_1_forbidden)).to eq false
|
||||
end
|
||||
|
||||
it "forbids installation of later license version with plus in forbidden license list" do
|
||||
expect(described_class.licenses_forbid_installation?("EPL-2.0", epl_1_plus_forbidden)).to eq true
|
||||
end
|
||||
|
||||
it "allows installation when one of the any_of licenses is allowed" do
|
||||
expect(described_class.licenses_forbid_installation?(any_of_license, mit_forbidden)).to eq false
|
||||
end
|
||||
|
||||
it "forbids installation when none of the any_of licenses are allowed" 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
|
||||
|
||||
it "allows installation with license + exception that aren't forbidden" do
|
||||
expect(described_class.licenses_forbid_installation?(license_exception, epl_1_forbidden)).to eq false
|
||||
end
|
||||
|
||||
it "forbids installation with license + exception that are't forbidden" do
|
||||
expect(described_class.licenses_forbid_installation?(license_exception, mit_forbidden)).to eq true
|
||||
end
|
||||
|
||||
it "allows installation with nested licenses with no forbidden licenses" do
|
||||
expect(described_class.licenses_forbid_installation?(nested_licenses, epl_1_forbidden)).to eq false
|
||||
end
|
||||
|
||||
it "allows installation with nested licenses when second hash item matches" do
|
||||
expect(described_class.licenses_forbid_installation?(nested_licenses, mit_forbidden)).to eq false
|
||||
end
|
||||
|
||||
it "forbids installation with nested licenses when all licenses are forbidden" do
|
||||
expect(described_class.licenses_forbid_installation?(nested_licenses, multiple_forbidden)).to eq true
|
||||
end
|
||||
end
|
||||
|
||||
describe ".forbidden_licenses_include?" do
|
||||
let(:mit_forbidden) { { "MIT" => described_class.license_version_info("MIT") } }
|
||||
let(:epl_1_forbidden) { { "EPL-1.0" => described_class.license_version_info("EPL-1.0") } }
|
||||
let(:epl_1_plus_forbidden) { { "EPL-1.0+" => described_class.license_version_info("EPL-1.0+") } }
|
||||
|
||||
it "returns false with no forbidden licenses" do
|
||||
expect(described_class.forbidden_licenses_include?("MIT", {})).to eq false
|
||||
end
|
||||
|
||||
it "returns false with no matching forbidden licenses" do
|
||||
expect(described_class.forbidden_licenses_include?("MIT", epl_1_forbidden)).to eq false
|
||||
end
|
||||
|
||||
it "returns true with matching license" do
|
||||
expect(described_class.forbidden_licenses_include?("MIT", mit_forbidden)).to eq true
|
||||
end
|
||||
|
||||
it "returns false with later version of forbidden license" do
|
||||
expect(described_class.forbidden_licenses_include?("EPL-2.0", epl_1_forbidden)).to eq false
|
||||
end
|
||||
|
||||
it "returns true with later version of forbidden license with later versions forbidden" do
|
||||
expect(described_class.forbidden_licenses_include?("EPL-2.0", epl_1_plus_forbidden)).to eq true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -5,19 +5,163 @@ require "utils/github"
|
||||
module SPDX
|
||||
module_function
|
||||
|
||||
JSON_PATH = (HOMEBREW_LIBRARY_PATH/"data/spdx.json").freeze
|
||||
DATA_PATH = (HOMEBREW_DATA_PATH/"spdx").freeze
|
||||
API_URL = "https://api.github.com/repos/spdx/license-list-data/releases/latest"
|
||||
|
||||
def spdx_data
|
||||
@spdx_data ||= JSON.parse(JSON_PATH.read)
|
||||
def license_data
|
||||
@license_data ||= JSON.parse (DATA_PATH/"spdx_licenses.json").read
|
||||
end
|
||||
|
||||
def exception_data
|
||||
@exception_data ||= JSON.parse (DATA_PATH/"spdx_exceptions.json").read
|
||||
end
|
||||
|
||||
def latest_tag
|
||||
@latest_tag ||= GitHub.open_api(API_URL)["tag_name"]
|
||||
end
|
||||
|
||||
def download_latest_license_data!(to: JSON_PATH)
|
||||
data_url = "https://raw.githubusercontent.com/spdx/license-list-data/#{latest_tag}/json/licenses.json"
|
||||
curl_download(data_url, to: to, partial: false)
|
||||
def download_latest_license_data!(to: DATA_PATH)
|
||||
data_url = "https://raw.githubusercontent.com/spdx/license-list-data/#{latest_tag}/json/"
|
||||
curl_download("#{data_url}licenses.json", to: to/"spdx_licenses.json", partial: false)
|
||||
curl_download("#{data_url}exceptions.json", to: to/"spdx_exceptions.json", partial: false)
|
||||
end
|
||||
|
||||
def parse_license_expression(license_expression)
|
||||
licenses = []
|
||||
exceptions = []
|
||||
|
||||
case license_expression
|
||||
when String, Symbol
|
||||
licenses.push license_expression
|
||||
when Hash, Array
|
||||
if license_expression.is_a? Hash
|
||||
license_expression = license_expression.map do |key, value|
|
||||
if key.is_a? String
|
||||
licenses.push key
|
||||
exceptions.push value[:with]
|
||||
next
|
||||
end
|
||||
value
|
||||
end.compact
|
||||
end
|
||||
|
||||
license_expression.each do |license|
|
||||
sub_license, sub_exception = parse_license_expression license
|
||||
licenses += sub_license
|
||||
exceptions += sub_exception
|
||||
end
|
||||
end
|
||||
|
||||
[licenses, exceptions]
|
||||
end
|
||||
|
||||
def valid_license?(license)
|
||||
return true if license == :public_domain
|
||||
|
||||
license = license.delete_suffix "+"
|
||||
license_data["licenses"].any? { |spdx_license| spdx_license["licenseId"] == license }
|
||||
end
|
||||
|
||||
def deprecated_license?(license)
|
||||
return false if license == :public_domain
|
||||
return false unless valid_license?(license)
|
||||
|
||||
license_data["licenses"].none? do |spdx_license|
|
||||
spdx_license["licenseId"] == license && !spdx_license["isDeprecatedLicenseId"]
|
||||
end
|
||||
end
|
||||
|
||||
def valid_license_exception?(exception)
|
||||
exception_data["exceptions"].any? do |spdx_exception|
|
||||
spdx_exception["licenseExceptionId"] == exception && !spdx_exception["isDeprecatedLicenseId"]
|
||||
end
|
||||
end
|
||||
|
||||
def license_expression_to_string(license_expression, bracket: false, hash_type: nil)
|
||||
case license_expression
|
||||
when String
|
||||
license_expression
|
||||
when :public_domain
|
||||
"Public Domain"
|
||||
when Hash, Array
|
||||
license_expression = { any_of: license_expression } if license_expression.is_a? Array
|
||||
expressions = []
|
||||
|
||||
if license_expression.keys.length == 1
|
||||
hash_type = license_expression.keys.first
|
||||
if hash_type.is_a? String
|
||||
expressions.push "#{hash_type} with #{license_expression[hash_type][:with]}"
|
||||
else
|
||||
expressions += license_expression[hash_type].map do |license|
|
||||
license_expression_to_string license, bracket: true, hash_type: hash_type
|
||||
end
|
||||
end
|
||||
else
|
||||
bracket = false
|
||||
license_expression.each do |expression|
|
||||
expressions.push license_expression_to_string(Hash[*expression], bracket: true)
|
||||
end
|
||||
end
|
||||
|
||||
operator = if hash_type == :any_of
|
||||
" or "
|
||||
else
|
||||
" and "
|
||||
end
|
||||
|
||||
if bracket
|
||||
"(#{expressions.join operator})"
|
||||
else
|
||||
expressions.join operator
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def license_version_info(license)
|
||||
return [license] if license == :public_domain
|
||||
|
||||
match = license.match(/-(?<version>[0-9.]+)(?:-.*?)??(?<or_later>\+|-only|-or-later)?$/)
|
||||
return [license] if match.blank?
|
||||
|
||||
license_name = license.split(match[0]).first
|
||||
or_later = match["or_later"].present? && %w[+ -or-later].include?(match["or_later"])
|
||||
|
||||
# [name, version, later versions allowed?]
|
||||
# e.g. GPL-2.0-or-later --> ["GPL", "2.0", true]
|
||||
[license_name, match["version"], or_later]
|
||||
end
|
||||
|
||||
def licenses_forbid_installation?(license_expression, forbidden_licenses)
|
||||
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
|
||||
key = license_expression.keys.first
|
||||
case key
|
||||
when :any_of
|
||||
license_expression[key].all? { |license| licenses_forbid_installation? license, forbidden_licenses }
|
||||
when :all_of
|
||||
license_expression[key].any? { |license| licenses_forbid_installation? license, forbidden_licenses }
|
||||
else
|
||||
forbidden_licenses_include? key, forbidden_licenses
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def forbidden_licenses_include?(license, forbidden_licenses)
|
||||
return true if forbidden_licenses.key? license
|
||||
|
||||
name, version, = license_version_info license
|
||||
|
||||
forbidden_licenses.each do |_, license_info|
|
||||
forbidden_name, forbidden_version, forbidden_or_later = *license_info
|
||||
next unless forbidden_name == name
|
||||
|
||||
return true if forbidden_or_later && forbidden_version <= version
|
||||
|
||||
return true if forbidden_version == version
|
||||
end
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user