Skip to content

CI: devicetree: Add compliance and formatting check #92334

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

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
Copy link
Contributor Author

kylebonnici commented Jul 7, 2025

@fabiobaltieri

I will look into adding something like clang-format off clang-format on

I think there's more value in having the linter tool than having fancy formatting in a single file, those comments can be moved in their own block or get rid of entirely. That said, is there a way of stopping the linter from linting a specific section? For clang we have clang-format off clang-format on blocks for sections where we prefer to do manual formatting, I think this should have it as well just in case.

I will prepare PR for the folders as you recommended

This is a huge diff, we are in freeze now so nothing is going to get in but my suggestion would be to split the diff to bring files into compliancy in few PRs, looking at the diffstat now it may be maybe 4 PR for files in: boards/, dts/, samples/ and tests/. Once most of them are in compliance if there's some more changes before submitting the workflow it'd be minor and can be done fleet wide.

I already have a PR to support negative numbers in expressions

It is valid, actually required, IIRC the whole thing falls apart with negative values with no parenthesis, something to do with macro expansion, we should really fix it somehow.

I still need to look at check_compliance.py plan is to add it to that but at the moment I am trying fix any there issue linked to the dts-linter it sel

Cool but we should probably decide now if this can stay on its own workflow or be part of check_compliance.py I'd prefer it to be part of check compliance and be skipped if node is not installed (just because installing it is one more step than the usual "apt-get install ..." and "pip install ..." that we already have in the getting started guide. No strong opinion though, but since this is going to block PR for users together with other linters I'd like to still be able to tell the users that they can use ONE script to check them all before uploading if they want to.

Re the below am I to support this or let linter whine about it as syntax error?

		antenna_mux0: antenna_mux0 {
		compatible = "skyworks,sky13317";
		status = "okay";
		gpios = <&gpio0 28 GPIO_ACTIVE_HIGH>, <&gpio0 29 GPIO_ACTIVE_HIGH>, \
				<&gpio0 30 GPIO_ACTIVE_HIGH>;
				.....

Re below I have found the bug on my end

I will also investigate why linter is complaining about zephyr/dts/arm/nuvoton/npck/npck3.dtsi as I see nothing wrong...

@kylebonnici
Copy link
Contributor Author

kylebonnici commented Jul 7, 2025

@fabiobaltieri

  • Created 4 PR as discussed above
  • Added support for // dts-format off and // dts-format which operate from line they are in inclusive and /* dts-format off / / dts-format on */ which operate on line:char off start at till line:char on ends at
  • I added // dts-format off and // dts-format on and manually formatted that section in boards/m5stack/m5stack_cores3/m5stack_mbus_connectors.dtsi. Any other change sin this file are by linter
  • I manually fixed the files with \ in them

Next I will look into check_compliance.py

@kylebonnici kylebonnici force-pushed the feat/add-dts-compliance-check branch from 8f316bb to ddf725b Compare July 8, 2025 22:37
@keith-zephyr
Copy link
Contributor

Good stuff, though I'm surprised how many files have changes now, looks like ts=4 was quite popular, checked again on the LInux code base just in case there's an exception I missed but they are indeed using ts=8 for dts and I don't think we should diverge from it. @kartben @keith-zephyr is that a fair assessment?

That works for me.

@kylebonnici
Copy link
Contributor Author

I have added some changes to the check_compliance.py should I delete the dts-compliance-check.yml?
@fabiobaltieri is this what you had in mind with regards to check_compliance.py?

@stephanosio
Copy link
Member

From the Linux kernel coding style:

"Tabs are 8 characters, and thus indentations are also 8 characters. There are heretic movements that try to make indentations 4 (or even 2!) characters deep, and that is akin to trying to define the value of PI to be 3."

I'm not sure we really want to add node.js to the dependencies of that

Why not? We already have the .NET runtime in the CI Docker image for Renode -- might as well include Node.js too while we are at it.

On a serious note, it would be nice if we could stick to only Python when possible; not because it is better than Node.js, but because it has been the main scripting language of choice by Zephyr. However, in this case, it would be unreasonable to ask dts-linter to be rewritten in Python, and the gains from having it in our toolbox would, as we have already seen in this PR, outweigh the losses from installing yet another runtime, which would be, at most, annoyance ...

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.

TBH, I think modern TypeScript is a much better choice for complex scripting than Python (or rather, Python with dynamic typing and weird OOP syntax is a truly horrible language to work with in general); not to mention, it is a much more natural choice for Zephyr developers given that most of us are already familiar with C/C++.

Maybe, this could be a starting point for more TypeScript stuff in Zephyr ...

Cool but we should probably decide now if this can stay on its own workflow or be part of check_compliance.py I'd prefer it to be part of check compliance and be skipped if node is not installed (just because installing it is one more step than the usual "apt-get install ..." and "pip install ..." that we already have in the getting started guide. No strong opinion though, but since this is going to block PR for users together with other linters I'd like to still be able to tell the users that they can use ONE script to check them all before uploading if they want to.

I agree that it would be good to have it be part of check_compliance.py and be skipped when Node.js is not installed with a warning.

Also, in general, we should try to minimise the number of CI workflows we have, for maintainability reasons.

@fabiobaltieri
Copy link
Member

I have added some changes to the check_compliance.py should I delete the dts-compliance-check.yml?
@fabiobaltieri is this what you had in mind with regards to check_compliance.py?

It is, yeah so the dedicated workflow can be dropped right? I see you had the workflow dispatch logic but I think it's something we can happily leave without.

@kylebonnici
Copy link
Contributor Author

kylebonnici commented Jul 9, 2025

Yes the my workflow allowed user to manually call the linter on files without a PR and allow us to change log level .... we can live without both IMO and user can run manually locally on files if they need this to be run without a PR/commit. I will drop that commit of my workflow... and I guess also the diff it self given it is own the other PRs right?

@fabiobaltieri
Copy link
Member

fabiobaltieri commented Jul 9, 2025

Yeah all other linter check right now runs automatically on all changed files based on a PR so I think we should stick to it for this as well.

The for the diff: linter errors ideally should be annotated by file and line, if possible, so that they show up correctly on the UI, there's other checks doing it. And yeah you could attach a diff and a description on how to have it fixed automatically, that'd be cool.

@kylebonnici
Copy link
Contributor Author

kylebonnici commented Jul 9, 2025

@fabiobaltieri by annotated you mean something like this

[check:lint   ] 
[check:lint   ] /home/runner/work/pc-nrfconnect-npm/pc-nrfconnect-npm/src/components/FuelGauge/FuelGaugeSettings.tsx
[check:lint   ]   303:77  error  Delete `·battery·models·`                                                                                                                                                                                                                                                                                                                                                                                                 prettier/prettier
[check:lint   ]   304:16  error  Replace `·are·good·for·evaluation·and·early·development.·Nordic·Semiconductor⏎················is·fine-tuning·the·profiling·for·small·batteries,·and·support·for·battery·models·for·mass` with `·battery·models·are·good·for·evaluation·and·early·development.⏎················Nordic·Semiconductor·is·fine-tuning·the·profiling·for·small⏎················batteries,·and·support·for·battery·models·for·mass·production`  prettier/prettier
[check:lint   ]   306:16  error  Delete `·production`                                                                                                                                                                                                                                                                                                                                                                                                      prettier/prettier
[check:lint   ] 

Or report the Text Edits e.g Start Line 5 col: 10 End: line 6 co:1 replace text = '...'?

if you can point me to the other changes that will be very helpful

I will extend to generate the diff as well not a problem and gut the user to download this and apply with git....locally user can also run the same command with fixAll.... I can also recommend that command ?

@fabiobaltieri
Copy link
Member

fabiobaltieri commented Jul 9, 2025

@fabiobaltieri by annotated you mean something like this
...
Or report the Text Edits e.g Start Line 5 col: 10 End: line 6 co:1 replace text = '...'?

GitHub has some known format for workflow outputs so that the annotations show up on the UI together with stuff like code review comments, this is documented here apparently. The check_compliance script uses the fmtd_failure function to print them, for example

self.fmtd_failure('warning', f'YAMLLint ({p.rule})', file,
p.line, col=p.column, desc=p.desc)

Is there a way of getting the lint error from your script ingested in the check compliance script in a structured way so that they could produce these kind of formatted failure messages? In a similar way as how the yaml linter was integrated in the example above. Clang and Pylint do it as well.

I will extend to generate the diff as well not a problem and gut the user to download this and apply with git....locally user can also run the same command with fixAll.... I can also recommend that command ?

I suppose it's possible but asking the user to download a patch file and apply locally sounds like a very crude approach, none of the other liners do anything like that, the expectations would be that either the comment is clear enough for the user to fix the issue manually (most of these) or the user can run the command locally (clang, ruff I think both support that).

I think if you add a message on the error on how to have the tool fix it for you it'd be plenty good enough.

Use dts-linter to check each touched file in PR

Signed-off-by: Kyle Micallef Bonnici <kylebonnici@hotmail.com>
@kylebonnici kylebonnici force-pushed the feat/add-dts-compliance-check branch from ddf725b to 038c803 Compare July 13, 2025 17:36
@github-actions github-actions bot added the area: Coding Guidelines Coding guidelines and style label Jul 13, 2025
Copy link

@kylebonnici
Copy link
Contributor Author

@fabiobaltieri I have added annotation (still to update this PR) but I have found some strange case where not all annotation show... have you ever had such an issue?

look at the bellow two runs ...
annotates not working
https://github.com/kylebonnici/zephyr/actions/runs/16258968300
(missing annotates for the nRF53)
working (with manual echo of the same strings...)
https://github.com/kylebonnici/zephyr/actions/runs/16259148578

@kylebonnici
Copy link
Contributor Author

kylebonnici commented Jul 14, 2025

Look like there are limits on how many annotation are shown

https://github.com/orgs/community/discussions/26680
https://github.com/orgs/community/discussions/68471
https://github.com/actions/toolkit/blob/main/docs/problem-matchers.md#limitations

For this reason I am considering notation the whole diff to the file .... I would assume all users can read diffs

See example here

https://github.com/kylebonnici/zephyr/actions/runs/16274081237

keith-zephyr added a commit to keith-zephyr/zephyr that referenced this pull request Jul 14, 2025
Add guidelines for how to split long property values across multiple
lines. This documents the style that is enforced by the devicetree
linter.

Link: zephyrproject-rtos#92334

Signed-off-by: Keith Short <keithshort@google.com>
keith-zephyr added a commit to keith-zephyr/zephyr that referenced this pull request Jul 15, 2025
Add guidelines for how to split long property values across multiple
lines. This documents the style that is enforced by the devicetree
linter.

Link: zephyrproject-rtos#92334

Signed-off-by: Keith Short <keithshort@google.com>
nashif pushed a commit that referenced this pull request Jul 19, 2025
Add guidelines for how to split long property values across multiple
lines. This documents the style that is enforced by the devicetree
linter.

Link: #92334

Signed-off-by: Keith Short <keithshort@google.com>
Chenhongren pushed a commit to Chenhongren/zephyr that referenced this pull request Jul 23, 2025
Add guidelines for how to split long property values across multiple
lines. This documents the style that is enforced by the devicetree
linter.

(cherry picked from commit b634ede)

Original-Link: zephyrproject-rtos#92334
Original-Signed-off-by: Keith Short <keithshort@google.com>
GitOrigin-RevId: b634ede
Cr-Build-Id: 8708896397616562065
Cr-Build-Url: https://cr-buildbucket.appspot.com/build/8708896397616562065
Copybot-Job-Name: zephyr-main-copybot-downstream
Change-Id: Ia04c700c669571d814a8f8d0f8fbca239f832116
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/zephyr/+/6772896
Bot-Commit: ChromeOS Copybot <copybot@chops-service-accounts.iam.gserviceaccount.com>
Commit-Queue: ChromeOS Copybot <copybot@chops-service-accounts.iam.gserviceaccount.com>
Tested-by: ChromeOS Copybot <copybot@chops-service-accounts.iam.gserviceaccount.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants