nuttx/Documentation/testing/nuttx-ci.rst
zhangning21 44cdacf607 ci: apply Depends-On dependencies to the memory report
The memory report builds this pull request merged into master, together
with the nuttx-apps default branch, and nothing else, so a companion or
predecessor pull request it declares is absent. For a breaking change the
target then fails to build and no report is produced at all, even though
the Build workflow already tests the declared sources through Depends-On.

Apply the declared dependencies before building, reusing the parser and
the fetch/cherry-pick sequence that build.yml uses, and mapping each
repository to its checkout exactly as build.yml does. A stacked nuttx
dependency is no more optional than an apps one: a pull request that uses
an API its predecessor introduces does not build without it.

As build.yml does, read the description through the API rather than
trusting the event payload, so that a manual re-run after editing a
Depends-On line applies the current declaration instead of the one the run
was created with. Unlike build.yml, a failed read stops the job rather
than falling back to the payload: build.yml resolves this once and hands
every target the same tree, while this job runs per target, so a fallback
could leave targets on different declarations while their results are
filed under one SHA.

A declared dependency that cannot be applied fails the job, and so does a
missing parser, a parser crash, or a status this step does not recognise:
each of those means the declaration was never evaluated, and continuing
would measure a combination nobody asked for. build.yml fails Fetch-Source
on the same conditions, and no other step in this job carries
continue-on-error, so falling back silently would be inconsistent with
both. A declaration that parses to nothing valid only warns, again
matching build.yml.

Forward the parser's warnings too. --print-state prints only the state, so
an entry the parser drops -- an unsupported repository, say -- would
otherwise leave no trace here at all, although build.yml annotates it, and
the source set named below would be silently incomplete.

The report is filed under the pull request head SHA rather than the SHA of
the tree that was built, so the measurement cannot be reproduced from that
SHA alone and cannot be split per dependency. That limits provenance, not
the measurement: a combined result is what the declaration asks for, and a
regression that only appears in combination is still a regression. Name
the whole source set in the step summary so the reader knows which heads
went into the number.

Note in the parser that the --print-state output is a parsed contract; the
edit gate that used to be its only caller is gone.

Update the CI documentation to match. Its Pull Request Dependencies
section attributes dependency application to build.yml's Fetch-Source
job alone, so after this change it would read as if the memory report
measured the normal source selection. Cross-reference the two sections
rather than restating the rules, which stay shared.

Signed-off-by: zhangning21 <zhangning21@xiaomi.com>
2026-09-15 08:47:25 -03:00

260 lines
11 KiB
ReStructuredText

================
NuttX CI Process
================
NuttX is a complex system with lots of compile-time configurable switches and
values. What's more is that NuttX supports hundreds of different hardware
targets, across multiple different architectures. This complexity makes it
critical to have CI processes for testing incoming patches.
.. note::
The NuttX CI resources are limited, and not everything in the kernel can be
reasonably tested in CI (for instance, there is currently no support for
testing directly on hardware targets via CI, although it's being worked on).
It is for this reason that NuttX still request patch authors perform their
own local testing (especially on hardware where applicable) to submit
alongside their patch so reviewers may have some confidence that the change
will not introduce obvious regressions.
.. tip::
NuttX is always appreciative of improvements to our processes! If you have
suggestions to improve the CI infrastructure, please let the community know.
Our CI team is currently bearing a high workload.
The focus of this documentation is the CI testing that takes place when a PR is
opened on the `nuttx <https://github.com/apache/nuttx>`_ (and `nuttx-apps
<https://github.com/apache/nuttx-apps>`_) GitHub repositories.
CI Stages
=========
When a PR is opened, the following CI actions take place:
* Pull request labeller assigns labels to the PR
* PR is checked using the host tool :doc:`checkpatch.sh
</components/tools/checkpatch>`
* Linting is performed using `Super-Linter
<https://github.com/super-linter/super-linter/pkgs/container/super-linter>`_
* Build tests are performed based on which files were modified
* Some CI runtime tests are performed on simulators/emulators through
:doc:`/testing/ntfc`.
* Memory footprint of a set of representative targets is tracked using
`MemBrowse <https://membrowse.com>`_ (see `Memory Footprint Tracking`_).
Pull Request Labelling
======================
This action is responsible for:
* Labelling the PR with the label(s) corresponding to changed files (i.e. files
changed under ``arch/arm/**`` are labelled ``Arch: arm``).
* Labelling the PR with a PR size (i.e. ``Size: XS``, ``Size: M``, etc.)
The workflow file for this action is located at
``.github/workflows/labeler.yml``. This file contains documentation in the form
of comments. To compute the PR labels, it:
* Gets information about the changes from GitHub (i.e. files and lines changed)
* Sums the total lines changed and assigns PR size labels based on this number
* Uses the wildcard paths-to-label assignments in `.github/labeler.yml` to
assign the correct change labels to PRs.
Types of labels are:
* Arch labels (i.e. ``Arch: arm``, ``Arch: risc-v``, etc), associated with
changes in ``arch/``
* Board labels (i.e. ``Board: arm``, ``Board: sim``, etc) associated with
changes in ``boards/``
* Area labels (i.e. ``Area: Bluetooth``, ``Area: Crypto``, ``Area: Drivers``)
associated with several different kernel "areas"
Once this workflow is done running, the PRs labels are updated by the computed
result and PRs in the pull-requests tab can be sorted according to these labels.
.. tip::
You can filter PRs by the ``Size: XS``, ``Size: S`` to review small PRs
quickly in between work :)
Checkpatch
==========
More information about the ``checkpatch.sh`` tool itself can be found at
:doc:`/components/tools/checkpatch`.
The goal of this action is to verify that the PR adheres to the :doc:`C coding
standard </contributing/coding_style>`, does not contain typos and adheres to
the :doc:`commit message format </contributing/making-changes>`. Additionally,
format checking of CMake files has been added to the checks.
The workflow file for this action is very short and can be found at
``.github/workflows/check.yml``.
Build
=====
This is the most complex step in the CI process, and also the longest duration.
It selects a category of NuttX configurations (``defconfig`` files) associated
with the changed files and builds them all in parallel. It also uses the
``tools/refresh.sh`` utility to check if any configurations need to be
normalized (see :doc:`/components/tools/refresh`) for more information).
The workflow file for this check is located at ``.github/workflows/build.yml``.
The name of this workflow is deceiving, as it does not only build
configurations, but also normalizes them and runs :doc:`NTFC </testing/ntfc>`
tests on architectures that support it.
Pull Request Dependencies
-------------------------
NuttX and ``nuttx-apps`` are built together. A change that spans both
repositories may therefore need to test two pull requests together before
either one is merged. A pull request targeting ``master`` can declare same- or
cross-repository dependencies in its description.
The recommended form is one dependency per line:
.. code-block:: text
Depends-On: https://github.com/apache/nuttx-apps/pull/1234
Depends-On: https://github.com/apache/nuttx/pull/5678
Alternatively, multiple dependencies can use a single bracket list:
.. code-block:: text
Depends-On: [apache/nuttx-apps/pull/1234 https://github.com/apache/nuttx/pull/5678]
The ``Depends-On:`` marker is case-insensitive; ``Depends-On:``,
``depends-on:``, and mixed-case spellings are equivalent. This documentation
uses ``Depends-On:`` as the canonical form.
References may use either a complete ``https://github.com/...`` URL or the
short ``owner/repository/pull/number`` form, and the forms may be mixed. Each
accepted reference must identify a pull request in ``apache/nuttx`` or
``apache/nuttx-apps``. Other tokens are ignored; a declaration is invalid only
when a ``Depends-On:`` marker is present but no valid dependency can be parsed.
Multiple declarations are applied in their listed order, and duplicate
references are applied only once. Each declaration must remain on one line;
Markdown bullet-list continuations are not supported. Pull requests without a
``Depends-On:`` declaration retain the normal CI source selection.
Declarations in release and backport pull requests are ignored because
dependencies are applied only when the target branch is ``master``.
The ``Fetch-Source`` job fetches each valid dependency's exact head SHA and
cherry-picks its commits into the corresponding checkout before the existing
build matrix runs. Invalid declarations are not applied: CI continues with the
normal source selection and posts a warning that the declaration could not be
parsed. If a valid dependency cannot be fetched, has no common history, its
commit list cannot be determined, or it causes a cherry-pick conflict,
``Fetch-Source`` fails instead of silently testing without the requested
dependency.
The memory footprint workflow applies the same declarations independently,
using the same parser and apply sequence (see `Memory Footprint Tracking`_).
When a valid dependency report is available, the follow-up comment reports one
of three outcomes:
* successfully applied dependencies and their fetched head SHAs (abbreviated in
the comment)
* a declaration from which no valid dependency could be parsed
* valid dependencies that could not be applied and the failure reason
The write-capable follow-up workflow does not execute fork code. It validates
the untrusted report's structure, repository allow-list, run and current PR
head binding, and fixed rendering fields before commenting. These checks make
posting the report safe, but do not independently attest that the dependency
was applied; the comment reflects the result produced by the read-only Build
workflow.
Editing the pull request description does not trigger CI. Every Build run
reads the current description when it starts. After changing a
``Depends-On:`` declaration, retrigger CI in one of these ways:
* push new or rebased commits to the pull request branch
* close and reopen the pull request
* press "Re-run all jobs" on the existing Build run
Updating a dependency pull request does not automatically trigger the
initiating pull request either, so its CI must be rerun to test the new
dependency head.
The combined result belongs to the initiating pull request. It does not set a
status on dependency pull requests, merge them automatically, or replace the
need to coordinate their merge order.
The steps executed in the build workflow are:
1. Fetch source: checks out ``nuttx`` and ``nuttx-apps`` repos. The source files
are added as an artifact to the GitHub action so that it can be downloaded by
subsequent steps.
2. In parallel, the builds to be performed are selected for each host OS/setup
supported by NuttX. These are Linux, MacOS, MSVC and MYSYS2.
.. note::
Typically only performed in the Linux environment. Performing the builds
across all host options consumes too many resources.
MacOS builds are currently always skipped, but sometimes tests are
performed for MSVC and MYSYS2. These builds are a small subset of the
total builds performed on Linux, but which were selected to still cover a
range of architectures. Some selected builds are ``raspberrypi-pico:nsh``,
``rv-virt:nsh``, ``sim:windows``, etc.
3. Once the build is selected for a host configuration (i.e. Linux), the builds
are performed in parallel. For example, if in Step 2 the selected Linux
builds were ``arm-01``, ``arm-02`` and ``arm-03``, this step will perform the
build test for each category in parallel.
This involves compiling all of the ``defconfig`` configurations included in
each category and running ``tools/refresh.sh`` on them. Some categories, like
``sim-01`` also perform NTFC tests. In this case, the NTFC runtime tests are
run using the architecture's designated ``citest/defconfig`` configuration.
4. After each category's builds are complete, `make host_info` is run in each
category's runner as a sanity check.
5. After all the selected Linux builds complete, an out-of-tree (OOT) build test
is performed. This is done using ``tools/ci/cibuild-oot.sh``.
Each build category (``arm-01``, etc.) produces a build artifact which contains
all of the resulting build outputs for that category. This includes the
resulting ``nuttx.bin``, ``nuttx.elf``, ``nuttx.hex``, etc., binaries. You
should be able to download the artifact, flash the NuttX image(s) to your target
device and test them that way if you'd like to avoid building all of the images
yourself!
Memory Footprint Tracking
=========================
Dashboard: `<https://membrowse.com/public/apache/nuttx>`_
The memory footprint of a set of representative targets is tracked over time
using `MemBrowse <https://membrowse.com>`_. This makes flash/RAM usage changes
visible on a per-section and per-symbol basis and helps catch unintended size
regressions. It:
* provides a trend graph of the builds across commits
* lets you compare the footprint between any two uploaded commits
* posts a footprint summary comment on each pull request
.. image:: image/membrowse-targets.png
:alt: MemBrowse targets page showing the current memory usage of each tracked NuttX target
The integration consists of:
* the set of tracked targets, configured in ``.github/membrowse-targets.json``
* the ``membrowse-*.yml`` workflows under ``.github/workflows/`` that drive it
The memory report applies the same ``Depends-On:`` declarations as the Build
workflow (see `Pull Request Dependencies`_), so a pull request that only builds
on top of another one is measured against a tree that compiles. The declaration
rules and the ``master``-only gate are the same. A dependency that cannot be
fetched or applied fails the job in both workflows.