Commit d5ab303d authored by Michael Pöhn's avatar Michael Pöhn

lint: license override config option + require FSF/OSI approved licenses by default

parent 3c9535d6
......@@ -330,3 +330,20 @@ The repository of older versions of applications from the main demo repository.
# 'com.facebook.orca',
# 'com.android.vending',
# )
# `fdroid lint` checks licenses in metadata against a built white list. By
# default we will require license metadata to be present and only allow
# licenses approved either by FSF or OSI. We're using the standardized SPDX
# license IDs. (https://spdx.org/licenses/)
#
# We use `python3 -m spdx-license-list print --filter-fsf-or-osi` for
# generating our default list. (https://pypi.org/project/spdx-license-list)
#
# You can override our default list of allowed licenes by setting this option.
# Just supply a custom list of licene names you would like to allow. Setting
# this to `None` disables this lint check.
#
# lint_licenses = (
# 'Custom-License-A',
# 'Another-License',
# )
......@@ -57,6 +57,7 @@ from pyasn1_modules import rfc2315
from pyasn1.error import PyAsn1Error
import fdroidserver.metadata
import fdroidserver.lint
from fdroidserver import _
from fdroidserver.exception import FDroidException, VCSException, NoSubmodulesException,\
BuildException, VerificationException
......@@ -145,6 +146,7 @@ default_config = {
using the tools on https://gitlab.com/u/fdroid.
''',
'archive_older': 0,
'lint_licenses': fdroidserver.lint.APPROVED_LICENSES,
}
......
......@@ -440,10 +440,17 @@ def check_format(app):
def check_license_tag(app):
'''Ensure all license tags are in https://spdx.org/license-list'''
if app.License.rstrip('+') not in FSF_APPROVED_SPDX:
yield _('Unexpected license tag "{}"! Use only FSF approved tags '
'from https://spdx.org/license-list').format(app.License)
'''Ensure all license tags contain only valid/approved values'''
if config['lint_licenses'] is None:
return
if app.License not in config['lint_licenses']:
if config['lint_licenses'] == APPROVED_LICENSES:
yield _('Unexpected license tag "{}"! Only use FSF or OSI '
'approved tags from https://spdx.org/license-list') \
.format(app.License)
else:
yield _('Unexpected license tag "{}"! Only use license tags '
'configured in your config file').format(app.License)
def check_extlib_dir(apps):
......@@ -622,38 +629,175 @@ def main():
sys.exit(1)
# A compiled, public domain list of official SPDX license tags.
# generated using: `python3 -m spdx_license_list print --filter-fsf`
# Only contains licenes approved by FSF to be libre/free software
FSF_APPROVED_SPDX = ['AFL-1.1', 'AFL-1.2', 'AFL-2.0', 'AFL-2.1', 'AFL-3.0',
'AGPL-3.0-only', 'AGPL-3.0-or-later', 'APSL-2.0',
'Apache-1.0', 'Apache-1.1', 'Apache-2.0', 'Artistic-2.0',
'BSD-2-Clause-FreeBSD', 'BSD-3-Clause',
'BSD-3-Clause-Clear', 'BSD-4-Clause', 'BSL-1.0',
'BitTorrent-1.1', 'CC-BY-4.0', 'CC-BY-SA-4.0', 'CC0-1.0',
'CDDL-1.0', 'CECILL-2.0', 'CECILL-B', 'CECILL-C',
'CPAL-1.0', 'CPL-1.0', 'ClArtistic', 'Condor-1.1',
'ECL-2.0', 'EFL-2.0', 'EPL-1.0', 'EPL-2.0', 'EUDatagrid',
'EUPL-1.1', 'EUPL-1.2', 'FSFAP', 'FTL', 'GFDL-1.1-only',
'GFDL-1.1-or-later', 'GFDL-1.2-only', 'GFDL-1.2-or-later',
'GFDL-1.3-only', 'GFDL-1.3-or-later', 'GPL-2.0-only',
'GPL-2.0-or-later', 'GPL-3.0-only', 'GPL-3.0-or-later',
'HPND', 'IJG', 'IPA', 'IPL-1.0', 'ISC', 'Imlib2', 'Intel',
'LGPL-2.1-only', 'LGPL-2.1-or-later', 'LGPL-3.0-only',
'LGPL-3.0-or-later', 'LPL-1.02', 'LPPL-1.2', 'LPPL-1.3a',
'MIT', 'MPL-1.1', 'MPL-2.0', 'MS-PL', 'MS-RL', 'NCSA',
'NOSL', 'NPL-1.0', 'NPL-1.1', 'Nokia', 'ODbL-1.0',
'OFL-1.0', 'OFL-1.1', 'OLDAP-2.3', 'OLDAP-2.7', 'OSL-1.0',
'OSL-1.1', 'OSL-2.0', 'OSL-2.1', 'OSL-3.0', 'OpenSSL',
'PHP-3.01', 'Python-2.0', 'QPL-1.0', 'RPSL-1.0', 'Ruby',
'SGI-B-2.0', 'SISSL', 'SMLNJ', 'SPL-1.0', 'Sleepycat',
'UPL-1.0', 'Unlicense', 'Vim', 'W3C', 'WTFPL', 'X11',
'XFree86-1.1', 'YPL-1.1', 'ZPL-2.0', 'ZPL-2.1',
'Zend-2.0', 'Zimbra-1.3', 'Zlib', 'gnuplot', 'iMatix',
'xinetd']
# A compiled, public domain list of official SPDX license tags. generated
# using: `python3 -m spdx_license_list print --filter-fsf-or-osi` Only contains
# licenes approved by either FSF to be free/libre software or OSI to be open
# source
APPROVED_LICENSES = [
'0BSD',
'AAL',
'AFL-1.1',
'AFL-1.2',
'AFL-2.0',
'AFL-2.1',
'AFL-3.0',
'AGPL-3.0-only',
'AGPL-3.0-or-later',
'APL-1.0',
'APSL-1.0',
'APSL-1.1',
'APSL-1.2',
'APSL-2.0',
'Apache-1.0',
'Apache-1.1',
'Apache-2.0',
'Artistic-1.0',
'Artistic-1.0-Perl',
'Artistic-1.0-cl8',
'Artistic-2.0',
'BSD-2-Clause',
'BSD-2-Clause-FreeBSD',
'BSD-2-Clause-Patent',
'BSD-3-Clause',
'BSD-3-Clause-Clear',
'BSD-3-Clause-LBNL',
'BSD-4-Clause',
'BSL-1.0',
'BitTorrent-1.1',
'CATOSL-1.1',
'CC-BY-4.0',
'CC-BY-SA-4.0',
'CC0-1.0',
'CDDL-1.0',
'CECILL-2.0',
'CECILL-2.1',
'CECILL-B',
'CECILL-C',
'CNRI-Python',
'CPAL-1.0',
'CPL-1.0',
'CUA-OPL-1.0',
'ClArtistic',
'Condor-1.1',
'ECL-1.0',
'ECL-2.0',
'EFL-1.0',
'EFL-2.0',
'EPL-1.0',
'EPL-2.0',
'EUDatagrid',
'EUPL-1.1',
'EUPL-1.2',
'Entessa',
'FSFAP',
'FTL',
'Fair',
'Frameworx-1.0',
'GFDL-1.1-only',
'GFDL-1.1-or-later',
'GFDL-1.2-only',
'GFDL-1.2-or-later',
'GFDL-1.3-only',
'GFDL-1.3-or-later',
'GPL-2.0-only',
'GPL-2.0-or-later',
'GPL-3.0-only',
'GPL-3.0-or-later',
'HPND',
'IJG',
'IPA',
'IPL-1.0',
'ISC',
'Imlib2',
'Intel',
'LGPL-2.0-only',
'LGPL-2.0-or-later',
'LGPL-2.1-only',
'LGPL-2.1-or-later',
'LGPL-3.0-only',
'LGPL-3.0-or-later',
'LPL-1.0',
'LPL-1.02',
'LPPL-1.2',
'LPPL-1.3a',
'LPPL-1.3c',
'LiLiQ-P-1.1',
'LiLiQ-R-1.1',
'LiLiQ-Rplus-1.1',
'MIT',
'MIT-0',
'MPL-1.0',
'MPL-1.1',
'MPL-2.0',
'MPL-2.0-no-copyleft-exception',
'MS-PL',
'MS-RL',
'MirOS',
'Motosoto',
'Multics',
'NASA-1.3',
'NCSA',
'NGPL',
'NOSL',
'NPL-1.0',
'NPL-1.1',
'NPOSL-3.0',
'NTP',
'Naumen',
'Nokia',
'OCLC-2.0',
'ODbL-1.0',
'OFL-1.0',
'OFL-1.1',
'OGTSL',
'OLDAP-2.3',
'OLDAP-2.7',
'OSET-PL-2.1',
'OSL-1.0',
'OSL-1.1',
'OSL-2.0',
'OSL-2.1',
'OSL-3.0',
'OpenSSL',
'PHP-3.0',
'PHP-3.01',
'PostgreSQL',
'Python-2.0',
'QPL-1.0',
'RPL-1.1',
'RPL-1.5',
'RPSL-1.0',
'RSCPL',
'Ruby',
'SGI-B-2.0',
'SISSL',
'SMLNJ',
'SPL-1.0',
'SimPL-2.0',
'Sleepycat',
'UPL-1.0',
'Unlicense',
'VSL-1.0',
'Vim',
'W3C',
'WTFPL',
'Watcom-1.0',
'X11',
'XFree86-1.1',
'Xnet',
'YPL-1.1',
'ZPL-2.0',
'ZPL-2.1',
'Zend-2.0',
'Zimbra-1.3',
'Zlib',
'gnuplot',
'iMatix',
'xinetd',
]
# an F-Droid addition, until we can enforce a better option
FSF_APPROVED_SPDX.append("PublicDomain")
APPROVED_LICENSES.append("PublicDomain")
if __name__ == "__main__":
main()
......@@ -179,6 +179,100 @@ class LintTest(unittest.TestCase):
logging.debug(warn)
self.assertTrue(anywarns)
def test_check_license_tag_no_custom_pass(self):
config = dict()
fdroidserver.common.fill_config_defaults(config)
fdroidserver.common.config = config
fdroidserver.lint.config = config
app = fdroidserver.metadata.App()
app.License = "GPL-3.0-or-later"
anywarns = False
for warn in fdroidserver.lint.check_license_tag(app):
anywarns = True
logging.debug(warn)
self.assertFalse(anywarns)
def test_check_license_tag_no_custom_fail(self):
config = dict()
fdroidserver.common.fill_config_defaults(config)
fdroidserver.common.config = config
fdroidserver.lint.config = config
app = fdroidserver.metadata.App()
app.License = "Adobe-2006"
anywarns = False
for warn in fdroidserver.lint.check_license_tag(app):
anywarns = True
logging.debug(warn)
self.assertTrue(anywarns)
def test_check_license_tag_with_custom_pass(self):
config = dict()
fdroidserver.common.fill_config_defaults(config)
fdroidserver.common.config = config
fdroidserver.lint.config = config
config['lint_licenses'] = ['fancy-license', 'GPL-3.0-or-later']
app = fdroidserver.metadata.App()
app.License = "fancy-license"
anywarns = False
for warn in fdroidserver.lint.check_license_tag(app):
anywarns = True
logging.debug(warn)
self.assertFalse(anywarns)
def test_check_license_tag_with_custom_fail(self):
config = dict()
fdroidserver.common.fill_config_defaults(config)
fdroidserver.common.config = config
fdroidserver.lint.config = config
config['lint_licenses'] = ['fancy-license', 'GPL-3.0-or-later']
app = fdroidserver.metadata.App()
app.License = "Apache-2.0"
anywarns = False
for warn in fdroidserver.lint.check_license_tag(app):
anywarns = True
logging.debug(warn)
self.assertTrue(anywarns)
def test_check_license_tag_with_custom_empty(self):
config = dict()
fdroidserver.common.fill_config_defaults(config)
fdroidserver.common.config = config
fdroidserver.lint.config = config
config['lint_licenses'] = []
app = fdroidserver.metadata.App()
app.License = "Apache-2.0"
anywarns = False
for warn in fdroidserver.lint.check_license_tag(app):
anywarns = True
logging.debug(warn)
self.assertTrue(anywarns)
def test_check_license_tag_disabled(self):
config = dict()
fdroidserver.common.fill_config_defaults(config)
fdroidserver.common.config = config
fdroidserver.lint.config = config
config['lint_licenses'] = None
app = fdroidserver.metadata.App()
app.License = "Apache-2.0"
anywarns = False
for warn in fdroidserver.lint.check_license_tag(app):
anywarns = True
logging.debug(warn)
self.assertFalse(anywarns)
if __name__ == "__main__":
os.chdir(os.path.dirname(__file__))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment