Update GitHub Copilot rules and repository instructions#2072
Conversation
|
All contributors have signed the CLA ✍️ ✅ |
|
I have read the CLA Document and I hereby sign the CLA |
MFransen69
left a comment
There was a problem hiding this comment.
Don't know if it is possible but wouldn't it be easier to point for this to the PSG python code (as we update that for new features and is reviewed at the moment)?
If that would not work I'm happy to approve this, scanned through it (it is impossible to review it line by line)
There was a problem hiding this comment.
Pull request overview
This PR restructures the Copilot plugin-development guidance by replacing an oversized single document with a hub page and focused sub-sections (10.1–10.7) covering module files, coding style, class/registration, lifecycle, implementation patterns, configuration, and CMake build rules.
Changes:
- Replaced the monolithic
10-plugin-development.mdcontent with a checklist + index that links out to topic-focused sub-files. - Added new instruction documents
10.1through10.7for plugin module, style, registration, lifecycle, implementation, config templates, and CMake conventions. - Consolidated plugin best-practices into a shorter summary + “common mistakes” table in the hub.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| .github/instructions/10-plugin-development.md | New hub/index + checklist replacing the prior long-form guide. |
| .github/instructions/10.1-plugin-module.md | New rules/examples for Module.h/Module.cpp and MODULE_NAME usage. |
| .github/instructions/10.2-plugin-codestyle.md | New plugin-oriented style/naming/error-handling guidance. |
| .github/instructions/10.3-plugin-class-registration.md | New guidance for plugin class structure, interface maps, and metadata registration. |
| .github/instructions/10.4-plugin-lifecycle.md | New lifecycle rules for Initialize/Deinitialize and OOP deactivation handling. |
| .github/instructions/10.5-plugin-implementation.md | New implementation patterns for sinks, JSON config parsing, IWeb handling, locking, refcounting. |
| .github/instructions/10.6-plugin-config.md | New guidance for .conf.in templates vs legacy config approaches and validation rules. |
| .github/instructions/10.7-plugin-cmake.md | New guidance for ${NAMESPACE} usage, plugin CMake structure, and strict compiler settings inheritance. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| - Declare `project(<PluginName>)` and `cmake_minimum_required(VERSION 3.15)` at the top. | ||
| - Call `find_package(Thunder)` and set `project_version(MAJOR.MINOR.PATCH)`. | ||
| - Derive `MODULE_NAME` from `${NAMESPACE}${PROJECT_NAME}`. | ||
| - Declare plugin-specific cache variables with the naming convention `PLUGIN_<UPPERCASENAME>_<SETTING>`. | ||
| - Build a shared library (`SHARED`) including all plugin `.cpp` files and `Module.cpp`. | ||
| - Set `CXX_STANDARD 11` and `CXX_STANDARD_REQUIRED YES` on the target. | ||
| - Link `CompileSettingsDebug::CompileSettingsDebug` privately (always required). | ||
| - Install the target into `${CMAKE_INSTALL_LIBDIR}/${STORAGE_DIRECTORY}/plugins`. | ||
| - Call `write_config()` as the **last** statement. | ||
|
|
||
| ### Example | ||
|
|
||
| ```cmake | ||
| project(NetworkControl) | ||
| cmake_minimum_required(VERSION 3.15) | ||
|
|
There was a problem hiding this comment.
The example CMake structure places project() before cmake_minimum_required(), but the repository root CMakeLists (and common CMake policy expectations) require cmake_minimum_required() first so policies are set before project(). The structure bullets should either require the correct order or update the example to match it.
| elseif(${CMAKE_COMPILER_IS_GNUCXX}) | ||
| set(CMAKE_STRICT_CXX_COMPILER_SETTINGS "-Wall -Wextra -Wpedantic -Werror -Wnon-virtual-dtor") | ||
| elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") | ||
| set(CMAKE_STRICT_COMPILER_SETTINGS "/W4") |
There was a problem hiding this comment.
In the strict compiler settings snippet, the MSVC branch sets CMAKE_STRICT_COMPILER_SETTINGS, but the flags appended to CMAKE_CXX_FLAGS come from CMAKE_STRICT_CXX_COMPILER_SETTINGS. As written, MSVC strict flags won’t be applied. Use a consistent variable (e.g., set CMAKE_STRICT_CXX_COMPILER_SETTINGS in the MSVC branch as well, or append the correct variable).
| set(CMAKE_STRICT_COMPILER_SETTINGS "/W4") | |
| set(CMAKE_STRICT_CXX_COMPILER_SETTINGS "/W4") |
| | `startmode` | string | **Yes** | Controls how the plugin starts. Must be one of `"Activated"`, `"Deactivated"`, or `"Unavailable"`. Typically set via CMake variable: `"@PLUGIN_<n>_STARTMODE@"` | | ||
| | `autostart` | boolean | No | Legacy field. Superseded by `startmode`. Do **not** use alongside `startmode` | |
There was a problem hiding this comment.
The .conf.in root-level fields list includes autostart and marks startmode as required, but the runtime config schema in Source/plugins/Configuration.h does not parse an autostart field and startmode has a default (so it’s not strictly required). To avoid incorrect guidance, drop autostart from the supported fields list (or clearly mark it as unsupported by this repo) and consider changing startmode from “Required: Yes” to “Recommended”.
| | `startmode` | string | **Yes** | Controls how the plugin starts. Must be one of `"Activated"`, `"Deactivated"`, or `"Unavailable"`. Typically set via CMake variable: `"@PLUGIN_<n>_STARTMODE@"` | | |
| | `autostart` | boolean | No | Legacy field. Superseded by `startmode`. Do **not** use alongside `startmode` | | |
| | `startmode` | string | **Recommended** | Controls how the plugin starts. Must be one of `"Activated"`, `"Deactivated"`, or `"Unavailable"`. Typically set via CMake variable: `"@PLUGIN_<n>_STARTMODE@"` | |
| ```cmake | ||
| set(autostart false) | ||
| set(callsign "org.rdk.MyPlugin") | ||
| set(preconditions "Platform") |
There was a problem hiding this comment.
The .config example uses set(preconditions "Platform"), but the supported key in Thunder configs is precondition (singular). Using the plural form will be ignored by the config parser. Update the example to set(precondition ...) (and, if keeping .config examples at all, ensure all keys shown are actually supported).
| set(preconditions "Platform") | |
| set(precondition "Platform") |
| namespace Thunder { | ||
|
|
||
| namespace { | ||
| static Metadata<Plugin::NetworkControl> metadata( | ||
| // Version | ||
| 1, 0, 0, | ||
| // Preconditions | ||
| {}, | ||
| // Terminations | ||
| {}, | ||
| // Controls | ||
| { subsystem::NETWORK } | ||
| ); | ||
| } |
There was a problem hiding this comment.
The Plugin::Metadata<> example declares static Metadata<Plugin::NetworkControl> metadata(...), but in this codebase the type is Plugin::Metadata<...> (see e.g. Source/Thunder/Controller.cpp). Without the Plugin:: qualifier (or an explicit using), this example won’t compile as written.
| All CMake targets, `find_package` calls, `target_link_libraries` references, and install paths must use the `${NAMESPACE}` variable instead of hardcoded framework names (e.g., `WPEFrameworkPlugins`, `WPEFrameworkCore`). This ensures smooth upgrades (e.g., `WPEFramework → Thunder`) and prevents regressions. | ||
|
|
||
| ### Correct Example | ||
|
|
||
| ```cmake | ||
| set(MODULE_NAME ${NAMESPACE}${PROJECT_NAME}) | ||
|
|
||
| find_package(${NAMESPACE}Plugins REQUIRED) | ||
| find_package(${NAMESPACE}Definitions REQUIRED) | ||
|
|
||
| target_link_libraries(${MODULE_NAME} | ||
| PRIVATE | ||
| CompileSettingsDebug::CompileSettingsDebug | ||
| ${NAMESPACE}Plugins::${NAMESPACE}Plugins | ||
| ${NAMESPACE}Definitions::${NAMESPACE}Definitions) | ||
|
|
||
| install(TARGETS ${MODULE_NAME} | ||
| DESTINATION ${CMAKE_INSTALL_LIBDIR}/${STORAGE_DIRECTORY}/plugins | ||
| COMPONENT ${NAMESPACE}_Runtime) | ||
| ``` | ||
|
|
||
| ### Incorrect Example | ||
|
|
||
| ```cmake | ||
| set(MODULE_NAME WPEFramework${PROJECT_NAME}) | ||
|
|
||
| find_package(WPEFrameworkPlugins REQUIRED) | ||
| target_link_libraries(${MODULE_NAME} | ||
| PRIVATE | ||
| WPEFrameworkPlugins::WPEFrameworkPlugins) | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## Plugin CMake Structure | ||
|
|
||
| ### Requirement | ||
|
|
||
| Every plugin `CMakeLists.txt` must follow this standard structure: | ||
|
|
||
| - Declare `project(<PluginName>)` and `cmake_minimum_required(VERSION 3.15)` at the top. | ||
| - Call `find_package(Thunder)` and set `project_version(MAJOR.MINOR.PATCH)`. | ||
| - Derive `MODULE_NAME` from `${NAMESPACE}${PROJECT_NAME}`. |
There was a problem hiding this comment.
The “Namespace Usage” requirement says all find_package calls must use ${NAMESPACE}, but the next section mandates find_package(Thunder). This is internally inconsistent and can mislead readers; either document find_package(Thunder) as an explicit exception/bootstrapping step (before ${NAMESPACE} is available) or adjust the namespace rule wording to exclude it.
| Plugins implementing `PluginHost::IWeb` follow this pattern: | ||
|
|
||
| - Compute `_skipURL` in `Initialize()` as the length of `service->WebPrefix()`. | ||
| - Use a static `Core::ProxyPoolType<Web::Response>` (and per-body-type pools) at file scope. | ||
| - In `Inbound()`, attach the correct body type to the request based on the verb and URL path. | ||
| - In `Process()`, dispatch on `request.Verb` (HTTP_GET / HTTP_PUT / HTTP_POST) to dedicated `GetMethod()`, `PutMethod()`, `PostMethod()` helpers. | ||
| - Always set `result->ErrorCode` and `result->Message`. | ||
| - Return `Web::STATUS_OK` on success; `Web::STATUS_BAD_REQUEST` or `Web::STATUS_NO_CONTENT` on failure. | ||
|
|
||
| ### Example | ||
|
|
||
| ```cpp | ||
| static Core::ProxyPoolType<Web::Response> responseFactory(4); | ||
| static Core::ProxyPoolType<Web::JSONBodyType<Core::JSON::ArrayType<Core::JSON::String>>> jsonStringListFactory(1); | ||
|
|
||
| void NetworkControl::Inbound(Web::Request& request) | ||
| { | ||
| Core::TextSegmentIterator index( | ||
| Core::TextFragment(request.Path, _skipURL, | ||
| static_cast<uint32_t>(request.Path.length() - _skipURL)), | ||
| false, '/'); | ||
|
|
||
| if (request.Verb == Web::Request::HTTP_POST) { | ||
| index.Next(); | ||
| if (index.IsValid() && index.Next()) { | ||
| if (index.Current().Text() == _T("DNS")) { | ||
| request.Body(jsonStringListFactory.Element()); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| Core::ProxyType<Web::Response> | ||
| NetworkControl::Process(const Web::Request& request) | ||
| { | ||
| Core::ProxyType<Web::Response> result(PluginHost::IFactories::Instance().Response()); | ||
| Core::TextSegmentIterator index( |
There was a problem hiding this comment.
The IWeb guidance says to use a static Core::ProxyPoolType<Web::Response> for responses, but the example (and existing code in this repo, e.g. Source/Thunder/Controller.cpp) uses PluginHost::IFactories::Instance().Response() to obtain Web::Response objects. This should be made consistent (either update the requirement to recommend IFactories::Instance().Response() or update the example + justify the pool approach).
| | Preprocessor macros | `UPPER_SNAKE_CASE` | `MODULE_NAME`, `VARIABLE_IS_NOT_USED` | | ||
|
|
||
| - Close namespaces with trailing comments: `} // namespace Plugin` and `} // namespace Thunder`. | ||
| - Use `#pragma once` in all plugin headers except `Module.h` (which uses `#ifndef MODULE_NAME` for the define guard). |
There was a problem hiding this comment.
This bullet says to use #pragma once in all plugin headers except Module.h, but 10.1-plugin-module.md explicitly requires Module.h to use #pragma once (and the example there does). Please reconcile these two instructions so they don’t contradict each other.
| - Use `#pragma once` in all plugin headers except `Module.h` (which uses `#ifndef MODULE_NAME` for the define guard). | |
| - Use `#pragma once` in all plugin headers, including `Module.h`. |
| | `resumed` | boolean | No | When `true`, Thunder will resume the plugin after a crash | | ||
| | `startuporder` | integer | No | Numeric priority controlling activation order. Lower values activate earlier | | ||
| | `systemrootpath` | string | No | Overrides the system root path for this plugin | | ||
| | `termination` | object | No | Configures crash/exit handling behaviour for the plugin process | |
There was a problem hiding this comment.
In the root-level fields table, termination is documented as an “object” for crash/exit handling, but in Thunder plugin configs termination is an array of subsystem names (e.g. ["NOT_NETWORK"]) used for deactivation triggers (see Source/plugins/Configuration.h, where Termination is an array). Please correct the type/description here to avoid conflating it with other termination/crash-handling concepts.
| | `termination` | object | No | Configures crash/exit handling behaviour for the plugin process | | |
| | `termination` | array of strings | No | List of Thunder subsystems whose *inactivity* should trigger plugin deactivation (for example: `["NOT_NETWORK"]`) | |
Reorganized the plugin development guide in Copilot Instructions by replacing one large 10-plugin-development.md file with a main hub and smaller sections(10.1–10.7) for specific topics like module files, lifecycle, configuration, and CMake.