Skip to content

Conversation

kylebonnici
Copy link
Contributor

@kylebonnici kylebonnici commented Jun 27, 2025

  • Formatting check to every devicetree file that is in the PR
  • If file is a .dts diagnostics are also checked such as syntax, property types, unresolved labels etc...
  • Check also validates files that are included by the changed file.

One can see an example run here
https://github.com/kylebonnici/zephyr/actions/runs/15936563443/job/44957546032

@kylebonnici kylebonnici force-pushed the feat/add-dts-compliance-check branch from fab15a8 to 985009f Compare June 27, 2025 22:07
@kylebonnici kylebonnici force-pushed the feat/add-dts-compliance-check branch 4 times, most recently from 8f97ce7 to fd78f84 Compare June 28, 2025 15:40
@pdgendt pdgendt self-requested a review June 29, 2025 10:52
@kylebonnici
Copy link
Contributor Author

I have updated the library version alpha20 to have better Output formatting
sample can be seen here:
https://github.com/kylebonnici/zephyr/actions/runs/15956934573/job/45004445056

@kylebonnici kylebonnici force-pushed the feat/add-dts-compliance-check branch from fd78f84 to e0431e7 Compare June 29, 2025 16:10
@fabiobaltieri
Copy link
Member

@kylebonnici hey, initial question on the basic design, this would check every file touched by a PR right? so if a PR changes only one line in the file and the styling issue is unrelated, this would fail and block it right?

@kylebonnici
Copy link
Contributor Author

Yes it checks all of the file. It also parses any includes and check these files too.

If it is a dts file it will also check for diagnostic issues.

We can disable formatting for included files even if I think it brings lots of value IMO

We can disable diagnostics when it is a dts file even if I think it brings lots of value IMO

@fabiobaltieri
Copy link
Member

Ok so few practical comments:

  • the current approach of checking modified files is not a good one in my opinion, we should first bring the current code base in complianace and then add this (has been done before). Does the tool support fixing the file style automatically? Could you run that in batch on the code base?
  • this should be integrated with scripts/ci/check_compliance.py like the other linting script (yamllint for example), it already has few helpers for running git diff etc... mainly so that one contributor can run it locally, with that in mind though, I'm not sure we really want to add node.js to the dependencies of that (what do you think @kartben @nashif?)

@kartben
Copy link
Contributor

kartben commented Jun 30, 2025

+1 on doing the global cleanup first, and I think it's something that can be done automatically AFAICT.

And +1 on making it part of check_compliance.py. Having to pull in node.js is a bit of a concern for me as well, but I think it makes sense to bite the bullet and go for it ; this might actually unlock some interesting stuff for documentation as well, by allowing to start using mermaid.js which I've wanted to do for a long time.

@fabiobaltieri
Copy link
Member

To give a bit more context: it's common practice for commit and pull request to be limited in scope, having this change as is would often require a contributor that is, for example, adding a driver, to also refactor the dts files (potentially a lot of them) in the same PR,

For node, this would be the first thing that requires it, I'd say add the check to compliance and have it skip automatically if node is not detected for now.

@kylebonnici
Copy link
Contributor Author

Few answers and questions:

  • I can add a --format-fix-all flag this should not be an issue.
    • Tool can already generate diff on all the repo but it stops short of applying it.
  • I can add to this PR the changes needed to fix all of the repo.
  • I cannot auto fix diagnostic issues so these will need to be addressed manually.
    • What shall I do here ... I think this tool bring value e.g.
      -boards/raytac/mdbt53v_db_40/raytac_mdbt53v_db_40_nrf5340_cpuapp_common.dts should be boards/raytac/mdbt53v_db_40/raytac_mdbt53v_db_40_nrf5340_cpuapp_common.dtsi
      - boards/native/nrf_bsim/nrf5340bsim_nrf5340_cpuapp.dts deletes properties/nodes that do not exists
  • I will look at scripts/ci/check_compliance.py
    • If node is preset; shall the tool install dts-linter and if so where should I put the version of the linter?
    • If node is not preset shell I to echo that node is missing and dts files will not be checked?
  • Re need for node I see that if users want to use my LSP (on nvim i.e. not vscode like Editors) node is needed
  • FYI Also user can install the dts-lsp and format documents as they work

@kylebonnici
Copy link
Contributor Author

Follow up question:

  • In scripts/ci/check_compliance.py are we to do action on all repo files or files touched in branch?

@kylebonnici
Copy link
Contributor Author

kylebonnici commented Jun 30, 2025

Who is the intended user for scripts/ci/check_compliance.py?

@nordicjm
Copy link
Contributor

nordicjm commented Jul 1, 2025

this needs to be usable from just the command line, I can't get this tool to work at all, it specifies the --files is needed then throws some node exception when I supply that, it also throws some node exception when I even try to run it with just --help, and why is node needed anyway, why can't this be done in python?

LSP stderr: node:internal/url:1564
    throw new ERR_INVALID_FILE_URL_HOST(platform);
          ^

TypeError [ERR_INVALID_FILE_URL_HOST]: File URL host must be "localhost" or empty on linux
    at getPathFromURLPosix (node:internal/url:1564:11)
    at Object.fileURLToPath (node:internal/url:1610:63)
    at Ve (/usr/lib/node_modules/dts-linter/dist/server.js:182:43983)
    at /usr/lib/node_modules/dts-linter/dist/server.js:309:10610
    at om.invoke (/usr/lib/node_modules/dts-linter/dist/server.js:2:11966)
    at t.fire (/usr/lib/node_modules/dts-linter/dist/server.js:2:12735)
    at /usr/lib/node_modules/dts-linter/dist/server.js:36:57396
    at Yl (/usr/lib/node_modules/dts-linter/dist/server.js:7:1541)
    at Ra (/usr/lib/node_modules/dts-linter/dist/server.js:6:6212)
    at zl (/usr/lib/node_modules/dts-linter/dist/server.js:6:6366) {
  code: 'ERR_INVALID_FILE_URL_HOST'
}

Node.js v24.3.0

^C

and

node:internal/util/parse_args/parse_args:107
      throw new ERR_PARSE_ARGS_UNKNOWN_OPTION(
      ^

TypeError [ERR_PARSE_ARGS_UNKNOWN_OPTION]: Unknown option '--help'
    at checkOptionUsage (node:internal/util/parse_args/parse_args:107:13)
    at node:internal/util/parse_args/parse_args:381:9
    at Array.forEach (<anonymous>)
    at parseArgs (node:internal/util/parse_args/parse_args:378:3)
    at Object.<anonymous> (/usr/lib/node_modules/dts-linter/dist/dts-linter.js:66:104428)
    at Module._compile (node:internal/modules/cjs/loader:1692:14)
    at Object..js (node:internal/modules/cjs/loader:1824:10)
    at Module.load (node:internal/modules/cjs/loader:1427:32)
    at Module._load (node:internal/modules/cjs/loader:1250:12)
    at TracingChannel.traceSync (node:diagnostics_channel:322:14) {
  code: 'ERR_PARSE_ARGS_UNKNOWN_OPTION'
}

Node.js v24.3.0

@kylebonnici
Copy link
Contributor Author

Yes I am working on improving the CLI but first I want to understand what it will look at the end... I have also found some issue with formatting some specific files hopefully I can get these fixed in LSP today evening. Plan is to upload a diff of all of the formatting changes here to get feedback on weather the formatter is doing any changes the should be changed.

@fabiobaltieri
Copy link
Member

  • I can add a --format-fix-all flag this should not be an issue.

    • Tool can already generate diff on all the repo but it stops short of applying it.
  • I can add to this PR the changes needed to fix all of the repo.

  • I cannot auto fix diagnostic issues so these will need to be addressed manually.

    • What shall I do here ... I think this tool bring value e.g.
      -boards/raytac/mdbt53v_db_40/raytac_mdbt53v_db_40_nrf5340_cpuapp_common.dts should be boards/raytac/mdbt53v_db_40/raytac_mdbt53v_db_40_nrf5340_cpuapp_common.dtsi
      • boards/native/nrf_bsim/nrf5340bsim_nrf5340_cpuapp.dts deletes properties/nodes that do not exists

Whatever the final linter configuration can detect should be fixed I suppose, then we can argue whether it's right or wrong but this sounds reasonable

  • I will look at scripts/ci/check_compliance.py

    • If node is preset; shall the tool install dts-linter and if so where should I put the version of the linter?
    • If node is not preset shell I to echo that node is missing and dts files will not be checked?

No I would not make check_compliance install stuff, right now I'd expect the user to install all the python requirement, run the script and it should work, adding another framework is a different conversation, I'd skip if node is not present

  • Re need for node I see that if users want to use my LSP (on nvim i.e. not vscode like Editors) node is needed

Sure but right now it's not a requirement for the project so it's a bit of a hard sell, if it was python it'd be way easier since we already have the infrastructure to manage python libraries but here we are talking about taking in yet another framework.

  • FYI Also user can install the dts-lsp and format documents as they work

It's a good argument, @kartben for it.

In scripts/ci/check_compliance.py are we to do action on all repo files or files touched in branch?

The script takes a git commit range, it should only work on files in that range, but there are some checks that work globally, so there is an expectation that the whole code base as committed is compliant.

Who is the intended user for scripts/ci/check_compliance.py?

Mainly the CI but it should be possible to run it for a contributor without major setups.

@nashif
Copy link
Member

nashif commented Jul 1, 2025

for any linters or code scanners, at least the following applies:

  • we need to be able to run them locally without too much hassle and dependencies
  • we need the linter to only report errors/warnings on changed code and not the entire code base
  • ideally the linter should provide inline annotations in PRs

Having a DTS linter is a great thing, this will remove the need for folks to go hunt for white space violations and trivial stuff a tool can catch, so thank you for looking into this.

@nashif
Copy link
Member

nashif commented Jul 1, 2025

... and having this in current check_compliance script is preferred, but I would not say no to a linter that does the above on their own, if integration in check_compliance is not possible (we need to refactor check_compliance script and make it more modular, just looking at this thing now gives me the creeps)

@kylebonnici kylebonnici force-pushed the feat/add-dts-compliance-check branch from 4f8e346 to 7e6c87d Compare September 18, 2025 22:50
@kylebonnici
Copy link
Contributor Author

@kylebonnici
Copy link
Contributor Author

kylebonnici commented Sep 19, 2025

I watched the TSC meeting and have some thoughts worth considering IMO,

The best case scenario IMO is some tool that can optionally be used in a development environment and the same tool can also be used to in CI. hence all requirements are always aligned.

Users who opt to have such LSP in the IDE will format as they work (similar to the point made to get users to have properly linted code before committing), and this should be optional but possible!

Currently if one looks at the adoption rate for the dts-lsp more than 3.5K users are already using this in VSCode like IDEs and form npm downloads (https://npm-stat.com/charts.html?package=devicetree-language-server&from=2024-12-01&to=2025-09-20) I would argue that at least 1K more are using it in VIM once you subtract some server caching downloads. And from feedback I have most are zephyr users.

The LSP does way more then just linting, IMO linting is the first step and if Zephyr opts make use of this tool we can also do more static checking, Running this tool with --diagnosticsFulll and passing the --include and --binding can I was already able find a large number of other issues that the linter format check is not doing in this PR.

Below is a sample report of issue I could detect on zephyr/boards/arm
diagnosticsFull_arm.json

Below is a sample report of issue I could detect on zephyr/boards/microchip
diagnosticsFull_microchip.json
e.g.

  • microchip there is a header file missing in the zephyr! dt-bindings/sam/sam_d5x_e5x/atsame54/atsame54p20a-pinctrl.h
  • boards/microchip/mec_assy6941/mec_assy6941_mec1743_qlj.dts: is referencing node label that does not exists uart1_tx_gpio170!

IMO having these tools available and accepted by the community is also a big plus for the developer experience!

I hope you can see how all of this can stack nicely together by giving users tools they can use in their Editors and at the same time reused the same tools in CI.

pdgendt
pdgendt previously approved these changes Sep 19, 2025
npx dts-linter --formatFixAll
If you want to format file while developing and is using a VS Code like IDEs you can install the extension
Copy link
Contributor

@JarmouniA JarmouniA Sep 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
If you want to format file while developing and is using a VS Code like IDEs you can install the extension
Editor Integration
~~~~~~~~~~~~~~~~~~

name: compliance.xml
path: compliance.xml

- name: upload-dts-linter-patch
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are normally human readable titles, guess it'd be "Upload dts linter patch" or something on that line.

What's the idea with it btw? That one can download the patch and apply it locally? (sorry I've been on and off this PR conversation, I imagine it's been discussed)

Copy link
Contributor Author

@kylebonnici kylebonnici Sep 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the idea is that a user can get all fixes and not need to install tool locally to mitigate the concern that users need to install the tool locally.

@nashif
Copy link
Member

nashif commented Sep 19, 2025

just tested that, the compliance check does not do any annotations, this is not great, what this raised as an issue before? (sorry, lots of comments in this PR, so such comments might be burried in there).

image

the diff is fine in there, but this is in the CI log and is not very user friendly, it would be nice to have this appear as annotation in the file view of the PR.

@nashif
Copy link
Member

nashif commented Sep 19, 2025

the diff is fine in there, but this is in the CI log and is not very user friendly, it would be nice to have this appear as annotation in the file view of the PR.

another question, does the linter tell you what is wrong with your formatting beside generating diffs?

@kylebonnici
Copy link
Contributor Author

kylebonnici commented Sep 19, 2025

just tested that, the compliance check does not do any annotations, this is not great, what this raised as an issue before? (sorry, lots of comments in this PR, so such comments might be burried in there).

the diff is fine in there, but this is in the CI log and is not very user friendly, it would be nice to have this appear as annotation in the file view of the PR.

@nashif

#92334 (comment)
#92334 (comment)

TLDR, I opted to annotate the changes needed to the whole file

another question, does the linter tell you what is wrong with your formatting beside generating diffs?

No the linter does generates changes (that may in some cases lead to the same text) and applies them to the current document. Then a diff is generated.

Explanation:

I had given a try with annotating each edit (by annotating the diff changes). But given the limitation I faced with GIT showing only a limited number of annotations (as can be seen in the comments above) and technical limitations we will run into as we enforce more rules, I opted to give just provide a diff.

This is also very common e.g. black a python formatter, also only provides diff, prettier - another linter - also cannot annotate each change and this doesn't even give a diff but rather just changes the files.

The reason why this becomes unmanageable is that text edits must be applied in reverse order. i.e from last line to the top. Also you cannot have overlapping text edit regions.

Therefore to solve certain problems and formatting rules you have to do the formatting in layers.

example I have reached this limitation already. Currently I am working on adding sorting of properties and nodes as per the spec (properties on top and nodes underneath); in most cases this will lead to overlapping text edits when changing tabs and spaces within the node and property it self.

This can be solved by first sorting and applying the edits internally to get the new code and then run the second layer to fix tabs, spaces etc...

Similarly to wrap lines so as to not exceed 100 characters, another layer will be needed. This is still WIP on my end that will be added later.

Given all the above technical limitations, the only feasible solution I reached is to provide a diff.

Having a tool which annotates each specific error would require to keep track of the position of each text component in the original text source, the more layers the harder and more costly this gets.

Note that syntax issues that stop the linter from formatting are annotated as can be see here

https://github.com/kylebonnici/zephyr/pull/6/files#annotation_39253140434

EDIT

to give a practical example how you one go about annotating this case from a diff output?
diff.patch

order Properties and Nodes

Initial PR to address sorting of properties and node (i.e. prop on top of nodes and sorting of properties as per my understanding of https://docs.kernel.org/devicetree/bindings/dts-coding-style.html)

#96317

Use dts-linter to check each touched file in PR

Signed-off-by: Kyle Micallef Bonnici <[email protected]>
@kylebonnici kylebonnici force-pushed the feat/add-dts-compliance-check branch from 826cf35 to a775a39 Compare September 25, 2025 19:10
Copy link

Copy link
Contributor

@mbolivar mbolivar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exciting work! The DT docs changes seem fine to me. I'm going to steer clear of the discussion on npm :)

@kylebonnici
Copy link
Contributor Author

kylebonnici commented Oct 8, 2025

Some point to address from today's TSC:

I know this is long but I am trying to get as much details as possible.

Do user Have to install it on their machines?

Short answer is no.

Longs answer:

  • The linter will provide a diff of changes needed to make it compliant.
  • The linter will generate a patch file to apply, This will fix all the issues reported in the CI Action. This patch file is available as part of the CI and can be downloaded.

Q: But if a user does not want to install this tool, won't this CI check make it more difficult to comply as PR is blocked and user has to manually fix the issues?

A: No, actually this will make things faster. This linter is respecting the rules as described in the coding guidelines so In theory all the changes this linter suggests should be done and flagged non the less. With this in mind the PR should still be blocked! The only difference is that now these infractions will be reported constantly every time. In addition the reporting will be consistent and not up to a humans to explain "add new line", "remove line", "Add space here"

Also the check_compliance.py can run locally without node and this and dts-linter, in such case it will skip the dts-linting step locally.

Concerns with adding Node (yet another scripting language):

  • This tool only needs node for runtime. No typescript files will be added to the zephyr repo.
  • Zephyr can keep its policy to not allow Typescript and Javascript as a scripting language and this tool can still be used.
  • The only reason package.json and package-lock.json where added was to allow locking the version of the tool to reduce the attack vectors when tool is installed, as recommended by @pdgendt.

Why Node:

Why reusing tools between IDEs and CI make logical sense to me:

  • One code base to maintain.
  • Changes to one tool will benefit both CI and IDEs,
  • Keeping requirements in sync
  • Users that opt to use the lsp in the editor will have a seamless experience as the linting rules for CI and the development environment are the same.

To date if one looks at the adoption rate for the dts-lsp more than 4.1K users are already using this in VSCode like IDEs and form npm downloads (https://npm-stat.com/charts.html?package=devicetree-language-server&from=2024-12-01&to=2025-10-08) At least 1K+ more are using it in VIM/neoVIM once you subtract server caching downloads and some CIs downloads from when testing this PRs. From the feedback I have, most are zephyr users.

Also if one looks at the dts-linter around 1K+ users are using this CLI tool see https://npm-stat.com/charts.html?package=dts-linter&from=2024-12-01&to=2025-10-08. This number considers server caching downloads and some CIs when testing these PRs

Concerns with dependencies:

There are two types of dependencies:

  • Required at runtime,
  • Required during development such as types, tool to build and lint typescript project etc...

There are two ways to ship a project

  • Bundle i.e generate one .js file
  • Import dependencies at runtime

First important point is that any dependency needed for development are never needed at runtime and hence these dependencies will never be bundled nor imported at runtime. E.g. in c++ gcc would be a dev dependency

Dependencies of dts-linter

Development:

esbuild
Build the code into .js file from typescript

@types/node
Need for Typescript to provide node.js common types (note javascript has no type safety, this is provided by typescript and these types are used to provide error when transpiling Typescript to Javascript)

devicetree-language-server-types
Types I have created to be able to enforce typing when communicating with dts-lsp

typescript
Needed to transplile TS to JS

vscode-languageserver-protocol-types
Microsoft library to communicate with any LSP client

license-checker
Used to generate THIRD_PARTY_LICENSES.txt

Runtime:

dts-lsp
Uses LSP to get new formatted text

diff
Convert the LSP Text edits to a diff format

glob
Used to search for files when no --file is passed to the CLI command

zod
Used to validate CLI argument input

vscode-jsonrpc
Microsoft library to communicate with any LSP client

Dependencies of dts-lsp

Development:

@jest/globals
Needed for unit test

@tsconfig/node20
Needed for the building of the project

@types/jest
Needed for unit test

@types/node
Need for Typescript to provide node.js common types (note javascript has no type safety, this is provided by typescript and these types are used to provide error when transpiling Typescript to Javascript)

jestts-jest
Needed for unit test

typescript
Needed to transplile TS to JS

vscode-languageserver-types
Needed to get the lsp types from Microsoft

license-checker
Used to generate THIRD_PARTY_LICENSES.txt

Runtime:

@hyperjump/browser
Used to support Linux Bindings

@hyperjump/json-schema
Used to support Linux Bindings

ajv
Used to support Linux Bindings

glob
Used to find bindings from binding root paths.

vscode-languageserver
Provides the LSP APIs

vscode-languageserver-textdocument
Provides the LSP APIs

yaml
Used to parse the binding files.

Note The development dependencies must be be in the devDependencies in the package.json of the dts-linter and dts-lsp

Let focus on the Runtime dependencies.

Case 1: Getting runtime dependencies at runtime with no bundling

  • The runtime dependencies must be defined as dependencies in the package.json of the dts-linter and dts-lsp
  • When npm i dts-linter@version is called, node_modules folder is created and each and every dependency from the dependency list will be also installed
  • when the npx dts-linter... is called the .js file for the dts-linter will dynamically load modules from the node_module folder

Note One can see this as the equivalent of a DLL concept where the DLL is loaded at runtime

Important notes

  • The version of dependencies download will depend the version specified in the packeg-lock.json
  • Running npm audit fix will update the dependencies and next run will use the new dependencies.

Currently the build process of both these tools ( dts-linter and dts-lsp ) is to bundle, hence if this above options is preferred changes to the build process will be needed.

Case 2: Bundled (current case)

Note One can see bundling as the equivalent of a static linking in c/c++ context

Licensing

When considering the bundled/runtime dependencies of the dts-lsp:
├─ MIT: 44
├─ ISC: 10
├─ BlueOak-1.0.0: 3
├─ Apache-2.0: 1
└─ BSD-3-Clause: 1

When considering the bundled/runtime dependencies of the dts-linter:
├─ MIT: 24
├─ ISC: 9
├─ BlueOak-1.0.0: 3
├─ Apache-2.0: 2
└─ BSD-3-Clause: 1

I have added to dts-linter and dts-lsp and automatically generated file with all the License of the bundled dependencies
dts-lsp
dts-linter
See dist/THIRD_PARTY_LICENSES.txt

Hope this address the concerns on licensing. If not let me know so I can address.

Annotations

As explain here #92334 (comment), it will be very difficult, if at all possible, to be able to provide meaningful annotations along side code fixes such as diffs; (read the comment to get full understanding of the difficulties). This is also a limitation in other formatters such as the python formater (black)[https://github.com/psf/black]

Also from the LSP perspective, annotations will bring no benefits to the end user. However if the committee opts to use this tool in CI, then I can dedicate time on this challenge.

Alternative tools

On a separate note, Zephyr has been looking at providing tools for VS Code (see #74005), The zephyr-ide already integrates with the dts-lsp used by the dts-linter

@kylebonnici
Copy link
Contributor Author

Minor addition:

I have been made aware of Ruff and that it does allow for annotations and somehow it does solve the issue of overlapping changes. I will look into how Ruff does this and start considering this addition to the tool for the near future.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: Coding Guidelines Coding guidelines and style area: Continuous Integration area: Documentation TSC Topics that need TSC discussion
Projects
Status: Todo
Development

Successfully merging this pull request may close these issues.