From 85cdbdf408c57650e43d3e36f0786aa4b772bf26 Mon Sep 17 00:00:00 2001 From: Dean Troyer Date: Thu, 13 Sep 2018 15:26:24 -0500 Subject: [PATCH] Add support for detecting SPDX license headers SPDX license headers are recognized as a short form of reference to specific licenses. [https://spdx.org/licenses/] StarlingX is actively using this form, specifically to replace the verbose Apache 2.0 header. H102 and H103 have been modified to recognize the following form as a valid Apache 2.0 licene header: # SPDX-License-Identifier: Apache-2.0 Change-Id: I7182fcabc6902f2d1d3a82865de49af94726f31d Signed-off-by: Dean Troyer --- HACKING.rst | 5 + hacking/checks/comments.py | 7 +- hacking/tests/checks/test_comments.py | 139 ++++++++++++++++++++++++++ 3 files changed, 149 insertions(+), 2 deletions(-) diff --git a/HACKING.rst b/HACKING.rst index 0deb2481..fa0cc018 100644 --- a/HACKING.rst +++ b/HACKING.rst @@ -373,6 +373,11 @@ OpenStack Licensing # License for the specific language governing permissions and limitations # under the License. + Alternately also check for the + `SPDX license header `__ for Apache 2.0:: + + # SPDX-License-Identifier: Apache-2.0 + - [H104] Files with no code shouldn't contain any license header nor comments, and must be left completely empty. diff --git a/hacking/checks/comments.py b/hacking/checks/comments.py index 96eaa8fa..e923e1fb 100644 --- a/hacking/checks/comments.py +++ b/hacking/checks/comments.py @@ -65,6 +65,8 @@ def hacking_has_license(physical_line, filename, lines, line_number): # header if 0 <= line.find('Licensed under the Apache License') < 10: license_found = True + if 0 <= line.find('SPDX-License-Identifier:') < 10: + license_found = True if not license_found: return (0, "H102: Apache 2.0 license header not found") @@ -86,8 +88,9 @@ def hacking_has_correct_license(physical_line, filename, lines, line_number): column = line.find('Licensed under the Apache License') if (0 < column < 10 and not _check_for_exact_apache(idx, lines)): - return (column, "H103: Header does not match Apache 2.0 " - "License notice") + if (line.find('SPDX-License-Identifier: Apache-2.0') <= 0): + return (column, "H103: Header does not match Apache 2.0 " + "License notice") EMPTY_LINE_RE = re.compile("^\s*(#.*|$)") diff --git a/hacking/tests/checks/test_comments.py b/hacking/tests/checks/test_comments.py index 5e2d7113..f837ee24 100644 --- a/hacking/tests/checks/test_comments.py +++ b/hacking/tests/checks/test_comments.py @@ -18,6 +18,145 @@ from hacking import tests class CoreTestCase(tests.TestCase): + def test_H102_none(self): + """Verify that the H102 check finds an SPDX header""" + self.assertEqual( + (0, 'H102: Apache 2.0 license header not found'), + comments.hacking_has_license( + None, + None, + [ + '# foo', + '# bar', + '# foo', + '# bar', + '# foo', + '# bar', + '# foo', + '# bar', + '# foo', + '# bar', + '# foo', + '# bar', + ], + 1, + ), + ) + + def test_H102_full(self): + """Verify that the H102 check finds an SPDX header""" + self.assertIsNone(comments.hacking_has_license( + None, + None, + [ + '# foo', + '# Licensed under the Apache License, Version 2.0', + '# foo', + '# bar', + '# foo', + '# bar', + '# foo', + '# bar', + '# foo', + '# bar', + '# foo', + '# bar', + ], + 1, + )) + + def test_H102_SPDX(self): + """Verify that the H102 check finds an SPDX header""" + self.assertIsNone(comments.hacking_has_license( + None, + None, + [ + '# foo', + '# SPDX-License-Identifier: Apache-2.0', + '# foo', + '# bar', + '# foo', + '# bar', + '# foo', + '# bar', + '# foo', + '# bar', + '# foo', + '# bar', + ], + 1, + )) + + def test_H103_full_fail(self): + """Verify that the H103 check finds an SPDX header""" + self.assertEqual( + (2, 'H103: Header does not match Apache 2.0 License notice'), + comments.hacking_has_correct_license( + None, + None, + [ + '# foo', + '# Licensed under the Apache License, Version 2.0', + '# foo', + '# bar', + '# foo', + '# bar', + '# foo', + '# bar', + '# foo', + '# bar', + '# foo', + '# bar', + ], + 1, + ), + ) + + def test_H103_full(self): + """Verify that the H103 check finds an SPDX header""" + self.assertIsNone(comments.hacking_has_correct_license( + None, + None, + [ + """ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + """ # noqa + ], + 1, + )) + + def test_H103_SPDX(self): + """Verify that the H103 check finds an SPDX header""" + self.assertIsNone(comments.hacking_has_correct_license( + None, + None, + [ + '# foo', + '# SPDX-License-Identifier: Apache-2.0', + '# foo', + '# bar', + '# foo', + '# bar', + '# foo', + '# bar', + '# foo', + '# bar', + '# foo', + '# bar', + ], + 1, + )) + def test_H104_regex(self): """Verify that the H104 regex matches correct lines.""" self.assertTrue(comments.hacking_has_only_comments(