Skip to content

Commit c4bf9d2

Browse files
committed
[rules_score] add archver
- provide tool for validation of sw arch design - initial check for consistency between plantuml and bazel
1 parent 7c8dd8f commit c4bf9d2

11 files changed

Lines changed: 1292 additions & 1 deletion

File tree

.github/CODEOWNERS

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,6 @@
1616
# were modified. All directories should have a proper codeowner
1717
# Syntax: https://help.github.com/articles/about-codeowners/
1818

19-
/plantuml/ @castler @hoe-jo @LittleHuba @limdor @ramceb
2019
/bazel/rules/rules_score/ @castler @hoe-jo @LittleHuba @limdor @ramceb
20+
/plantuml/ @castler @hoe-jo @LittleHuba @limdor @ramceb
21+
/validation/ @castler @hoe-jo @LittleHuba @limdor @ramceb

plantuml/parser/puml_serializer/src/fbs/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ rust_library(
4040
],
4141
visibility = [
4242
"//plantuml/parser:__subpackages__",
43+
"//validation/archver:__pkg__",
4344
],
4445
deps = [
4546
"@crates//:flatbuffers",

validation/BUILD

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# *******************************************************************************
2+
# Copyright (c) 2026 Contributors to the Eclipse Foundation
3+
#
4+
# See the NOTICE file(s) distributed with this work for additional
5+
# information regarding copyright ownership.
6+
#
7+
# This program and the accompanying materials are made available under the
8+
# terms of the Apache License Version 2.0 which is available at
9+
# https://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# SPDX-License-Identifier: Apache-2.0
12+
# *******************************************************************************

validation/archver/BUILD

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# *******************************************************************************
2+
# Copyright (c) 2026 Contributors to the Eclipse Foundation
3+
#
4+
# See the NOTICE file(s) distributed with this work for additional
5+
# information regarding copyright ownership.
6+
#
7+
# This program and the accompanying materials are made available under the
8+
# terms of the Apache License Version 2.0 which is available at
9+
# https://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# SPDX-License-Identifier: Apache-2.0
12+
# *******************************************************************************
13+
load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_test")
14+
15+
rust_binary(
16+
name = "archver",
17+
srcs = [
18+
"src/bazel_reader.rs",
19+
"src/diagram_reader.rs",
20+
"src/main.rs",
21+
"src/models.rs",
22+
"src/validation.rs",
23+
],
24+
crate_root = "src/main.rs",
25+
visibility = ["//visibility:public"],
26+
deps = [
27+
"//plantuml/parser/puml_serializer/src/fbs:component_fbs",
28+
"@crates//:clap",
29+
"@crates//:flatbuffers",
30+
"@crates//:serde",
31+
"@crates//:serde_json",
32+
],
33+
)
34+
35+
rust_test(
36+
name = "archver_test",
37+
crate = ":archver",
38+
)

validation/archver/README.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<!--
2+
Copyright (c) 2026 Contributors to the Eclipse Foundation
3+
4+
See the NOTICE file(s) distributed with this work for additional
5+
information regarding copyright ownership.
6+
7+
This program and the accompanying materials are made available under the
8+
terms of the Apache License Version 2.0 which is available at
9+
https://www.apache.org/licenses/LICENSE-2.0
10+
11+
SPDX-License-Identifier: Apache-2.0
12+
-->
13+
14+
# Architecture Verifier (archver)
15+
16+
Validates that PlantUML component diagrams match the Bazel build graph structure.
17+
18+
## Overview
19+
20+
The archver tool ensures architectural consistency by comparing:
21+
- **Bazel build graph**: Dependable element, component and unit hierarchy from the build system
22+
- **PlantUML diagrams**: Static architecture documentation with stereotypes
23+
24+
## Usage
25+
26+
### Standalone CLI
27+
28+
```bash
29+
archver \
30+
--architecture-json path/to/architecture.json \
31+
--static-fbs path/to/diagram1.fbs.bin path/to/diagram2.fbs.bin
32+
33+
# Write a debug log in addition to stderr output
34+
archver \
35+
--architecture-json path/to/architecture.json \
36+
--static-fbs path/to/diagram.fbs.bin \
37+
--output path/to/archver.log
38+
```
39+
40+
Exits with code `0` on success, `1` on any validation error or I/O failure.
41+
42+
## What is Validated
43+
44+
### Dependable Element
45+
The dependable element is represented in the Bazel JSON as a top-level component
46+
entry. In PlantUML, it corresponds to a `package` with stereotype `<<SEooC>>`.
47+
The alias of the PlantUML package must match the Bazel dependable element name.
48+
49+
### Component Validation (Check 1)
50+
For each unique `(component_alias, parent_alias)` combination that Bazel expects:
51+
- PlantUML must have the exact same number of components with that combination
52+
- Uses target name only (package path is stripped)
53+
- Components nested under the dependable element have the dependable element as parent
54+
- Entities must have stereotype `<<component>>`
55+
56+
### Unit Validation (Check 2)
57+
For each unique `(unit_alias, parent_component_alias)` combination that Bazel expects:
58+
- PlantUML must have the exact same number of units with that combination
59+
- Uses target name only (e.g., `unit_1`, `unit_2`)
60+
- Entities must have stereotype `<<unit>>`
61+
62+
### Duplicate Detection
63+
If two Bazel targets resolve to the same `(alias, parent)` key (e.g., same target
64+
name in different packages), or two PlantUML entities have the same key, an error
65+
is emitted.
66+
67+
### Example
68+
If Bazel build graph has:
69+
- Dependable element: `safety_software_seooc_example` → key: `safety_software_seooc_example`
70+
- Component: `@//examples/seooc:component_example` → key: `component_example` (parent: `safety_software_seooc_example`)
71+
- Unit: `@//examples/seooc/unit_1:unit_1` → key: `unit_1` (parent: `component_example`)
72+
- Unit: `@//examples/seooc/unit_2:unit_2` → key: `unit_2` (parent: `component_example`)
73+
74+
PlantUML must have:
75+
```plantuml
76+
package "Safety Software SEooC Example" as safety_software_seooc_example <<SEooC>> {
77+
component "ComponentExample" as component_example <<component>> {
78+
component "Unit 1" as unit_1 <<unit>>
79+
component "Unit 2" as unit_2 <<unit>>
80+
}
81+
}
82+
```
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
' *******************************************************************************
2+
' Copyright (c) 2026 Contributors to the Eclipse Foundation
3+
'
4+
' See the NOTICE file(s) distributed with this work for additional
5+
' information regarding copyright ownership.
6+
'
7+
' This program and the accompanying materials are made available under the
8+
' terms of the Apache License Version 2.0 which is available at
9+
' https://www.apache.org/licenses/LICENSE-2.0
10+
'
11+
' SPDX-License-Identifier: Apache-2.0
12+
' *******************************************************************************
13+
14+
@startuml
15+
16+
package "archver" as archver {
17+
18+
component "main" as main <<component>> {
19+
component "Args" as args <<unit>>
20+
}
21+
22+
component "models" as models <<component>> {
23+
component "BazelInput" as bazel_input <<unit>>
24+
component "BazelArchitecture" as bazel_architecture <<unit>>
25+
component "DiagramInputs" as diagram_inputs_model <<unit>>
26+
component "DiagramInput" as diagram_input_model <<unit>>
27+
component "DiagramArchitecture" as diagram_architecture <<unit>>
28+
}
29+
30+
component "bazel_reader" as bazel_reader <<component>> {
31+
component "BazelReader" as bazel_reader_cls <<unit>>
32+
}
33+
34+
component "diagram_reader" as reader <<component>> {
35+
component "DiagramReader" as diagram_reader_cls <<unit>>
36+
}
37+
38+
component "validation" as validation <<component>> {
39+
component "Validator" as validator <<unit>>
40+
}
41+
42+
}
43+
44+
file "architecture.json\n(from dependable_element rule)" as arch_json_file
45+
file "*.fbs.bin\n(from architectural_design rule)" as fbs_files
46+
47+
main --> bazel_reader_cls : reads JSON
48+
main --> diagram_reader_cls : reads FBS files
49+
main --> validator : runs validation
50+
51+
bazel_reader_cls --> bazel_input : produces
52+
diagram_reader_cls --> diagram_inputs_model : produces
53+
54+
bazel_input --> bazel_architecture : to_bazel_architecture()
55+
diagram_inputs_model --> diagram_architecture : to_diagram_architecture()
56+
validator --> bazel_architecture : compares
57+
validator --> diagram_architecture : compares
58+
59+
arch_json_file ..> bazel_reader_cls : --architecture-json
60+
fbs_files ..> diagram_reader_cls : --static-fbs
61+
62+
bazel_architecture -[hidden]r-> diagram_architecture
63+
64+
@enduml
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// *******************************************************************************
2+
// Copyright (c) 2026 Contributors to the Eclipse Foundation
3+
//
4+
// See the NOTICE file(s) distributed with this work for additional
5+
// information regarding copyright ownership.
6+
//
7+
// This program and the accompanying materials are made available under the
8+
// terms of the Apache License Version 2.0 which is available at
9+
// <https://www.apache.org/licenses/LICENSE-2.0>
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
// *******************************************************************************
13+
14+
use std::fs;
15+
16+
use crate::models::BazelInput;
17+
18+
/// Reads the `architecture.json` file produced by the `dependable_element`
19+
/// Bazel rule and deserializes it into a [`BazelInput`] model.
20+
pub struct BazelReader;
21+
22+
impl BazelReader {
23+
/// Read and parse the architecture JSON at `path`.
24+
pub fn read(path: &str) -> Result<BazelInput, String> {
25+
let json_content =
26+
fs::read_to_string(path).map_err(|e| format!("Failed to read {path}: {e}"))?;
27+
serde_json::from_str(&json_content)
28+
.map_err(|e| format!("Failed to parse architecture JSON: {e}"))
29+
}
30+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// *******************************************************************************
2+
// Copyright (c) 2026 Contributors to the Eclipse Foundation
3+
//
4+
// See the NOTICE file(s) distributed with this work for additional
5+
// information regarding copyright ownership.
6+
//
7+
// This program and the accompanying materials are made available under the
8+
// terms of the Apache License Version 2.0 which is available at
9+
// <https://www.apache.org/licenses/LICENSE-2.0>
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
// *******************************************************************************
13+
14+
use crate::models::{DiagramInput, DiagramInputs};
15+
use component_fbs::component as fb_component;
16+
use std::fs;
17+
18+
pub struct DiagramReader;
19+
20+
impl DiagramReader {
21+
/// Read all `Component` and `Package` entities from the given FlatBuffers
22+
/// binary files.
23+
pub fn read(paths: &[String]) -> Result<DiagramInputs, String> {
24+
let mut out = Vec::new();
25+
26+
for path in paths {
27+
let data = fs::read(path).map_err(|e| format!("Failed to read {path}: {e}"))?;
28+
29+
let graph = flatbuffers::root::<fb_component::ComponentGraph>(&data)
30+
.map_err(|e| format!("Failed to parse FlatBuffer {path}: {e}"))?;
31+
32+
if let Some(entries) = graph.components() {
33+
for entry in entries.iter() {
34+
if let Some(comp) = entry.value() {
35+
match comp.comp_type() {
36+
fb_component::ComponentType::Component
37+
| fb_component::ComponentType::Package => {
38+
out.push(DiagramInput {
39+
id: comp.id().unwrap_or_default().to_string(),
40+
alias: comp.alias().map(|s| s.to_string()),
41+
parent_id: comp.parent_id().map(|s| s.to_string()),
42+
stereotype: comp.stereotype().map(|s| s.to_string()),
43+
});
44+
}
45+
// Other diagram entity types (Artifact, Database, etc.)
46+
// are not relevant for architecture verification.
47+
_ => {}
48+
}
49+
} else {
50+
return Err(format!(
51+
"FlatBuffer entry with key {:?} has null value in {path} (corrupted or truncated file)",
52+
entry.key()
53+
));
54+
}
55+
}
56+
}
57+
}
58+
59+
Ok(DiagramInputs { entities: out })
60+
}
61+
}

0 commit comments

Comments
 (0)