Skip to content

Commit 7da1e4b

Browse files
kylebonnicinashif
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 9dfac5a commit 7da1e4b

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
@@ -73,6 +73,7 @@ target/
7373

7474
# CI output
7575
compliance.xml
76+
dts_linter.patch
7677
_error.types
7778

7879
# Tag files
@@ -112,3 +113,6 @@ SysbuildKconfigBasicNoModules.txt
112113
TextEncoding.txt
113114
YAMLLint.txt
114115
ZephyrModuleFile.txt
116+
117+
# Node dependecies
118+
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
@@ -491,6 +491,105 @@ def required_false_check(self, binding):
491491
"'required: false' is redundant, please remove"
492492
)
493493

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

0 commit comments

Comments
 (0)