roschema 0.2.1__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {roschema-0.2.1 → roschema-0.4.0}/.github/workflows/ci.yml +3 -3
- {roschema-0.2.1 → roschema-0.4.0}/.github/workflows/release.yml +1 -1
- {roschema-0.2.1 → roschema-0.4.0}/CHANGELOG.md +43 -0
- {roschema-0.2.1 → roschema-0.4.0}/PKG-INFO +69 -21
- {roschema-0.2.1 → roschema-0.4.0}/README.md +67 -20
- {roschema-0.2.1 → roschema-0.4.0}/action/action.yml +25 -2
- {roschema-0.2.1 → roschema-0.4.0}/pyproject.toml +2 -1
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/cli.py +191 -32
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/config.py +24 -11
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/report/__init__.py +5 -1
- roschema-0.4.0/src/roschema/report/changes.py +92 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/report/github.py +14 -3
- roschema-0.4.0/src/roschema/report/json_report.py +87 -0
- roschema-0.4.0/src/roschema/report/markdown.py +92 -0
- roschema-0.4.0/src/roschema/report/summary.py +122 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/report/text.py +49 -13
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/rules/gendocs.py +28 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/rules/model.py +3 -1
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/golden_check.json +6 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/test_action.py +37 -8
- {roschema-0.2.1 → roschema-0.4.0}/tests/test_check_cli.py +153 -0
- roschema-0.4.0/tests/test_cli_explain.py +23 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/test_config.py +23 -1
- {roschema-0.2.1 → roschema-0.4.0}/tests/test_gendocs.py +14 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/test_golden.py +14 -7
- {roschema-0.2.1 → roschema-0.4.0}/tests/test_lint.py +8 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/test_mcap.py +42 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/test_report_formats.py +29 -1
- roschema-0.4.0/tests/test_report_markdown.py +61 -0
- roschema-0.2.1/src/roschema/report/json_report.py +0 -52
- roschema-0.2.1/src/roschema/report/summary.py +0 -60
- {roschema-0.2.1 → roschema-0.4.0}/.gitignore +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/CONTRIBUTING.md +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/LICENSE +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/docs/rules.md +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/__init__.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/deep.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/differ.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/discovery/__init__.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/discovery/discover.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/discovery/model.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/discovery/resolve.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/formatter.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/freeze.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/ir.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/loader.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/mcap_fs.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/parser/__init__.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/parser/core.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/parser/errors.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/py.typed +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/rules/__init__.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/rules/breaking/__init__.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/rules/breaking/rs001_field_removed.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/rules/breaking/rs002_field_added.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/rules/breaking/rs003_field_type_changed.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/rules/breaking/rs004_fields_reordered.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/rules/breaking/rs005_field_renamed.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/rules/breaking/rs006_array_kind_changed.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/rules/breaking/rs007_bound_tightened.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/rules/breaking/rs008_bound_loosened.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/rules/breaking/rs020_constant_removed.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/rules/breaking/rs021_constant_value_changed.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/rules/breaking/rs022_constant_type_changed.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/rules/breaking/rs030_default_changed.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/rules/breaking/rs040_interface_removed.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/rules/breaking/rs060_message_version.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/rules/breaking/rs110_transitive.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/rules/lint/__init__.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/rules/lint/_common.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/rules/lint/rsl001_field_snake_case.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/rules/lint/rsl002_constant_upper_snake.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/rules/lint/rsl003_name_pascal_case.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/rules/lint/rsl004_missing_doc.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/rules/lint/rsl005_discouraged_types.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/rules/lint/rsl006_unbounded.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/rules/lint/rsl010_srv_shadow.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/rules/registry.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/src/roschema/vfs.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/_helpers.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/conftest.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/AMENT_IGNORE +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/COLCON_IGNORE +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_new/demo_msgs/action/Move.action +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_new/demo_msgs/msg/ArrayKind.msg +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_new/demo_msgs/msg/BoundLoose.msg +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_new/demo_msgs/msg/BoundTight.msg +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_new/demo_msgs/msg/BoundedNest.msg +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_new/demo_msgs/msg/Constants.msg +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_new/demo_msgs/msg/Empty.msg +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_new/demo_msgs/msg/FieldAdded.msg +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_new/demo_msgs/msg/FieldRemoved.msg +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_new/demo_msgs/msg/Renamed.msg +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_new/demo_msgs/msg/Reordered.msg +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_new/demo_msgs/msg/Suppressed.msg +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_new/demo_msgs/msg/TypeChanged.msg +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_new/demo_msgs/msg/Unchanged.msg +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_new/demo_msgs/msg/WeirdSpacing.msg +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_new/demo_msgs/package.xml +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_new/demo_msgs/srv/DoThing.srv +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_old/demo_msgs/action/Move.action +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_old/demo_msgs/msg/ArrayKind.msg +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_old/demo_msgs/msg/BoundLoose.msg +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_old/demo_msgs/msg/BoundTight.msg +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_old/demo_msgs/msg/BoundedNest.msg +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_old/demo_msgs/msg/Constants.msg +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_old/demo_msgs/msg/Empty.msg +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_old/demo_msgs/msg/FieldAdded.msg +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_old/demo_msgs/msg/FieldRemoved.msg +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_old/demo_msgs/msg/Removed.msg +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_old/demo_msgs/msg/Renamed.msg +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_old/demo_msgs/msg/Reordered.msg +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_old/demo_msgs/msg/Suppressed.msg +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_old/demo_msgs/msg/TypeChanged.msg +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_old/demo_msgs/msg/Unchanged.msg +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_old/demo_msgs/msg/WeirdSpacing.msg +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_old/demo_msgs/package.xml +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/fixtures/ws_old/demo_msgs/srv/DoThing.srv +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/test_cli_ls.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/test_cli_version.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/test_deep.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/test_differ.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/test_discovery.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/test_fmt.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/test_freeze.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/test_parser.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/test_rules.py +0 -0
- {roschema-0.2.1 → roschema-0.4.0}/tests/test_vfs.py +0 -0
|
@@ -15,7 +15,7 @@ jobs:
|
|
|
15
15
|
runs-on: ubuntu-latest
|
|
16
16
|
steps:
|
|
17
17
|
- uses: actions/checkout@v4
|
|
18
|
-
- uses: astral-sh/setup-uv@
|
|
18
|
+
- uses: astral-sh/setup-uv@v10.3.3
|
|
19
19
|
with:
|
|
20
20
|
python-version: "3.12"
|
|
21
21
|
enable-cache: true
|
|
@@ -36,10 +36,10 @@ jobs:
|
|
|
36
36
|
strategy:
|
|
37
37
|
fail-fast: false
|
|
38
38
|
matrix:
|
|
39
|
-
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
39
|
+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
40
40
|
steps:
|
|
41
41
|
- uses: actions/checkout@v4
|
|
42
|
-
- uses: astral-sh/setup-uv@
|
|
42
|
+
- uses: astral-sh/setup-uv@v10.3.3
|
|
43
43
|
with:
|
|
44
44
|
python-version: ${{ matrix.python-version }}
|
|
45
45
|
enable-cache: true
|
|
@@ -3,6 +3,49 @@
|
|
|
3
3
|
The format follows [Keep a Changelog](https://keepachangelog.com/), and the
|
|
4
4
|
project adheres to [Semantic Versioning](https://semver.org/).
|
|
5
5
|
|
|
6
|
+
## [0.4.0] - 2026-11-03
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- JSON findings link to their rule's documentation (`help_uri`).
|
|
11
|
+
- `roschema explain RULE` prints a rule's reference section.
|
|
12
|
+
- `check --verbose` and `bag-check --verbose` list the changes that are not
|
|
13
|
+
reported (added, outside the level, rule off or suppressed) with the reason;
|
|
14
|
+
JSON adds a `changes` array.
|
|
15
|
+
- `--summary FILE` appends a Markdown report; the Action writes it to the job
|
|
16
|
+
summary, so every finding and the semver advice are visible even when GitHub
|
|
17
|
+
truncates the annotations.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- The semver advice suggests MINOR for a package that adds an interface or a
|
|
22
|
+
constant, and is printed even when there are no findings.
|
|
23
|
+
- The coverage line names the first few new interfaces and says "new" and
|
|
24
|
+
"removed" instead of "only in the target" and "only in the baseline".
|
|
25
|
+
|
|
26
|
+
## [0.3.0] - 2026-11-03
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- Reports say how many interfaces were compared, and on which side the rest
|
|
31
|
+
exist only (JSON: `coverage`).
|
|
32
|
+
- `--warnings-as-errors` for `check`, `bag-check` and `lint`, and the Action's
|
|
33
|
+
`warnings-as-errors` input.
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- Python 3.14 is supported and tested.
|
|
38
|
+
- An empty baseline is an error (exit `2`) instead of a silent pass; pass
|
|
39
|
+
`--allow-empty` (Action: `allow-empty`) when that is expected.
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
|
|
43
|
+
- `--format github` attaches findings with no new-side location, such as a
|
|
44
|
+
removed field or a reorder, to the interface's file instead of leaving GitHub
|
|
45
|
+
to show them against the workflow file.
|
|
46
|
+
- The Action uses `astral-sh/setup-uv@v10.3.3` (Node 24) with its cache off,
|
|
47
|
+
removing the Node 20 deprecation and missing `uv.lock` warnings.
|
|
48
|
+
|
|
6
49
|
## [0.2.1] - 2026-11-03
|
|
7
50
|
|
|
8
51
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: roschema
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Breaking-change detection, linting and formatting for ROS 2 interface definitions
|
|
5
5
|
Project-URL: Homepage, https://github.com/justagist/roschema
|
|
6
6
|
Project-URL: Documentation, https://github.com/justagist/roschema#readme
|
|
@@ -17,6 +17,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
21
|
Classifier: Topic :: Software Development :: Quality Assurance
|
|
21
22
|
Classifier: Typing :: Typed
|
|
22
23
|
Requires-Python: >=3.10
|
|
@@ -147,13 +148,17 @@ roschema bag-check recording.mcap
|
|
|
147
148
|
|
|
148
149
|
# what would be checked? (discovered packages and interfaces)
|
|
149
150
|
roschema ls
|
|
151
|
+
|
|
152
|
+
# what does a rule report, and why?
|
|
153
|
+
roschema explain RS004
|
|
150
154
|
```
|
|
151
155
|
|
|
152
156
|
## Checking
|
|
153
157
|
|
|
154
158
|
```
|
|
155
159
|
roschema check [TARGET] --against BASELINE [--level LEVEL] [--format text|json|github]
|
|
156
|
-
[--deep | --no-deep] [--exit-zero] [--
|
|
160
|
+
[--deep | --no-deep] [--exit-zero] [--warnings-as-errors] [--allow-empty]
|
|
161
|
+
[--verbose] [--summary FILE] [--config PATH] [--exclude GLOB]
|
|
157
162
|
```
|
|
158
163
|
|
|
159
164
|
`TARGET` selects what to check; omit it to check every interface package below
|
|
@@ -176,6 +181,16 @@ MCAP bag (`.mcap`) or a lockfile (`roschema.lock`); the type is detected
|
|
|
176
181
|
automatically. Initialised git submodules are compared at the commit the
|
|
177
182
|
baseline records for them.
|
|
178
183
|
|
|
184
|
+
Every report says how much was compared, for example `compared 12 interface(s)
|
|
185
|
+
in 3 package(s); 1 new (my_msgs/msg/Status)`. New interfaces are not findings,
|
|
186
|
+
since adding one breaks nothing; the line names the first few so that an
|
|
187
|
+
unexpected one (say, from an unrelated checkout below the target) is easy to
|
|
188
|
+
spot. A baseline with no interfaces at all is
|
|
189
|
+
an error (exit `2`): every current interface would look newly added, so the
|
|
190
|
+
check would pass without comparing anything. That usually means the wrong
|
|
191
|
+
baseline or target; on the pull request that adds the first interfaces, pass
|
|
192
|
+
`--allow-empty`.
|
|
193
|
+
|
|
179
194
|
Limitations:
|
|
180
195
|
|
|
181
196
|
- When a git ref and a directory share a name, the git ref wins; prefix the
|
|
@@ -199,7 +214,7 @@ followed.
|
|
|
199
214
|
|
|
200
215
|
```
|
|
201
216
|
roschema lint [TARGET] [--format text|json|github] [--rules RULE=SEV] [--exit-zero]
|
|
202
|
-
[--config PATH] [--exclude GLOB]
|
|
217
|
+
[--warnings-as-errors] [--config PATH] [--exclude GLOB]
|
|
203
218
|
roschema fmt [TARGET] [--check] [--exclude GLOB]
|
|
204
219
|
```
|
|
205
220
|
|
|
@@ -238,7 +253,8 @@ same. Comparison ignores it.
|
|
|
238
253
|
|
|
239
254
|
```
|
|
240
255
|
roschema bag-check BAG.mcap [TARGETS...] [--level LEVEL] [--config PATH]
|
|
241
|
-
[--format text|json|github] [--exit-zero] [--
|
|
256
|
+
[--format text|json|github] [--exit-zero] [--warnings-as-errors]
|
|
257
|
+
[--verbose] [--summary FILE] [--exclude GLOB]
|
|
242
258
|
```
|
|
243
259
|
|
|
244
260
|
Needs the `bag` extra (`uv tool install 'roschema[bag]'`).
|
|
@@ -327,19 +343,30 @@ allow every rule for that target.
|
|
|
327
343
|
|
|
328
344
|
## Output
|
|
329
345
|
|
|
330
|
-
**Text** (the default)
|
|
331
|
-
semver advice per package:
|
|
346
|
+
**Text** (the default) starts with what was compared, groups findings by
|
|
347
|
+
package and ends with a summary and a semver advice per package:
|
|
332
348
|
|
|
333
349
|
- **MAJOR** when the package has a wire- or source-level error;
|
|
334
|
-
- **MINOR** when
|
|
335
|
-
|
|
350
|
+
- **MINOR** when it adds an interface or a constant (neither is a finding,
|
|
351
|
+
since neither breaks anything), or an added field (RS002) whose severity you
|
|
352
|
+
lowered to `warn`;
|
|
336
353
|
- **PATCH** otherwise.
|
|
337
354
|
|
|
338
|
-
|
|
339
|
-
|
|
355
|
+
**`--verbose`** also lists, under "not reported", every change that produced no
|
|
356
|
+
reported finding, and why: a new interface or constant (no rule reports either,
|
|
357
|
+
at any level; an added field is RS002), a rule whose level is outside the one
|
|
358
|
+
being checked, a rule that is off, or a suppression, with the allowlist reason.
|
|
359
|
+
Comment-only edits are not changes, so they never appear.
|
|
360
|
+
|
|
361
|
+
**`--format github`** prints what was compared, then emits `::error`/`::warning`
|
|
362
|
+
workflow commands, which GitHub shows as annotations on the PR diff.
|
|
340
363
|
|
|
341
|
-
**`--
|
|
342
|
-
|
|
364
|
+
**`--summary FILE`** appends a Markdown report to `FILE` as well, whatever the
|
|
365
|
+
`--format`: what was compared, a table of findings linked to the rule
|
|
366
|
+
reference, the semver advice, and the unreported changes in a collapsed
|
|
367
|
+
section. Pointed at `$GITHUB_STEP_SUMMARY`, it becomes the job summary, which
|
|
368
|
+
matters because GitHub shows only the first 10 error and 10 warning annotations
|
|
369
|
+
of a step.
|
|
343
370
|
|
|
344
371
|
**`--format json`** emits a stable document (`version: 1`):
|
|
345
372
|
|
|
@@ -358,7 +385,8 @@ shows as annotations on the PR diff.
|
|
|
358
385
|
"message": "type changed float32 -> float64",
|
|
359
386
|
"old": { "file": "my_msgs/msg/Pose.msg", "line": 1 },
|
|
360
387
|
"new": { "file": "my_msgs/msg/Pose.msg", "line": 1 },
|
|
361
|
-
"suppressed": false
|
|
388
|
+
"suppressed": false,
|
|
389
|
+
"help_uri": "https://github.com/justagist/roschema/blob/v0.4.0/docs/rules.md#rs003----field-type-changed"
|
|
362
390
|
}
|
|
363
391
|
],
|
|
364
392
|
"summary": {
|
|
@@ -367,12 +395,24 @@ shows as annotations on the PR diff.
|
|
|
367
395
|
"warnings": 0,
|
|
368
396
|
"suppressed": 0,
|
|
369
397
|
"advisories": { "my_msgs": "MAJOR" }
|
|
398
|
+
},
|
|
399
|
+
"coverage": {
|
|
400
|
+
"packages": 1,
|
|
401
|
+
"compared": 1,
|
|
402
|
+
"target_only": 0,
|
|
403
|
+
"baseline_only": 0
|
|
370
404
|
}
|
|
371
405
|
}
|
|
372
406
|
```
|
|
373
407
|
|
|
374
408
|
`old`/`new` are `null` when there is no corresponding side (a removed interface
|
|
375
|
-
has no `new`). `
|
|
409
|
+
has no `new`). `coverage` counts interfaces present on both sides (`compared`)
|
|
410
|
+
and on one side only. `help_uri` links to the rule's section of the
|
|
411
|
+
[rule reference](docs/rules.md) for the installed release. With `--verbose`, a
|
|
412
|
+
`changes` array lists the unreported changes (each with `rule`, `package`,
|
|
413
|
+
`interface`, `section`, `target`, `message` and `reason`), including suppressed
|
|
414
|
+
findings with the reason they are suppressed. `lint` output has no
|
|
415
|
+
`advisories`, `coverage` or `changes`.
|
|
376
416
|
|
|
377
417
|
## Exit codes
|
|
378
418
|
|
|
@@ -380,15 +420,17 @@ has no `new`). `lint` output has no `advisories`.
|
|
|
380
420
|
|---|---|---|---|
|
|
381
421
|
| `0` | no unsuppressed errors (warnings allowed) | every file formatted | success |
|
|
382
422
|
| `1` | at least one unsuppressed error | a file would change | n/a |
|
|
383
|
-
| `2` | tool or usage error (bad target, baseline, configuration or file) | tool or usage error | tool or usage error |
|
|
423
|
+
| `2` | tool or usage error (bad target, baseline, configuration or file, or an empty baseline) | tool or usage error | tool or usage error |
|
|
384
424
|
|
|
385
|
-
`--
|
|
386
|
-
errors still exit
|
|
425
|
+
`--warnings-as-errors` makes unsuppressed warnings exit `1` too. `--exit-zero`
|
|
426
|
+
turns `1` into `0` for `check`, `bag-check` and `lint`; tool errors still exit
|
|
427
|
+
`2`.
|
|
387
428
|
|
|
388
429
|
## GitHub Action
|
|
389
430
|
|
|
390
431
|
A composite Action runs `roschema check` on the runner (no setup needed) and
|
|
391
|
-
posts the findings as annotations on the PR diff
|
|
432
|
+
posts the findings as annotations on the PR diff, with a full report (findings,
|
|
433
|
+
semver advice and unreported changes) as the job summary:
|
|
392
434
|
|
|
393
435
|
```yaml
|
|
394
436
|
# .github/workflows/interfaces.yml
|
|
@@ -403,9 +445,10 @@ jobs:
|
|
|
403
445
|
- uses: justagist/roschema/action@main
|
|
404
446
|
with:
|
|
405
447
|
against: origin/${{ github.base_ref }}
|
|
406
|
-
# version: "roschema==0.
|
|
448
|
+
# version: "roschema==0.4.0" # pin a release for reproducible results
|
|
407
449
|
# level: source
|
|
408
450
|
# deep: "true"
|
|
451
|
+
# warnings-as-errors: "true"
|
|
409
452
|
# exclude: "vendor_msgs* legacy_msgs/msg/Old"
|
|
410
453
|
```
|
|
411
454
|
|
|
@@ -416,6 +459,8 @@ jobs:
|
|
|
416
459
|
| `level` | (empty) | reporting level; empty uses the configured level, else `source` |
|
|
417
460
|
| `format` | `github` | `text`, `json` or `github` |
|
|
418
461
|
| `deep` | `"false"` | `"true"` adds `--deep` |
|
|
462
|
+
| `allow-empty` | `"false"` | `"true"` adds `--allow-empty` |
|
|
463
|
+
| `warnings-as-errors` | `"false"` | `"true"` adds `--warnings-as-errors` |
|
|
419
464
|
| `exclude` | (empty) | whitespace-separated globs passed as `--exclude` |
|
|
420
465
|
| `version` | `roschema` | the roschema requirement installed with `uvx` |
|
|
421
466
|
|
|
@@ -426,8 +471,10 @@ because it is part of the checkout.
|
|
|
426
471
|
|
|
427
472
|
## Troubleshooting
|
|
428
473
|
|
|
429
|
-
**"no breaking changes found", but you expected some.**
|
|
430
|
-
|
|
474
|
+
**"no breaking changes found", but you expected some.** The line under the
|
|
475
|
+
header says how many interfaces were compared, and `--verbose` lists the changes
|
|
476
|
+
that were seen but not reported, with the reason. Run `roschema ls` with the
|
|
477
|
+
same target to see which packages and interfaces are discovered, then check
|
|
431
478
|
that:
|
|
432
479
|
|
|
433
480
|
- the packages are not below a `COLCON_IGNORE` or `AMENT_IGNORE` marker;
|
|
@@ -445,6 +492,7 @@ shown in [Configuration](#configuration); the error suggests the closest one.
|
|
|
445
492
|
|
|
446
493
|
Every rule, its levels, default severity, rationale and an example is
|
|
447
494
|
documented in [docs/rules.md](docs/rules.md), generated from the rule metadata.
|
|
495
|
+
`roschema explain RULE` prints the same section in the terminal.
|
|
448
496
|
|
|
449
497
|
## Non-goals
|
|
450
498
|
|
|
@@ -113,13 +113,17 @@ roschema bag-check recording.mcap
|
|
|
113
113
|
|
|
114
114
|
# what would be checked? (discovered packages and interfaces)
|
|
115
115
|
roschema ls
|
|
116
|
+
|
|
117
|
+
# what does a rule report, and why?
|
|
118
|
+
roschema explain RS004
|
|
116
119
|
```
|
|
117
120
|
|
|
118
121
|
## Checking
|
|
119
122
|
|
|
120
123
|
```
|
|
121
124
|
roschema check [TARGET] --against BASELINE [--level LEVEL] [--format text|json|github]
|
|
122
|
-
[--deep | --no-deep] [--exit-zero] [--
|
|
125
|
+
[--deep | --no-deep] [--exit-zero] [--warnings-as-errors] [--allow-empty]
|
|
126
|
+
[--verbose] [--summary FILE] [--config PATH] [--exclude GLOB]
|
|
123
127
|
```
|
|
124
128
|
|
|
125
129
|
`TARGET` selects what to check; omit it to check every interface package below
|
|
@@ -142,6 +146,16 @@ MCAP bag (`.mcap`) or a lockfile (`roschema.lock`); the type is detected
|
|
|
142
146
|
automatically. Initialised git submodules are compared at the commit the
|
|
143
147
|
baseline records for them.
|
|
144
148
|
|
|
149
|
+
Every report says how much was compared, for example `compared 12 interface(s)
|
|
150
|
+
in 3 package(s); 1 new (my_msgs/msg/Status)`. New interfaces are not findings,
|
|
151
|
+
since adding one breaks nothing; the line names the first few so that an
|
|
152
|
+
unexpected one (say, from an unrelated checkout below the target) is easy to
|
|
153
|
+
spot. A baseline with no interfaces at all is
|
|
154
|
+
an error (exit `2`): every current interface would look newly added, so the
|
|
155
|
+
check would pass without comparing anything. That usually means the wrong
|
|
156
|
+
baseline or target; on the pull request that adds the first interfaces, pass
|
|
157
|
+
`--allow-empty`.
|
|
158
|
+
|
|
145
159
|
Limitations:
|
|
146
160
|
|
|
147
161
|
- When a git ref and a directory share a name, the git ref wins; prefix the
|
|
@@ -165,7 +179,7 @@ followed.
|
|
|
165
179
|
|
|
166
180
|
```
|
|
167
181
|
roschema lint [TARGET] [--format text|json|github] [--rules RULE=SEV] [--exit-zero]
|
|
168
|
-
[--config PATH] [--exclude GLOB]
|
|
182
|
+
[--warnings-as-errors] [--config PATH] [--exclude GLOB]
|
|
169
183
|
roschema fmt [TARGET] [--check] [--exclude GLOB]
|
|
170
184
|
```
|
|
171
185
|
|
|
@@ -204,7 +218,8 @@ same. Comparison ignores it.
|
|
|
204
218
|
|
|
205
219
|
```
|
|
206
220
|
roschema bag-check BAG.mcap [TARGETS...] [--level LEVEL] [--config PATH]
|
|
207
|
-
[--format text|json|github] [--exit-zero] [--
|
|
221
|
+
[--format text|json|github] [--exit-zero] [--warnings-as-errors]
|
|
222
|
+
[--verbose] [--summary FILE] [--exclude GLOB]
|
|
208
223
|
```
|
|
209
224
|
|
|
210
225
|
Needs the `bag` extra (`uv tool install 'roschema[bag]'`).
|
|
@@ -293,19 +308,30 @@ allow every rule for that target.
|
|
|
293
308
|
|
|
294
309
|
## Output
|
|
295
310
|
|
|
296
|
-
**Text** (the default)
|
|
297
|
-
semver advice per package:
|
|
311
|
+
**Text** (the default) starts with what was compared, groups findings by
|
|
312
|
+
package and ends with a summary and a semver advice per package:
|
|
298
313
|
|
|
299
314
|
- **MAJOR** when the package has a wire- or source-level error;
|
|
300
|
-
- **MINOR** when
|
|
301
|
-
|
|
315
|
+
- **MINOR** when it adds an interface or a constant (neither is a finding,
|
|
316
|
+
since neither breaks anything), or an added field (RS002) whose severity you
|
|
317
|
+
lowered to `warn`;
|
|
302
318
|
- **PATCH** otherwise.
|
|
303
319
|
|
|
304
|
-
|
|
305
|
-
|
|
320
|
+
**`--verbose`** also lists, under "not reported", every change that produced no
|
|
321
|
+
reported finding, and why: a new interface or constant (no rule reports either,
|
|
322
|
+
at any level; an added field is RS002), a rule whose level is outside the one
|
|
323
|
+
being checked, a rule that is off, or a suppression, with the allowlist reason.
|
|
324
|
+
Comment-only edits are not changes, so they never appear.
|
|
325
|
+
|
|
326
|
+
**`--format github`** prints what was compared, then emits `::error`/`::warning`
|
|
327
|
+
workflow commands, which GitHub shows as annotations on the PR diff.
|
|
306
328
|
|
|
307
|
-
**`--
|
|
308
|
-
|
|
329
|
+
**`--summary FILE`** appends a Markdown report to `FILE` as well, whatever the
|
|
330
|
+
`--format`: what was compared, a table of findings linked to the rule
|
|
331
|
+
reference, the semver advice, and the unreported changes in a collapsed
|
|
332
|
+
section. Pointed at `$GITHUB_STEP_SUMMARY`, it becomes the job summary, which
|
|
333
|
+
matters because GitHub shows only the first 10 error and 10 warning annotations
|
|
334
|
+
of a step.
|
|
309
335
|
|
|
310
336
|
**`--format json`** emits a stable document (`version: 1`):
|
|
311
337
|
|
|
@@ -324,7 +350,8 @@ shows as annotations on the PR diff.
|
|
|
324
350
|
"message": "type changed float32 -> float64",
|
|
325
351
|
"old": { "file": "my_msgs/msg/Pose.msg", "line": 1 },
|
|
326
352
|
"new": { "file": "my_msgs/msg/Pose.msg", "line": 1 },
|
|
327
|
-
"suppressed": false
|
|
353
|
+
"suppressed": false,
|
|
354
|
+
"help_uri": "https://github.com/justagist/roschema/blob/v0.4.0/docs/rules.md#rs003----field-type-changed"
|
|
328
355
|
}
|
|
329
356
|
],
|
|
330
357
|
"summary": {
|
|
@@ -333,12 +360,24 @@ shows as annotations on the PR diff.
|
|
|
333
360
|
"warnings": 0,
|
|
334
361
|
"suppressed": 0,
|
|
335
362
|
"advisories": { "my_msgs": "MAJOR" }
|
|
363
|
+
},
|
|
364
|
+
"coverage": {
|
|
365
|
+
"packages": 1,
|
|
366
|
+
"compared": 1,
|
|
367
|
+
"target_only": 0,
|
|
368
|
+
"baseline_only": 0
|
|
336
369
|
}
|
|
337
370
|
}
|
|
338
371
|
```
|
|
339
372
|
|
|
340
373
|
`old`/`new` are `null` when there is no corresponding side (a removed interface
|
|
341
|
-
has no `new`). `
|
|
374
|
+
has no `new`). `coverage` counts interfaces present on both sides (`compared`)
|
|
375
|
+
and on one side only. `help_uri` links to the rule's section of the
|
|
376
|
+
[rule reference](docs/rules.md) for the installed release. With `--verbose`, a
|
|
377
|
+
`changes` array lists the unreported changes (each with `rule`, `package`,
|
|
378
|
+
`interface`, `section`, `target`, `message` and `reason`), including suppressed
|
|
379
|
+
findings with the reason they are suppressed. `lint` output has no
|
|
380
|
+
`advisories`, `coverage` or `changes`.
|
|
342
381
|
|
|
343
382
|
## Exit codes
|
|
344
383
|
|
|
@@ -346,15 +385,17 @@ has no `new`). `lint` output has no `advisories`.
|
|
|
346
385
|
|---|---|---|---|
|
|
347
386
|
| `0` | no unsuppressed errors (warnings allowed) | every file formatted | success |
|
|
348
387
|
| `1` | at least one unsuppressed error | a file would change | n/a |
|
|
349
|
-
| `2` | tool or usage error (bad target, baseline, configuration or file) | tool or usage error | tool or usage error |
|
|
388
|
+
| `2` | tool or usage error (bad target, baseline, configuration or file, or an empty baseline) | tool or usage error | tool or usage error |
|
|
350
389
|
|
|
351
|
-
`--
|
|
352
|
-
errors still exit
|
|
390
|
+
`--warnings-as-errors` makes unsuppressed warnings exit `1` too. `--exit-zero`
|
|
391
|
+
turns `1` into `0` for `check`, `bag-check` and `lint`; tool errors still exit
|
|
392
|
+
`2`.
|
|
353
393
|
|
|
354
394
|
## GitHub Action
|
|
355
395
|
|
|
356
396
|
A composite Action runs `roschema check` on the runner (no setup needed) and
|
|
357
|
-
posts the findings as annotations on the PR diff
|
|
397
|
+
posts the findings as annotations on the PR diff, with a full report (findings,
|
|
398
|
+
semver advice and unreported changes) as the job summary:
|
|
358
399
|
|
|
359
400
|
```yaml
|
|
360
401
|
# .github/workflows/interfaces.yml
|
|
@@ -369,9 +410,10 @@ jobs:
|
|
|
369
410
|
- uses: justagist/roschema/action@main
|
|
370
411
|
with:
|
|
371
412
|
against: origin/${{ github.base_ref }}
|
|
372
|
-
# version: "roschema==0.
|
|
413
|
+
# version: "roschema==0.4.0" # pin a release for reproducible results
|
|
373
414
|
# level: source
|
|
374
415
|
# deep: "true"
|
|
416
|
+
# warnings-as-errors: "true"
|
|
375
417
|
# exclude: "vendor_msgs* legacy_msgs/msg/Old"
|
|
376
418
|
```
|
|
377
419
|
|
|
@@ -382,6 +424,8 @@ jobs:
|
|
|
382
424
|
| `level` | (empty) | reporting level; empty uses the configured level, else `source` |
|
|
383
425
|
| `format` | `github` | `text`, `json` or `github` |
|
|
384
426
|
| `deep` | `"false"` | `"true"` adds `--deep` |
|
|
427
|
+
| `allow-empty` | `"false"` | `"true"` adds `--allow-empty` |
|
|
428
|
+
| `warnings-as-errors` | `"false"` | `"true"` adds `--warnings-as-errors` |
|
|
385
429
|
| `exclude` | (empty) | whitespace-separated globs passed as `--exclude` |
|
|
386
430
|
| `version` | `roschema` | the roschema requirement installed with `uvx` |
|
|
387
431
|
|
|
@@ -392,8 +436,10 @@ because it is part of the checkout.
|
|
|
392
436
|
|
|
393
437
|
## Troubleshooting
|
|
394
438
|
|
|
395
|
-
**"no breaking changes found", but you expected some.**
|
|
396
|
-
|
|
439
|
+
**"no breaking changes found", but you expected some.** The line under the
|
|
440
|
+
header says how many interfaces were compared, and `--verbose` lists the changes
|
|
441
|
+
that were seen but not reported, with the reason. Run `roschema ls` with the
|
|
442
|
+
same target to see which packages and interfaces are discovered, then check
|
|
397
443
|
that:
|
|
398
444
|
|
|
399
445
|
- the packages are not below a `COLCON_IGNORE` or `AMENT_IGNORE` marker;
|
|
@@ -411,6 +457,7 @@ shown in [Configuration](#configuration); the error suggests the closest one.
|
|
|
411
457
|
|
|
412
458
|
Every rule, its levels, default severity, rationale and an example is
|
|
413
459
|
documented in [docs/rules.md](docs/rules.md), generated from the rule metadata.
|
|
460
|
+
`roschema explain RULE` prints the same section in the terminal.
|
|
414
461
|
|
|
415
462
|
## Non-goals
|
|
416
463
|
|
|
@@ -22,6 +22,14 @@ inputs:
|
|
|
22
22
|
deep:
|
|
23
23
|
description: Resolve nested types and report transitive breakage (RS110).
|
|
24
24
|
default: "false"
|
|
25
|
+
allow-empty:
|
|
26
|
+
description: >-
|
|
27
|
+
Pass when the baseline has no interfaces, e.g. on the pull request that
|
|
28
|
+
adds the first ones. Otherwise an empty baseline fails the step.
|
|
29
|
+
default: "false"
|
|
30
|
+
warnings-as-errors:
|
|
31
|
+
description: Fail the step on warnings as well as errors.
|
|
32
|
+
default: "false"
|
|
25
33
|
exclude:
|
|
26
34
|
description: >-
|
|
27
35
|
Whitespace-separated globs of packages/interfaces to skip. Split on
|
|
@@ -30,14 +38,18 @@ inputs:
|
|
|
30
38
|
version:
|
|
31
39
|
description: >-
|
|
32
40
|
roschema requirement for uvx. Unpinned runs the latest release (refreshed
|
|
33
|
-
on every run, even with a uv cache); pin it (e.g. "roschema==0.
|
|
41
|
+
on every run, even with a uv cache); pin it (e.g. "roschema==0.4.0") for
|
|
34
42
|
reproducible results.
|
|
35
43
|
default: roschema
|
|
36
44
|
|
|
37
45
|
runs:
|
|
38
46
|
using: composite
|
|
39
47
|
steps:
|
|
40
|
-
- uses: astral-sh/setup-uv@
|
|
48
|
+
- uses: astral-sh/setup-uv@v10.3.3
|
|
49
|
+
with:
|
|
50
|
+
# Nothing to key a cache on in the caller's repo, and uvx refreshes
|
|
51
|
+
# roschema on every run anyway.
|
|
52
|
+
enable-cache: false
|
|
41
53
|
- shell: bash
|
|
42
54
|
env:
|
|
43
55
|
ROSCHEMA_TARGET: ${{ inputs.target }}
|
|
@@ -45,6 +57,8 @@ runs:
|
|
|
45
57
|
ROSCHEMA_LEVEL: ${{ inputs.level }}
|
|
46
58
|
ROSCHEMA_FORMAT: ${{ inputs.format }}
|
|
47
59
|
ROSCHEMA_DEEP: ${{ inputs.deep }}
|
|
60
|
+
ROSCHEMA_ALLOW_EMPTY: ${{ inputs.allow-empty }}
|
|
61
|
+
ROSCHEMA_WARNINGS_AS_ERRORS: ${{ inputs.warnings-as-errors }}
|
|
48
62
|
ROSCHEMA_EXCLUDE: ${{ inputs.exclude }}
|
|
49
63
|
ROSCHEMA_VERSION: ${{ inputs.version }}
|
|
50
64
|
run: |
|
|
@@ -58,6 +72,15 @@ runs:
|
|
|
58
72
|
if [ "$ROSCHEMA_DEEP" = "true" ]; then
|
|
59
73
|
args+=(--deep)
|
|
60
74
|
fi
|
|
75
|
+
if [ "$ROSCHEMA_ALLOW_EMPTY" = "true" ]; then
|
|
76
|
+
args+=(--allow-empty)
|
|
77
|
+
fi
|
|
78
|
+
if [ "$ROSCHEMA_WARNINGS_AS_ERRORS" = "true" ]; then
|
|
79
|
+
args+=(--warnings-as-errors)
|
|
80
|
+
fi
|
|
81
|
+
if [ -n "${GITHUB_STEP_SUMMARY:-}" ]; then
|
|
82
|
+
args+=(--summary "$GITHUB_STEP_SUMMARY")
|
|
83
|
+
fi
|
|
61
84
|
read -r -a excludes <<< "${ROSCHEMA_EXCLUDE//$'\n'/ }"
|
|
62
85
|
for glob in "${excludes[@]}"; do
|
|
63
86
|
args+=(--exclude "$glob")
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "roschema"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.4.0"
|
|
8
8
|
description = "Breaking-change detection, linting and formatting for ROS 2 interface definitions"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "Apache-2.0"
|
|
@@ -19,6 +19,7 @@ classifiers = [
|
|
|
19
19
|
"Programming Language :: Python :: 3.11",
|
|
20
20
|
"Programming Language :: Python :: 3.12",
|
|
21
21
|
"Programming Language :: Python :: 3.13",
|
|
22
|
+
"Programming Language :: Python :: 3.14",
|
|
22
23
|
"Topic :: Software Development :: Quality Assurance",
|
|
23
24
|
"Typing :: Typed",
|
|
24
25
|
]
|