From 43fa876b444a50dd22a5cf7bbbad3bf67c0d585f Mon Sep 17 00:00:00 2001 From: Wouter Born Date: Fri, 31 Jul 2026 08:34:29 +0200 Subject: [PATCH 1/2] Add Code formatting documentation --- docs/developer-guide/025-code-formatting.md | 520 ++++++++++++++++++++ 1 file changed, 520 insertions(+) create mode 100644 docs/developer-guide/025-code-formatting.md diff --git a/docs/developer-guide/025-code-formatting.md b/docs/developer-guide/025-code-formatting.md new file mode 100644 index 00000000..33dd1e54 --- /dev/null +++ b/docs/developer-guide/025-code-formatting.md @@ -0,0 +1,520 @@ +# Code formatting with Spotless + +OpenRemote projects use [Spotless](https://github.com/diffplug/spotless) to apply and verify consistent code formatting and license headers. + +Spotless is configured through [Gradle](https://gradle.org/) and is the authoritative implementation of the repository's formatting rules. + +## Working directory + +Always run Spotless commands from the root directory of the repository, not from an individual Gradle subproject or from the `ui` directory. + +For example: + +```shell +cd openremote +./gradlew spotlessCheck +``` + +This also applies to custom projects and other OpenRemote repositories. + +## Checking formatting + +Before submitting a pull request, check all supported files with: + +```shell +./gradlew spotlessCheck +``` + +This command does not modify files. It fails when one or more files do not comply with the configured formatting rules. + +## Integration with Gradle verification + +Spotless is included in Gradle's standard `check` lifecycle. Running: + +```shell +./gradlew check +``` + +also runs `spotlessCheck` together with the other configured verification tasks. + +Use `spotlessCheck` directly when you only want to check formatting without running the complete verification lifecycle. + +## Applying formatting + +To automatically format all supported files, run: + +```shell +./gradlew spotlessApply +``` + +Review the resulting changes before committing them. Depending on the file type, Spotless can update: + +* code layout; +* imports; +* indentation and whitespace; +* line endings and final newlines; +* license headers. + +## Backend and UI tasks + +Repositories that separate backend and UI sources, such as `openremote/openremote` and OpenRemote custom projects, provide additional task groups. + +To check only backend or UI sources, run: + +```shell +./gradlew spotlessBackendCheck +./gradlew spotlessUiCheck +``` + +To format only backend or UI sources, run: + +```shell +./gradlew spotlessBackendApply +./gradlew spotlessUiApply +``` + +These grouped tasks are not available in every repository. For example, the `openremote/extensions` repository does not separate its Spotless configuration into backend and UI groups. Use the general tasks there: + +```shell +./gradlew spotlessCheck +./gradlew spotlessApply +``` + +Run the following command from the repository root to see which Spotless tasks are available: + +```shell +./gradlew tasks --group verification +``` + +## Formatting an individual file type + +Spotless creates tasks for each configured format. This is useful when you only want to check or apply formatting to one type of source file. + +For example: + +```shell +./gradlew spotlessJavaApply +./gradlew spotlessTypescriptApply +``` + +The exact tasks and supported file types differ between repositories. + +## Java coding convention + +Java source code is formatted with [google-java-format](https://github.com/google/google-java-format), which formats Java code according to the [Google Java Style Guide](https://google.github.io/styleguide/javaguide.html). + +The formatting convention includes: + +* two-space block indentation; +* K&R-style braces; +* consistent line wrapping; +* standardized import ordering; +* removal of unused imports; +* consistent formatting of annotations and Javadoc. + +Run Spotless from the repository root to apply or check Java formatting: + +```shell +./gradlew spotlessJavaApply +./gradlew spotlessJavaCheck +``` + +The Google Java Style Guide also contains conventions that cannot be fully enforced by a formatter, including guidance on naming, class structure, programming practices, and documentation. Follow these conventions when writing or reviewing Java code. + +IDE integrations such as the IntelliJ IDEA google-java-format plugin can provide immediate feedback, but they may use a different formatter version. The output produced by the repository's Spotless configuration remains authoritative. + +## Installing the Git pre-push hook + +Spotless provides an optional [Git pre-push hook](https://github.com/diffplug/spotless/blob/main/plugin-gradle/README.md#git-hook) that checks formatting before code is pushed. + +Install the hook from the repository root: + +```shell +./gradlew spotlessInstallGitPrePushHook +``` + +After installation, pushing changes runs `spotlessCheck` automatically. + +When formatting problems are found, the hook: + +1. runs `spotlessApply` to fix the affected files; +2. aborts the push; +3. allows you to review and commit the formatting changes; +4. lets you push again after the changes have been committed. + +The hook is installed in the local Git checkout and is not shared automatically with other contributors. Each contributor who wants to use it must install it separately. + +The hook provides a useful local safeguard, but it does not replace the formatting checks run by continuous integration. + +## UI linting and formatting with Yarn + +Projects with a UI and corresponding scripts in their root `package.json`, such as `openremote/openremote` and OpenRemote custom projects, can also run [ESLint](https://eslint.org/) and [Prettier](https://prettier.io/) directly with [Yarn](https://yarnpkg.com/). + +Run these commands from the repository root. + +Check JavaScript and TypeScript with ESLint: + +```shell +yarn lint +``` + +Apply ESLint fixes where possible: + +```shell +yarn lint:fix +``` + +Apply Prettier formatting: + +```shell +yarn format +``` + +Check Prettier formatting without changing files: + +```shell +yarn format:check +``` + +When applying both ESLint fixes and Prettier formatting, run them in this order: + +```shell +yarn lint:fix +yarn format +``` + +ESLint may change imports or code structure. Running Prettier afterwards ensures that the final result is consistently formatted. + +These commands provide quick feedback when working exclusively on the UI. They do not completely replace Spotless because Spotless may also: + +* format backend and repository-level files; +* apply or verify license headers; +* run additional formatters; +* enforce repository-specific exclusions and rules. + +Before pushing changes, run the appropriate Spotless check from the repository root. + +## Basic editor settings + +The repository-level [EditorConfig](https://editorconfig.org/) file, `.editorconfig`, defines the default text-file settings: + +* UTF-8 character encoding; +* Unix-style LF line endings; +* two spaces for indentation; +* spaces instead of tab characters; +* a newline at the end of every file; +* removal of trailing whitespace. + +Trailing whitespace is not automatically removed from Markdown files because two trailing spaces can be used to create an explicit Markdown line break. + +Most modern IDEs support EditorConfig either directly or through an extension. Enable EditorConfig support and let the repository's `.editorconfig` control these settings. + +Avoid overriding the repository settings with conflicting global IDE preferences. + +When EditorConfig support is unavailable, configure the editor manually to use: + +```text +Encoding: UTF-8 +Line endings: LF +Indentation: 2 spaces +Tabs: disabled +Insert final newline: enabled +Trim trailing whitespace: enabled +``` + +Disable trailing-whitespace removal for Markdown files unless the editor understands Markdown's significant trailing spaces. + +## Prettier and ESLint responsibilities + +Prettier and ESLint have different responsibilities: + +* **Prettier** formats supported UI and documentation files. +* **ESLint** reports JavaScript and TypeScript code-quality problems and applies safe fixes where possible. +* **Spotless** coordinates these tools with the other repository formatters and performs additional checks such as license-header enforcement. + +The repository's ESLint configuration disables conflicting formatting rules through `eslint-config-prettier`. Do not configure ESLint as a second general-purpose formatter on top of Prettier. + +The recommended save sequence is: + +1. Apply ESLint fixes. +2. Format the resulting code with Prettier. +3. Let EditorConfig govern basic text-file properties. + +Always use the Prettier and ESLint versions installed by the repository. Avoid configuring an IDE to use unrelated global installations or a personal configuration file. + +## IntelliJ IDEA + +Useful plugins include: + +* [Spotless Gradle](https://plugins.jetbrains.com/plugin/18321-spotless-gradle) +* [google-java-format](https://plugins.jetbrains.com/plugin/8527-google-java-format) + +Open the root directory of the repository as the IntelliJ IDEA project. This allows the IDE to discover the Gradle project, root `package.json`, UI configuration files, and `.editorconfig`. + +### Configuring Prettier + +Open: + +```text +Settings +→ Languages & Frameworks +→ JavaScript +→ Prettier +``` + +Select **Automatic Prettier configuration**. The IDE should use: + +* the Prettier package installed by the repository; +* the closest `.prettierrc.json`; +* the applicable `.prettierignore`. + +Optionally enable: + +* **Run on save**; +* **Run on 'Reformat Code' action**. + +Do not select a globally installed Prettier package when the repository package can be detected. + +If automatic detection fails, use manual configuration and select the Prettier package provided by the repository. Do not create a separate IDE-only Prettier configuration. + +### Configuring ESLint + +Open: + +```text +Settings +→ Languages & Frameworks +→ JavaScript +→ Code Quality Tools +→ ESLint +``` + +Select **Automatic ESLint configuration**. + +The IDE should use the repository's ESLint installation and locate the configuration nearest to the file being edited. + +Optionally enable: + +```text +Run eslint --fix on save +``` + +For repositories with the ESLint configuration in `ui`, specify `ui` as the working directory only when automatic detection does not work correctly. + +Use ESLint for inspections and fixes and Prettier for formatting. When both run on save, ESLint fixes should be applied before Prettier formats the resulting code. + +Avoid enabling both the built-in JavaScript formatter and Prettier for the same save or reformat action because the two formatters may produce different output. + +### Configuring EditorConfig + +Ensure that EditorConfig support is enabled: + +```text +Settings +→ Editor +→ Code Style +→ Enable EditorConfig support +``` + +The IDE should display an EditorConfig indicator when editing files covered by the repository configuration. + +The repository configuration should take precedence over personal code-style settings for indentation, line endings, final newlines, and trailing whitespace. + +## Visual Studio Code + +Install these extensions: + +* [Prettier - Code formatter](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) +* [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) +* [Spotless Gradle](https://marketplace.visualstudio.com/items?itemName=richardwillis.vscode-spotless-gradle) +* [google-java-format](https://marketplace.visualstudio.com/items?itemName=JoseVSeb.google-java-format-for-vs-code) + +Open the repository root as the Visual Studio Code workspace. This allows the extensions to discover the root `package.json`, UI configuration files, and `.editorconfig`. + +Configure Prettier as the formatter for JavaScript and TypeScript and run ESLint fixes as a save action: + +```json +{ + "prettier.requireConfig": true, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" + }, + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true + }, + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true + } +} +``` + +Equivalent language-specific settings can be added for CSS, HTML, JSON, JSONC, Markdown, and YAML. + +Visual Studio Code requires a separate language block for each language. A combined key such as `[javascript][typescript]` does not configure both languages correctly. + +For repositories whose ESLint configuration is located in the `ui` directory, automatic detection should normally work. When it does not, add: + +```json +{ + "eslint.workingDirectories": ["./ui"] +} +``` + +Do not set `prettier.configPath` to a personal or global configuration. Doing so can cause the extension to ignore the repository's `ui/.prettierrc.json`. + +Similarly, do not set `eslint.options.overrideConfigFile` unless troubleshooting requires it. ESLint should automatically discover the repository configuration. + +After configuring the extensions, verify their output against the command-line tools: + +```shell +yarn lint +yarn format:check +./gradlew spotlessUiCheck +``` + +### Recommended Visual Studio Code whitespace settings + +EditorConfig should normally manage whitespace settings. When explicit Visual Studio Code defaults are needed, use: + +```json +{ + "editor.insertSpaces": true, + "editor.tabSize": 2, + "files.eol": "\n", + "files.insertFinalNewline": true, + "files.trimTrailingWhitespace": true, + "[markdown]": { + "files.trimTrailingWhitespace": false + } +} +``` + +These should be fallback settings rather than replacements for `.editorconfig`. + +## Applying large formatting changes + +A repository-wide `spotlessApply` can modify many files without changing their behavior. Mixing these changes with functional modifications makes pull requests difficult to review and makes later `git blame` results less useful. + +When applying a large formatting change: + +1. update and review the Spotless configuration first; +2. apply formatting across the repository; +3. keep the generated formatting changes separate from functional changes; +4. commit the formatting changes in a dedicated commit; +5. avoid making manual or behavioral changes in that commit. + +A dedicated formatting commit makes the change easier to review and allows it to be excluded from blame history. + +## Ignoring formatting commits in Git blame + +For large formatting-only commits, add the final commit hash to a [`.git-blame-ignore-revs`](https://docs.github.com/en/repositories/working-with-files/using-files/viewing-and-understanding-files#ignore-commits-in-the-blame-view) file in the root of the repository. + +For example: + +```text +# Applied the initial repository-wide Spotless formatting +0123456789abcdef0123456789abcdef01234567 +``` + +Use the full commit hash and include a comment explaining what the commit changed. + +The formatting commit must already exist before its hash can be added. Add the hash to `.git-blame-ignore-revs` in a separate follow-up commit. + +When a pull request is squash-merged, use the final commit hash created on the target branch rather than the hash of an intermediate commit from the pull-request branch. + +GitHub automatically uses a root-level `.git-blame-ignore-revs` file in its blame view. + +To use the file explicitly from the command line, run: + +```shell +git blame --ignore-revs-file .git-blame-ignore-revs +``` + +You can also configure the local repository to use it by default: + +```shell +git config blame.ignoreRevsFile .git-blame-ignore-revs +``` + +This configuration applies to the current repository. Add `--global` only when you intentionally want to use the same ignore-file path for all local repositories. + +Only add commits that are overwhelmingly mechanical, such as repository-wide formatting or line-ending changes. Do not ignore commits that contain meaningful functional changes because that would hide useful authorship and history information. + +## IDE formatting remains optional + +IDE integration provides faster feedback but does not replace the repository commands. + +An IDE extension may: + +* use a different formatter version; +* fail to locate the correct configuration; +* ignore part of `.prettierignore`; +* apply a built-in formatter before or after Prettier; +* format only the currently opened file. + +Before pushing changes, always run: + +```shell +./gradlew spotlessCheck +``` + +For repositories that separate backend and UI sources, a narrower check can be used while developing: + +```shell +./gradlew spotlessBackendCheck +./gradlew spotlessUiCheck +``` + +Run the complete `spotlessCheck` before submitting a substantial or cross-cutting pull request. + +## Continuous integration + +Pull requests are checked for formatting by GitHub Actions. In repositories with separate backend and UI task groups, the workflow can run the relevant Spotless check based on the files changed by the pull request. + +When a Spotless check fails: + +1. Run the corresponding `spotlessApply`, `spotlessBackendApply`, or `spotlessUiApply` task locally. +2. Review the changes. +3. Commit and push the formatted files. + +Do not manually reproduce the formatter output. The Gradle Spotless tasks are the authoritative formatting implementation. + +## Temporarily disabling formatting + +Some generated, third-party, or unusually formatted code may not be suitable for automatic formatting. Where the configured formatter supports it, formatting can be temporarily disabled using `spotless:off` and restored using `spotless:on` in the appropriate comment syntax: + +```java +// spotless:off +codeThatMustRetainItsOriginalFormatting(); +// spotless:on +``` + +Use this only for small, exceptional sections. Repository-wide exclusions should be added to the central Spotless configuration instead. + +## Configuration + +The main Spotless configuration is stored in: + +```text +spotless.gradle +``` + +Additional formatter configuration and license-header files may be stored under: + +```text +tools/spotless/ +``` + +For projects with a UI, the configuration also uses files such as: + +```text +.editorconfig +ui/.eslintrc.json +ui/.prettierignore +ui/.prettierrc.json +package.json +``` + +Changes to formatting configuration affect the entire repository and should preferably be reviewed separately from ordinary source-code changes. From d47317dac6497d96fbf267f10679b2c090ee6b7e Mon Sep 17 00:00:00 2001 From: Wouter Born Date: Fri, 31 Jul 2026 08:39:27 +0200 Subject: [PATCH 2/2] Add links to code formatting docs --- docs/20-quick-start.md | 2 ++ docs/developer-guide/020-setting-up-an-ide.md | 2 ++ .../060-licensing-guidelines-for-contributors.md | 2 ++ docs/developer-guide/110-working-on-ui-and-apps.md | 2 ++ 4 files changed, 8 insertions(+) diff --git a/docs/20-quick-start.md b/docs/20-quick-start.md index 3fbeeca7..287c2146 100644 --- a/docs/20-quick-start.md +++ b/docs/20-quick-start.md @@ -67,6 +67,8 @@ We follow the [GitHub Flow](https://docs.github.com/en/get-started/quickstart/gi When your changes are complete then create a Pull Request ensuring that your branch is up-to-date with the source branch and that code changes are covered by tests and that the full test suite passes. +Before opening the pull request, [check and apply the repository's code-formatting rules](./developer-guide/025-code-formatting.md). + ## Discuss OpenRemote Join us on the [community forum](https://forum.openremote.io/). diff --git a/docs/developer-guide/020-setting-up-an-ide.md b/docs/developer-guide/020-setting-up-an-ide.md index 86d321e7..1c3b777f 100644 --- a/docs/developer-guide/020-setting-up-an-ide.md +++ b/docs/developer-guide/020-setting-up-an-ide.md @@ -37,6 +37,8 @@ You can download the [IntelliJ Community Edition](https://www.jetbrains.com/idea - [Grep Console](https://plugins.jetbrains.com/plugin/7125-grep-console) - [Markdown Navigator](https://plugins.jetbrains.com/plugin/7896-markdown-navigator) +For formatter plugins and IDE settings, see [Code formatting with Spotless](./025-code-formatting.md#intellij-idea). + ##### Grep Console Styling The log messages of the running application can be colour-highlighted with the [GrepConsole plugin](https://plugins.jetbrains.com/plugin/7125-grep-console) and our [configuration](https://github.com/openremote/openremote/tree/master/tools/intellij). diff --git a/docs/developer-guide/060-licensing-guidelines-for-contributors.md b/docs/developer-guide/060-licensing-guidelines-for-contributors.md index 43881d40..1303cf9a 100644 --- a/docs/developer-guide/060-licensing-guidelines-for-contributors.md +++ b/docs/developer-guide/060-licensing-guidelines-for-contributors.md @@ -6,6 +6,8 @@ The `LICENSE.txt` file contains the whole text of the AGPL/GPL (or whatever lice Tip: use `./gradlew dependencies` on projects that use gradle as build tool. +[Spotless](./025-code-formatting.md) applies and verifies license headers for supported source files. + In all source files (except where not appropriate such as property files or test fixtures), include a copyright header: ``` diff --git a/docs/developer-guide/110-working-on-ui-and-apps.md b/docs/developer-guide/110-working-on-ui-and-apps.md index 2e76a10e..5e3ed330 100644 --- a/docs/developer-guide/110-working-on-ui-and-apps.md +++ b/docs/developer-guide/110-working-on-ui-and-apps.md @@ -56,6 +56,8 @@ Yarn workspaces are used to symlink the OpenRemote packages into a root `node_mo NPM scripts are used for build and development purposes (the main build tool for the entire code base is `gradle` so there are `gradle` tasks that launch NPM scripts - these `gradle` tasks have the prefix `npm` followed by the NPM script name e.g. `npmBuild`). +See [UI linting and formatting](./025-code-formatting.md#ui-linting-and-formatting-with-yarn) for the Yarn, ESLint, Prettier, and Spotless commands used by OpenRemote. + The following standard NPM scripts are used throughout the components and apps for consistency: * `clean` - Cleans up any build artefacts (typically uses the [`shx`](https://www.npmjs.com/package/shx) package)