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:
Todd Gamblin 2021-01-01 23:09:18 -08:00
parent 4d94c4c460
commit 78f39bdfee
3 changed files with 65 additions and 20 deletions

View File

@ -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)

View File

@ -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

View File

@ -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() {