|
18 | 18 | import tempfile
|
19 | 19 | import textwrap
|
20 | 20 | import traceback
|
| 21 | +from collections.abc import Iterable |
21 | 22 | from itertools import takewhile
|
22 | 23 | from pathlib import Path, PurePath
|
23 | 24 |
|
|
26 | 27 | import yaml
|
27 | 28 | from dotenv import load_dotenv
|
28 | 29 | from junitparser import Error, Failure, JUnitXml, Skipped, TestCase, TestSuite
|
| 30 | +from reuse.project import Project |
| 31 | +from reuse.report import ProjectReport, ProjectSubsetReport |
29 | 32 | from west.manifest import Manifest, ManifestProject
|
30 | 33 | from yamllint import config, linter
|
31 | 34 |
|
@@ -1557,6 +1560,68 @@ def run(self):
|
1557 | 1560 | self.failure("\n".join(offending_lines))
|
1558 | 1561 |
|
1559 | 1562 |
|
| 1563 | +class LicenseAndCopyrightCheck(ComplianceTest): |
| 1564 | + """ |
| 1565 | + Verify that every file touched by the patch set has correct SPDX headers and uses allowed |
| 1566 | + license. |
| 1567 | + """ |
| 1568 | + |
| 1569 | + name = "LicenseAndCopyrightCheck" |
| 1570 | + doc = "Check SPDX headers and copyright lines with the reuse Python API." |
| 1571 | + |
| 1572 | + def _report_violations( |
| 1573 | + self, |
| 1574 | + paths: Iterable[Path], |
| 1575 | + title: str, |
| 1576 | + severity: str, |
| 1577 | + desc: str | None = None, |
| 1578 | + ) -> None: |
| 1579 | + for p in paths: |
| 1580 | + rel_path = os.path.relpath(str(p), GIT_TOP) |
| 1581 | + self.fmtd_failure(severity, title, rel_path, desc=desc or "", line=1) |
| 1582 | + |
| 1583 | + def run(self) -> None: |
| 1584 | + changed_files = get_files(filter="d") |
| 1585 | + if not changed_files: |
| 1586 | + return |
| 1587 | + |
| 1588 | + # Only scan text files for now, in the future we may want to leverage REUSE standard's |
| 1589 | + # ability to also associate license/copyright info with binary files. |
| 1590 | + for file in changed_files: |
| 1591 | + full_path = GIT_TOP / file |
| 1592 | + mime_type = magic.from_file(os.fspath(full_path), mime=True) |
| 1593 | + if not mime_type.startswith("text/"): |
| 1594 | + changed_files.remove(file) |
| 1595 | + |
| 1596 | + project = Project.from_directory(GIT_TOP) |
| 1597 | + report = ProjectSubsetReport.generate(project, changed_files, multiprocessing=False) |
| 1598 | + |
| 1599 | + self._report_violations( |
| 1600 | + report.files_without_licenses, |
| 1601 | + "License missing", |
| 1602 | + "warning", |
| 1603 | + "File has no SPDX-License-Identifier header, consider adding one.", |
| 1604 | + ) |
| 1605 | + |
| 1606 | + self._report_violations( |
| 1607 | + report.files_without_copyright, |
| 1608 | + "Copyright missing", |
| 1609 | + "warning", |
| 1610 | + "File has no SPDX-FileCopyrightText header, consider adding one.", |
| 1611 | + ) |
| 1612 | + |
| 1613 | + for lic_id, paths in getattr(report, "missing_licenses", {}).items(): |
| 1614 | + self._report_violations( |
| 1615 | + paths, |
| 1616 | + "License may not be allowed", |
| 1617 | + "warning", |
| 1618 | + ( |
| 1619 | + f"License file for '{lic_id}' not found in /LICENSES. Please check " |
| 1620 | + "https://docs.zephyrproject.org/latest/contribute/guidelines.html#components-using-other-licenses." |
| 1621 | + ), |
| 1622 | + ) |
| 1623 | + |
| 1624 | + |
1560 | 1625 | class GitLint(ComplianceTest):
|
1561 | 1626 | """
|
1562 | 1627 | Runs gitlint on the commits and finds issues with style and syntax
|
|
0 commit comments