diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 4aca88cc..0078dedc 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,13 +1,6 @@ -This PR adds/fixes/refactors ... - -### Firmware image size difference + - - -- dronecan_v2: ? -> ? (+?) -- dronecan_v3: ? -> ? (+?) -- cyphal_v2: ? -> ? (+?) -- cyphal_v3: ? -> ? (+?) +This PR adds/fixes/refactors ... ### Test coverage diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fbfb4831..9cea515c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -152,14 +152,19 @@ jobs: shell: bash run: make $BUILD_TARGET + # Only vX.Y.Z tags are releases. Other tags (e.g. cubemx-v0.1.0) must not deploy. + - name: Check for a release tag + shell: bash + run: | + if [[ "${{ github.ref }}" =~ ^refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "IS_RELEASE_TAG=true" >> "$GITHUB_ENV" + fi + - name: Deploy binaries to Telegram if: > matrix.runner == 'ubuntu-24.04' && github.repository == 'RaccoonlabDev/mini_v2_node' && - ( - github.ref_name == github.event.repository.default_branch || - startsWith(github.ref, 'refs/tags/') - ) + env.IS_RELEASE_TAG == 'true' uses: PonomarevDA/telegram-upload@v1 with: bot_token: ${{ secrets.TELEGRAM_BOT_TOKEN }} diff --git a/.sonarcloud.properties b/.sonarcloud.properties new file mode 100644 index 00000000..3e5b5cca --- /dev/null +++ b/.sonarcloud.properties @@ -0,0 +1,10 @@ +# SonarCloud Automatic Analysis configuration. +# +# Automatic Analysis reads this file only. It ignores sonar-project.properties, +# which is why the previous configuration had no effect and the whole repository +# was scanned, submodules included. +# +# Src is the code we own. Libs/ and scripts/tools/ are git submodules: their +# findings cannot be fixed from this repository, and they accounted for every +# reported bug and all but one vulnerability. +sonar.sources=Src diff --git a/AGENTS.md b/AGENTS.md index e4a87748..a6ed8328 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,5 +4,13 @@ - When using as a framework (submodule, custom boards/targets), follow [docs/framework.md](docs/framework.md). - Prebuilt CubeMX HAL archives (CI without CubeMX): [docs/cubemx.md](docs/cubemx.md). - Firmware contract: [docs/embedded_firmware_contract.md](docs/embedded_firmware_contract.md). -- Match the code style: [docs/code_style.md](docs/code_style.md). +- Commit naming and code style: [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md). - When writing docs, follow [docs/documentation_guidelines.md](docs/documentation_guidelines.md). +- Treat Git as read-only: you may run inspection commands like `git status`, + `git diff`, and `git log`, but never modify Git state, including + staging/unstaging files, committing, branching, rebasing, resetting, checking + out, stashing, tagging, or changing config. +- Never touch remotes: no `git push`, `git pull`, `git fetch`, or any + equivalent through other tools (`gh`, scripts, CI triggers). This applies to + every branch, including the current one. Pushing is done only by a human, + ever. diff --git a/README.md b/README.md index aae28c3a..851b18c1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![CI](https://github.com/RaccoonlabDev/mini_v2_node/actions/workflows/ci.yml/badge.svg)](https://github.com/RaccoonlabDev/mini_v2_node/actions/workflows/ci.yml) [![SonarCloud](https://github.com/RaccoonlabDev/mini_v2_node/actions/workflows/sonarcloud.yml/badge.svg)](https://github.com/RaccoonlabDev/mini_v2_node/actions/workflows/sonarcloud.yml) [![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=RaccoonlabDev_mini_v2_node&metric=code_smells)](https://sonarcloud.io/summary/new_code?id=RaccoonlabDev_mini_v2_node) [![LOC](https://sonarcloud.io/api/project_badges/measure?project=RaccoonlabDev_mini_v2_node&metric=ncloc)](https://sonarcloud.io/summary/new_code?id=RaccoonlabDev_mini_v2_node) +[![CI](https://github.com/RaccoonlabDev/mini_v2_node/actions/workflows/ci.yml/badge.svg)](https://github.com/RaccoonlabDev/mini_v2_node/actions/workflows/ci.yml) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=RaccoonlabDev_mini_v2_node&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=RaccoonlabDev_mini_v2_node) [![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=RaccoonlabDev_mini_v2_node&metric=code_smells)](https://sonarcloud.io/summary/new_code?id=RaccoonlabDev_mini_v2_node) [![LOC](https://sonarcloud.io/api/project_badges/measure?project=RaccoonlabDev_mini_v2_node&metric=ncloc)](https://sonarcloud.io/summary/new_code?id=RaccoonlabDev_mini_v2_node) # General-purpose CAN node application diff --git a/cmake/create_binary_with_meaningful_name.cmake b/cmake/create_binary_with_meaningful_name.cmake index 6e741595..894ee816 100644 --- a/cmake/create_binary_with_meaningful_name.cmake +++ b/cmake/create_binary_with_meaningful_name.cmake @@ -39,15 +39,18 @@ if(DEFINED NC_TARGET AND NOT NC_TARGET STREQUAL "" AND set(RELEASE_BOARD_NAME "${BOARD_NAME}_${NC_TARGET}") endif() -set(RELEASE_HW_VERSION "${PLATFORM_NAME}") -if(NOT DEFINED RELEASE_HW_VERSION OR RELEASE_HW_VERSION STREQUAL "") - set(RELEASE_HW_VERSION "unknown") -endif() - +# BOARD_NAME already identifies the hardware (mini_v2, mini_v3, node_v4), so +# PLATFORM_NAME is deliberately not part of the file name: it would repeat the +# board suffix, and for a custom framework board it would name an unrelated +# MCU family. IMAGE_KIND is abbreviated here only - build dirs and make target +# names keep the full spelling. set(RELEASE_FW_TYPE "${IMAGE_KIND}") if(NOT DEFINED RELEASE_FW_TYPE OR RELEASE_FW_TYPE STREQUAL "") set(RELEASE_FW_TYPE "standalone") endif() +if(RELEASE_FW_TYPE STREQUAL "application") + set(RELEASE_FW_TYPE "app") +endif() set(RELEASE_COMMIT_HASH "${GIT_HASH_SHORT_8_DIGITS}") if(NOT RELEASE_APP_ROOT_DIR STREQUAL "" AND EXISTS "${RELEASE_APP_ROOT_DIR}/.git") @@ -100,7 +103,7 @@ if(NOT DEFINED RELEASE_FW_VERSION OR RELEASE_FW_VERSION STREQUAL "") endif() string(TIMESTAMP RELEASE_DATE "%Y.%m.%d") -set(DESTINATION_BIN "${DESTINATION_DIR}/${BOARD_VENDOR}_${RELEASE_BOARD_NAME}_${RELEASE_HW_VERSION}_${RELEASE_FW_TYPE}_${RELEASE_FW_VERSION}_${RELEASE_DATE}_${RELEASE_COMMIT_HASH}.bin") +set(DESTINATION_BIN "${DESTINATION_DIR}/${BOARD_VENDOR}_${RELEASE_BOARD_NAME}_${RELEASE_FW_TYPE}_${RELEASE_FW_VERSION}_${RELEASE_DATE}_${RELEASE_COMMIT_HASH}.bin") # Create the release directory if it doesn't exist file(MAKE_DIRECTORY "${DESTINATION_DIR}") diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md new file mode 100644 index 00000000..000424aa --- /dev/null +++ b/docs/CONTRIBUTING.md @@ -0,0 +1,55 @@ +# Contributing + +## Commits and pull requests + +Pull requests are squash-merged, so the pull request title becomes the commit +subject. One convention covers both: + +``` +(): +``` + +- Write the summary in the imperative, lower case, with no trailing dot. +- `` is optional and names the affected part: `fix(spi):`, `feat(params):`. +- A scope is not a type: write `fix(rcout):`, not `rcout:`. + +| Type | Use for | +|---|---| +| `feat` | User-visible feature or new build target | +| `fix` | Bug fix or behaviour correction | +| `docs` | Documentation, including code comments | +| `tests` | Tests, test data, test helpers | +| `refactor` | Restructuring without intended behaviour change | +| `ci` | Workflows, checks, automation | +| `build` | Build system, CMake, release artifacts | +| `chore` | Dependencies, submodules, repository maintenance | + +## Code style + +We use [PX4-Autopilot code style](https://docs.px4.io/main/en/contribute/code), +with the following (minimal) modifications: + +- Indentation: use 4 spaces instead of 8-space Tabs +- Attach a pointer or reference operator (*, &) to the variable type (left) +- Use UPPER_CASE for constants names instead of kThisIsConstant + +``` +indent=spaces=4 +align-pointer=type +align-reference=type +``` + +```bash +make code_style +``` + +## Before opening a pull request + +- Build every target you touched: [build_system.md](build_system.md). +- Run `make code_style` and the tests: [python_test_guide.md](python_test_guide.md). +- Fill in the firmware size difference in the pull request template. + +## Writing docs + +Keep docs short and follow [documentation_guidelines.md](documentation_guidelines.md). +Known bugs and the tests covering them live in [issues.md](issues.md). diff --git a/docs/code_style.md b/docs/code_style.md deleted file mode 100644 index c8ae5191..00000000 --- a/docs/code_style.md +++ /dev/null @@ -1,19 +0,0 @@ -# Code style - -We use [PX4-Autopilot code style](https://docs.px4.io/main/en/contribute/code), with the following (minimal) modifications: - -- Indentation: use 4 spaces instead of 8-space Tabs -- Attach a pointer or reference operator (*, &) to the variable type (left) -- Use UPPER_CASE for constants names instead of kThisIsConstant - -``` -indent=spaces=4 -align-pointer=type -align-reference=type -``` - -## How to use? - -```bash -make check_format -``` diff --git a/scripts/kocherga_image.py b/scripts/kocherga_image.py index e7086101..25e4c75b 100755 --- a/scripts/kocherga_image.py +++ b/scripts/kocherga_image.py @@ -394,6 +394,19 @@ def _get_output_file_name(input_file_name: str, descriptor: AppDescriptor) -> st return f"{base_stem}-{descriptor}.bin" +def _validate_firmware_image_path(raw_path: str) -> str: + """Resolve the firmware image path and ensure it refers to a regular file. + + The path comes from a CLI argument; resolving and checking it before any + file system access keeps a malformed argument from reaching an unintended + target. + """ + resolved = os.path.realpath(raw_path) + if not os.path.isfile(resolved): + raise FileNotFoundError(f"Firmware image is not a regular file: {raw_path!r}") + return resolved + + def _validate_side_patch_path(raw_path: str, base_dir: str) -> str: """Resolve a side-patch target and ensure it stays within base_dir. @@ -488,20 +501,22 @@ def _main() -> int: return 0 _logger.debug(f"CLI arguments: {args}") + firmware_image = _validate_firmware_image_path(args.firmware_image) + # Read the input file. All operations done in-memory. - with open(args.firmware_image, "rb") as in_file: + with open(firmware_image, "rb") as in_file: img = in_file.read() model = ImageModel.construct_from_image(img, uninitialized_only=True) if not model: existing_model = ImageModel.construct_from_image(img) if existing_model and args.lazy: _logger.info( - f"Image {args.firmware_image!r} does not require processing because it already contains a " + f"Image {firmware_image!r} does not require processing because it already contains a " f"valid app descriptor: {existing_model.app_descriptor!r}" ) return 0 _logger.fatal( - f"An uninitialized app descriptor could not be found in {args.firmware_image!r}. " + f"An uninitialized app descriptor could not be found in {firmware_image!r}. " f"If this is intentional, use --lazy to squelch this error. " f"Existing app descriptor: {existing_model.app_descriptor if existing_model else None!r}" ) @@ -536,7 +551,7 @@ def _main() -> int: _logger.info(f"Final app descriptor: {model.app_descriptor!r}") # Write the resulting image into the output file. - out_name = _get_output_file_name(args.firmware_image, model.app_descriptor) + out_name = _get_output_file_name(firmware_image, model.app_descriptor) with open(out_name, "wb") as out_file: assert model.validate_app_descriptor(), "Internal logic error: output image validation failed" out_file.write(model.image) @@ -544,7 +559,7 @@ def _main() -> int: # Perform the side-patching. Constrain targets to the firmware image's own # directory to guard against path traversal from malformed CLI arguments. - base_dir = os.path.dirname(os.path.realpath(args.firmware_image)) + base_dir = os.path.dirname(firmware_image) for raw_path in args.side_patch: path = _validate_side_patch_path(raw_path, base_dir) with open(path, "rb") as f: diff --git a/sonar-project.properties b/sonar-project.properties deleted file mode 100644 index b9470519..00000000 --- a/sonar-project.properties +++ /dev/null @@ -1,10 +0,0 @@ -sonar.projectKey=RaccoonlabDev_mini_v2_node -sonar.organization=raccoonlabdev -sonar.sources=Src -sonar.cfamily.build-wrapper-output=bw-output -sonar.host.url=https://sonarcloud.io -sonar.cfamily.gcov.reportsPath=build - -sonar.issue.ignore.multicriteria=e1 -sonar.issue.ignore.multicriteria.e1.ruleKey=cpp:S5780 -sonar.issue.ignore.multicriteria.e1.resourceKey=**