Skip to content

Commit a7ace50

Browse files
committed
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 9e42f0c commit a7ace50

File tree

8 files changed

+209
-0
lines changed

8 files changed

+209
-0
lines changed

.github/workflows/compliance.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,23 @@ 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: Cache npm dependencies
64+
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
65+
with:
66+
path: ~/.npm
67+
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
68+
restore-keys: |
69+
${{ runner.os }}-npm-
70+
71+
- name: Setup Node.js
72+
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0
73+
with:
74+
node-version: "lts/*"
75+
check-latest: true
76+
77+
- name: Install Node dependencies
78+
run: npm ci
79+
6380
- name: Run Compliance Tests
6481
continue-on-error: true
6582
id: compliance
@@ -86,6 +103,14 @@ jobs:
86103
name: compliance.xml
87104
path: compliance.xml
88105

106+
- name: upload-dts-linter-patch
107+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
108+
continue-on-error: true
109+
if: hashFiles('dts_linter.patch') != ''
110+
with:
111+
name: dts_linter.patch
112+
path: dts_linter.patch
113+
89114
- name: check-warns
90115
run: |
91116
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: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,36 @@ When there are differences between the `Coding Style Guidelines`_ guidelines and
8585
formatting generated by code formatting tools, the `Coding Style Guidelines`_ guidelines
8686
take precedence. If there is ambiguity between formatting tools and the
8787
guidelines, maintainers may decide which style should be adopted.
88+
89+
dts-linter
90+
============
91+
92+
The `dts-linter <https://www.npmjs.com/package/dts-linter>`_ can be helpful
93+
to quickly reformat large amounts of devicetree files to our `Coding Style Guidelines`_
94+
standards. You can also run it manually like this:
95+
96+
For individual files
97+
.. code-block:: bash
98+
99+
npx dts-linter --format --file board.dts --file board_pinctrl.dtsi --patchFile diff.patch
100+
git apply diff.patch
101+
102+
You can omit ``--file`` and this will format all files under the directory where the command
103+
has been called. Alternatively ``--cwd`` can also be passed set the base dir where the tool
104+
should look for files. This option is also used to make the paths relative in the patch file.
105+
106+
You can also fix in place with
107+
.. code-block:: bash
108+
109+
npx dts-linter --formatFixAll
110+
111+
112+
If you want to format file while developing and is using a VS Code like IDEs you can install the extension
113+
* For VS Code: Install the extension from the
114+
`VS Code Marketplace <https://marketplace.visualstudio.com/items?itemName=KyleMicallefBonnici.dts-lsp>`_ or
115+
`Open VSIX <https://open-vsx.org/extension/KyleMicallefBonnici/dts-lsp>`_
116+
* For other editors with LSP Client support: Use the devicetree-language-server
117+
`devicetree-language-server <https://www.npmjs.com/package/devicetree-language-server>`_
118+
119+
Make sure you follow `Devicetree Style Guidelines <https://docs.zephyrproject.org/latest/contribute/style/devicetree.html>`_
120+
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-beta2"
5+
}
6+
}

scripts/checkpatch.pl

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

2575+
# skip package-lock.json and package.json files specifically
2576+
if ($realfile =~ /package(-lock)?\.json$/) {
2577+
next;
2578+
}
2579+
25752580
#make up the handle for any error we report on this line
25762581
if ($showfile) {
25772582
$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)