Skip to content

Commit 1dc7eb9

Browse files
kylebonnicicfriedt
authored andcommitted
CI: devicetree: linting to check_compliance.py
Use dts-linter to check each touched file in PR Signed-off-by: Kyle Micallef Bonnici <[email protected]>
1 parent 0d2e895 commit 1dc7eb9

File tree

8 files changed

+201
-0
lines changed

8 files changed

+201
-0
lines changed

.github/workflows/compliance.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,16 @@ jobs:
6060
west config manifest.group-filter -- +ci,-optional
6161
west update -o=--depth=1 -n 2>&1 1> west.update.log || west update -o=--depth=1 -n 2>&1 1> west.update2.log
6262
63+
- name: Setup Node.js
64+
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
65+
with:
66+
node-version: "lts/*"
67+
cache: npm
68+
check-latest: true
69+
70+
- name: Install Node dependencies
71+
run: npm ci
72+
6373
- name: Run Compliance Tests
6474
continue-on-error: true
6575
id: compliance
@@ -86,6 +96,14 @@ jobs:
8696
name: compliance.xml
8797
path: compliance.xml
8898

99+
- name: Upload dts linter patch
100+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
101+
continue-on-error: true
102+
if: hashFiles('dts_linter.patch') != ''
103+
with:
104+
name: dts_linter.patch
105+
path: dts_linter.patch
106+
89107
- name: check-warns
90108
run: |
91109
if [[ ! -s "compliance.xml" ]]; then

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ target/
7676

7777
# CI output
7878
compliance.xml
79+
dts_linter.patch
7980
_error.types
8081

8182
# Tag files
@@ -118,3 +119,6 @@ TextEncoding.txt
118119
YAMLLint.txt
119120
ZephyrModuleFile.txt
120121
# zephyr-keep-sorted-stop
122+
123+
# Node dependecies
124+
node_modules

doc/contribute/style/devicetree.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Devicetree Style Guidelines
44
###########################
55

66
* Indent with tabs.
7+
* Tab size is 8 characters.
78
* Follow the Devicetree specification conventions and rules.
89
* If the Linux kernel rules in
910
`Devicetree Sources (DTS) Coding Style <https://docs.kernel.org/devicetree/bindings/dts-coding-style.html>`_

doc/contribute/style/index.rst

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,35 @@ When there are differences between the `Coding Style Guidelines`_ guidelines and
8787
formatting generated by code formatting tools, the `Coding Style Guidelines`_ guidelines
8888
take precedence. If there is ambiguity between formatting tools and the
8989
guidelines, maintainers may decide which style should be adopted.
90+
91+
dts-linter
92+
============
93+
94+
The `dts-linter <https://www.npmjs.com/package/dts-linter>`_ can be helpful
95+
to quickly reformat large amounts of devicetree files to our `Coding Style Guidelines`_
96+
standards. You can also run it manually like this:
97+
98+
For individual files
99+
.. code-block:: bash
100+
101+
npx dts-linter --format --file board.dts --file board_pinctrl.dtsi --patchFile diff.patch
102+
git apply diff.patch
103+
104+
You can omit ``--file`` and this will format all files under the directory where the command
105+
has been called. Alternatively ``--cwd`` can also be passed set the base dir where the tool
106+
should look for files. This option is also used to make the paths relative in the patch file.
107+
108+
You can also fix in place with
109+
.. code-block:: bash
110+
111+
npx dts-linter --formatFixAll
112+
113+
114+
Editor Integration
115+
~~~~~~~~~~~~~~~~~~
116+
117+
* For VS Code: Install the extension from the `VS Code Marketplace <https://marketplace.visualstudio.com/items?itemName=KyleMicallefBonnici.dts-lsp>`_ or `Open VSIX <https://open-vsx.org/extension/KyleMicallefBonnici/dts-lsp>`_
118+
* For other editors with LSP Client support: Use the devicetree-language-server `devicetree-language-server <https://www.npmjs.com/package/devicetree-language-server>`_
119+
120+
Make sure you follow `Devicetree Style Guidelines <https://docs.zephyrproject.org/latest/contribute/style/devicetree.html>`_
121+
requirements to configure the editor correctly.

package-lock.json

Lines changed: 36 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"private": true,
3+
"dependencies": {
4+
"dts-linter": "^0.3.0"
5+
}
6+
}

scripts/checkpatch.pl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2573,6 +2573,11 @@ sub process {
25732573
next;
25742574
}
25752575

2576+
# skip package-lock.json and package.json files specifically
2577+
if ($realfile =~ /package(-lock)?\.json$/) {
2578+
next;
2579+
}
2580+
25762581
#make up the handle for any error we report on this line
25772582
if ($showfile) {
25782583
$prefix = "$realfile:$realline: "

scripts/ci/check_compliance.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,105 @@ def required_false_check(self, binding):
489489
"'required: false' is redundant, please remove"
490490
)
491491

492+
493+
class DevicetreeLintingCheck(ComplianceTest):
494+
"""
495+
Checks if we are introducing syntax or formatting issues to devicetree files.
496+
"""
497+
name = "DevicetreeLinting"
498+
doc = "See https://docs.zephyrproject.org/latest/contribute/style/devicetree.html for more details."
499+
500+
def _parse_json_output(self, cmd, cwd=None):
501+
"""Run command and parse single JSON output with issues array"""
502+
result = subprocess.run(
503+
cmd,
504+
stdout=subprocess.PIPE,
505+
stderr=subprocess.STDOUT,
506+
check=False,
507+
text=True,
508+
cwd=cwd or GIT_TOP
509+
)
510+
511+
if not result.stdout.strip():
512+
return None
513+
514+
try:
515+
json_data = json.loads(result.stdout)
516+
return json_data
517+
except json.JSONDecodeError as e:
518+
raise RuntimeError(f"Failed to parse dts-linter JSON output: {e}")
519+
520+
def run(self):
521+
# Get changed DTS files
522+
dts_files = [
523+
file for file in get_files(filter="d")
524+
if file.endswith((".dts", ".dtsi", ".overlay"))
525+
]
526+
527+
if not dts_files:
528+
self.skip('No DTS')
529+
530+
temp_patch_files = []
531+
batch_size = 500
532+
533+
for i in range(0, len(dts_files), batch_size):
534+
batch = dts_files[i:i + batch_size]
535+
536+
# use a temporary file for each batch
537+
temp_patch = f"dts_linter_{i}.patch"
538+
temp_patch_files.append(temp_patch)
539+
540+
cmd = [
541+
"npx", "--no", "dts-linter", "--",
542+
"--outputFormat", "json",
543+
"--format",
544+
"--patchFile", temp_patch,
545+
]
546+
for file in batch:
547+
cmd.extend(["--file", file])
548+
549+
try:
550+
json_output = self._parse_json_output(cmd)
551+
552+
if json_output and "issues" in json_output:
553+
cwd = json_output.get("cwd", "")
554+
logging.info(f"Processing issues from: {cwd}")
555+
556+
for issue in json_output["issues"]:
557+
level = issue.get("level", "unknown")
558+
message = issue.get("message", "")
559+
560+
if level == "info":
561+
logging.info(message)
562+
else:
563+
title = issue.get("title", "")
564+
file = issue.get("file", "")
565+
line = issue.get("startLine", None)
566+
col = issue.get("startCol", None)
567+
end_line = issue.get("endLine", None)
568+
end_col = issue.get("endCol", None)
569+
self.fmtd_failure(level, title, file, line, col, message, end_line, end_col)
570+
571+
except subprocess.CalledProcessError as ex:
572+
stderr_output = ex.stderr if ex.stderr else ""
573+
if stderr_output.strip():
574+
self.failure(f"dts-linter found issues:\n{stderr_output}")
575+
else:
576+
self.failure("dts-linter failed with no output. "
577+
"Make sure you install Node.JS and then run npm ci inside ZEPHYR_BASE")
578+
except RuntimeError as ex:
579+
self.failure(f"{ex}")
580+
581+
# merge all temp patch files into one
582+
with open("dts_linter.patch", "wb") as final_patch:
583+
for patch in temp_patch_files:
584+
with open(patch, "rb") as f:
585+
shutil.copyfileobj(f, final_patch)
586+
587+
# cleanup
588+
for patch in temp_patch_files:
589+
os.remove(patch)
590+
492591
class KconfigCheck(ComplianceTest):
493592
"""
494593
Checks is we are introducing any new warnings/errors with Kconfig,

0 commit comments

Comments
 (0)