commands: add spack license update-copyright-year
				
					
				
			This adds a new subcommand to `spack license` that automatically updates the copyright year in files that should have a license header. - [x] add `spack license update-copyright-year` command - [x] add test
This commit is contained in:
		@@ -9,6 +9,7 @@
 | 
				
			|||||||
import re
 | 
					import re
 | 
				
			||||||
from collections import defaultdict
 | 
					from collections import defaultdict
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import llnl.util.filesystem as fs
 | 
				
			||||||
import llnl.util.tty as tty
 | 
					import llnl.util.tty as tty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import spack.paths
 | 
					import spack.paths
 | 
				
			||||||
@@ -77,15 +78,15 @@ def _all_spack_files(root=spack.paths.prefix):
 | 
				
			|||||||
                visited.add(path)
 | 
					                visited.add(path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _licensed_files(root=spack.paths.prefix):
 | 
					def _licensed_files(args):
 | 
				
			||||||
    for relpath in _all_spack_files(root):
 | 
					    for relpath in _all_spack_files(args.root):
 | 
				
			||||||
        if any(regex.match(relpath) for regex in licensed_files):
 | 
					        if any(regex.match(relpath) for regex in licensed_files):
 | 
				
			||||||
            yield relpath
 | 
					            yield relpath
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def list_files(args):
 | 
					def list_files(args):
 | 
				
			||||||
    """list files in spack that should have license headers"""
 | 
					    """list files in spack that should have license headers"""
 | 
				
			||||||
    for relpath in sorted(_licensed_files()):
 | 
					    for relpath in sorted(_licensed_files(args)):
 | 
				
			||||||
        print(os.path.join(spack.paths.spack_root, relpath))
 | 
					        print(os.path.join(spack.paths.spack_root, relpath))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -93,6 +94,8 @@ def list_files(args):
 | 
				
			|||||||
# bool(value) evaluates to True
 | 
					# bool(value) evaluates to True
 | 
				
			||||||
OLD_LICENSE, SPDX_MISMATCH, GENERAL_MISMATCH = range(1, 4)
 | 
					OLD_LICENSE, SPDX_MISMATCH, GENERAL_MISMATCH = range(1, 4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					strict_date = r'Copyright 2013-2021'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LicenseError(object):
 | 
					class LicenseError(object):
 | 
				
			||||||
    def __init__(self):
 | 
					    def __init__(self):
 | 
				
			||||||
@@ -118,17 +121,15 @@ def error_messages(self):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
def _check_license(lines, path):
 | 
					def _check_license(lines, path):
 | 
				
			||||||
    license_lines = [
 | 
					    license_lines = [
 | 
				
			||||||
        r'Copyright 2013-(?:201[789]|202\d) Lawrence Livermore National Security, LLC and other',  # noqa: E501
 | 
					        r'Copyright 2013-(?:202[01]) Lawrence Livermore National Security, LLC and other',  # noqa: E501
 | 
				
			||||||
        r'Spack Project Developers\. See the top-level COPYRIGHT file for details.',  # noqa: E501
 | 
					        r'Spack Project Developers\. See the top-level COPYRIGHT file for details.',  # noqa: E501
 | 
				
			||||||
        r'SPDX-License-Identifier: \(Apache-2\.0 OR MIT\)'
 | 
					        r'SPDX-License-Identifier: \(Apache-2\.0 OR MIT\)'
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    strict_date = r'Copyright 2013-2020'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    found = []
 | 
					    found = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for line in lines:
 | 
					    for line in lines:
 | 
				
			||||||
        line = re.sub(r'^[\s#\.]*', '', line)
 | 
					        line = re.sub(r'^[\s#\%\.]*', '', line)
 | 
				
			||||||
        line = line.rstrip()
 | 
					        line = line.rstrip()
 | 
				
			||||||
        for i, license_line in enumerate(license_lines):
 | 
					        for i, license_line in enumerate(license_lines):
 | 
				
			||||||
            if re.match(license_line, line):
 | 
					            if re.match(license_line, line):
 | 
				
			||||||
@@ -175,7 +176,7 @@ def verify(args):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    license_errors = LicenseError()
 | 
					    license_errors = LicenseError()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for relpath in _licensed_files(args.root):
 | 
					    for relpath in _licensed_files(args):
 | 
				
			||||||
        path = os.path.join(args.root, relpath)
 | 
					        path = os.path.join(args.root, relpath)
 | 
				
			||||||
        with open(path) as f:
 | 
					        with open(path) as f:
 | 
				
			||||||
            lines = [line for line in f][:license_lines]
 | 
					            lines = [line for line in f][:license_lines]
 | 
				
			||||||
@@ -190,15 +191,28 @@ def verify(args):
 | 
				
			|||||||
        tty.msg('No license issues found.')
 | 
					        tty.msg('No license issues found.')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def setup_parser(subparser):
 | 
					def update_copyright_year(args):
 | 
				
			||||||
    sp = subparser.add_subparsers(metavar='SUBCOMMAND', dest='license_command')
 | 
					    """update copyright for the current year in all licensed files"""
 | 
				
			||||||
    sp.add_parser('list-files', help=list_files.__doc__)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    verify_parser = sp.add_parser('verify', help=verify.__doc__)
 | 
					    llns_and_other = ' Lawrence Livermore National Security, LLC and other'
 | 
				
			||||||
    verify_parser.add_argument(
 | 
					    for filename in _licensed_files(args):
 | 
				
			||||||
 | 
					        fs.filter_file(
 | 
				
			||||||
 | 
					            r'Copyright \d{4}-\d{4}' + llns_and_other,
 | 
				
			||||||
 | 
					            strict_date + llns_and_other,
 | 
				
			||||||
 | 
					            os.path.join(args.root, filename)
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def setup_parser(subparser):
 | 
				
			||||||
 | 
					    subparser.add_argument(
 | 
				
			||||||
        '--root', action='store', default=spack.paths.prefix,
 | 
					        '--root', action='store', default=spack.paths.prefix,
 | 
				
			||||||
        help='scan a different prefix for license issues')
 | 
					        help='scan a different prefix for license issues')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sp = subparser.add_subparsers(metavar='SUBCOMMAND', dest='license_command')
 | 
				
			||||||
 | 
					    sp.add_parser('list-files', help=list_files.__doc__)
 | 
				
			||||||
 | 
					    sp.add_parser('verify', help=verify.__doc__)
 | 
				
			||||||
 | 
					    sp.add_parser('update-copyright-year', help=update_copyright_year.__doc__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def license(parser, args):
 | 
					def license(parser, args):
 | 
				
			||||||
    if not git:
 | 
					    if not git:
 | 
				
			||||||
@@ -209,5 +223,6 @@ def license(parser, args):
 | 
				
			|||||||
    commands = {
 | 
					    commands = {
 | 
				
			||||||
        'list-files': list_files,
 | 
					        'list-files': list_files,
 | 
				
			||||||
        'verify': verify,
 | 
					        'verify': verify,
 | 
				
			||||||
 | 
					        'update-copyright-year': update_copyright_year,
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return commands[args.license_command](args)
 | 
					    return commands[args.license_command](args)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
# Copyright 2013-2020 Lawrence Livermore National Security, LLC and other
 | 
					# Copyright 2013-2021 Lawrence Livermore National Security, LLC and other
 | 
				
			||||||
# Spack Project Developers. See the top-level COPYRIGHT file for details.
 | 
					# Spack Project Developers. See the top-level COPYRIGHT file for details.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
 | 
					# SPDX-License-Identifier: (Apache-2.0 OR MIT)
 | 
				
			||||||
@@ -8,6 +8,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from llnl.util.filesystem import touch, mkdirp
 | 
					from llnl.util.filesystem import touch, mkdirp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import spack.cmd.license
 | 
				
			||||||
import spack.paths
 | 
					import spack.paths
 | 
				
			||||||
from spack.main import SpackCommand
 | 
					from spack.main import SpackCommand
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -31,7 +32,7 @@ def test_verify(tmpdir):
 | 
				
			|||||||
    lgpl_header = source_dir.join('lgpl_header.py')
 | 
					    lgpl_header = source_dir.join('lgpl_header.py')
 | 
				
			||||||
    with lgpl_header.open('w') as f:
 | 
					    with lgpl_header.open('w') as f:
 | 
				
			||||||
        f.write("""\
 | 
					        f.write("""\
 | 
				
			||||||
# Copyright 2013-2020 Lawrence Livermore National Security, LLC and other
 | 
					# Copyright 2013-2021 Lawrence Livermore National Security, LLC and other
 | 
				
			||||||
# Spack Project Developers. See the top-level COPYRIGHT file for details.
 | 
					# Spack Project Developers. See the top-level COPYRIGHT file for details.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# SPDX-License-Identifier: LGPL-2.1-only
 | 
					# SPDX-License-Identifier: LGPL-2.1-only
 | 
				
			||||||
@@ -48,13 +49,13 @@ def test_verify(tmpdir):
 | 
				
			|||||||
    correct_header = source_dir.join('correct_header.py')
 | 
					    correct_header = source_dir.join('correct_header.py')
 | 
				
			||||||
    with correct_header.open('w') as f:
 | 
					    with correct_header.open('w') as f:
 | 
				
			||||||
        f.write("""\
 | 
					        f.write("""\
 | 
				
			||||||
# Copyright 2013-2020 Lawrence Livermore National Security, LLC and other
 | 
					# Copyright 2013-2021 Lawrence Livermore National Security, LLC and other
 | 
				
			||||||
# Spack Project Developers. See the top-level COPYRIGHT file for details.
 | 
					# Spack Project Developers. See the top-level COPYRIGHT file for details.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
 | 
					# SPDX-License-Identifier: (Apache-2.0 OR MIT)
 | 
				
			||||||
""")
 | 
					""")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    out = license('verify', '--root', str(tmpdir), fail_on_error=False)
 | 
					    out = license('--root', str(tmpdir), 'verify', fail_on_error=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    assert str(no_header) in out
 | 
					    assert str(no_header) in out
 | 
				
			||||||
    assert str(lgpl_header) in out
 | 
					    assert str(lgpl_header) in out
 | 
				
			||||||
@@ -66,3 +67,28 @@ def test_verify(tmpdir):
 | 
				
			|||||||
    assert re.search(r'files with old license header:\s*1', out)
 | 
					    assert re.search(r'files with old license header:\s*1', out)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    assert license.returncode == 1
 | 
					    assert license.returncode == 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_update_copyright_year(tmpdir):
 | 
				
			||||||
 | 
					    source_dir = tmpdir.join('lib', 'spack', 'spack')
 | 
				
			||||||
 | 
					    mkdirp(str(source_dir))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    years = list(range(2018, 2021))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for year in years:
 | 
				
			||||||
 | 
					        outdated = source_dir.join('header_%d.py' % year)
 | 
				
			||||||
 | 
					        with outdated.open('w') as f:
 | 
				
			||||||
 | 
					            f.write("""\
 | 
				
			||||||
 | 
					# Copyright 2013-%d Lawrence Livermore National Security, LLC and other
 | 
				
			||||||
 | 
					# Spack Project Developers. See the top-level COPYRIGHT file for details.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: (Apache-2.0 OR MIT)
 | 
				
			||||||
 | 
					""" % year)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    license('--root', str(tmpdir), 'update-copyright-year')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for year in years:
 | 
				
			||||||
 | 
					        outdated = source_dir.join('header_%d.py' % year)
 | 
				
			||||||
 | 
					        first_line = outdated.open().read().split("\n")[0]
 | 
				
			||||||
 | 
					        assert str(year) not in first_line
 | 
				
			||||||
 | 
					        assert spack.cmd.license.strict_date in first_line
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1042,9 +1042,9 @@ _spack_install() {
 | 
				
			|||||||
_spack_license() {
 | 
					_spack_license() {
 | 
				
			||||||
    if $list_options
 | 
					    if $list_options
 | 
				
			||||||
    then
 | 
					    then
 | 
				
			||||||
        SPACK_COMPREPLY="-h --help"
 | 
					        SPACK_COMPREPLY="-h --help --root"
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
        SPACK_COMPREPLY="list-files verify"
 | 
					        SPACK_COMPREPLY="list-files verify update-copyright-year"
 | 
				
			||||||
    fi
 | 
					    fi
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1053,7 +1053,11 @@ _spack_license_list_files() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
_spack_license_verify() {
 | 
					_spack_license_verify() {
 | 
				
			||||||
    SPACK_COMPREPLY="-h --help --root"
 | 
					    SPACK_COMPREPLY="-h --help"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_spack_license_update_copyright_year() {
 | 
				
			||||||
 | 
					    SPACK_COMPREPLY="-h --help"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
_spack_list() {
 | 
					_spack_list() {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user