Skip to content

Commit 2df3908

Browse files
authored
Implement validity attributes (#274)
Derive requirements for validity attributes introduced in score_process 1.2.0.
1 parent eb09154 commit 2df3908

File tree

8 files changed

+134
-2
lines changed

8 files changed

+134
-2
lines changed

MODULE.bazel

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
module(
1515
name = "score_docs_as_code",
16-
version = "1.3.0",
16+
version = "1.4.0",
1717
compatibility_level = 1,
1818
)
1919

docs/requirements/requirements.rst

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,32 @@ Mapping
427427
No concept yet
428428

429429

430+
.. tool_req:: Enforce validity attribute correctness
431+
:id: tool_req__docs_req_attr_validity_correctness
432+
:tags: Requirements
433+
:implemented: PARTIAL
434+
:parent_covered: YES
435+
:satisfies: PROCESS_gd_req__req_validity
436+
:status: valid
437+
438+
Docs-as-Code shall enforce that the ``valid_from`` and ``valid_until`` attributes of stakeholder and feature requirements are correct.
439+
440+
The format of a milestone is something like "v0.5" or "v1.0.1".
441+
No suffixes like "-SNAPSHOT" or "-beta" are allowed.
442+
443+
.. tool_req:: Enforce validity start is before end
444+
:id: tool_req__docs_req_attr_validity_consistency
445+
:tags: Requirements
446+
:implemented: PARTIAL
447+
:parent_covered: YES
448+
:satisfies: PROCESS_gd_req__req_validity
449+
:status: valid
450+
451+
Docs-as-Code shall enforce that ``valid_from`` is before ``valid_until`` attribute in stakeholder and feature requirements.
452+
We consider "from" is inclusive but "until" is exclusive, so from v0.5 until v1.0 means valid for v0.5 but not for v1.0.
453+
If either attribute is missing, no check is performed.
454+
455+
430456
-------------------------
431457
🔗 Links
432458
-------------------------

src/extensions/score_metamodel/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ def parse_checks_filter(filter: str) -> list[str]:
6262
}
6363
for check in checks:
6464
assert check in all_check_names, (
65-
f"Check: '{check}' is not one of the defined local or graph checks"
65+
f"Check: '{check}' is not one of the defined local or graph checks: "
66+
+ ", ".join(all_check_names)
6667
)
6768

6869
return checks

src/extensions/score_metamodel/checks/check_options.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,3 +207,43 @@ def check_extra_options(
207207
extra_options_str = ", ".join(f"`{option}`" for option in extra_options)
208208
msg = f"has these extra options: {extra_options_str}."
209209
log.warning_for_need(need, msg)
210+
211+
212+
def parse_milestone(value: str) -> tuple[int, int, int]:
213+
"""Parse a string like 'v0.5' or 'v1.0.0'. No suffixes."""
214+
match = re.match(r"v(\d+)(\.(\d+))?(\.(\d+))?$", value)
215+
if not match:
216+
raise ValueError(f"Invalid milestone format: {value}")
217+
major = int(match.group(1))
218+
minor = int(match.group(3) or 0)
219+
patch = int(match.group(5) or 0)
220+
return (major, minor, patch)
221+
222+
223+
# req-Id: tool_req__docs_req_attr_validity_consistency
224+
@local_check
225+
def check_validity_consistency(
226+
app: Sphinx,
227+
need: NeedsInfoType,
228+
log: CheckLogger,
229+
):
230+
"""
231+
Check if the attributes valid_from < valid_until.
232+
"""
233+
if need["type"] not in ("stkh_req", "feat_req"):
234+
return
235+
236+
valid_from = need.get("valid_from", None)
237+
valid_until = need.get("valid_until", None)
238+
239+
if not valid_from or not valid_until:
240+
return
241+
242+
valid_from_version = parse_milestone(valid_from)
243+
valid_until_version = parse_milestone(valid_until)
244+
if valid_from_version >= valid_until_version:
245+
msg = (
246+
"inconsistent validity: "
247+
f"valid_from ({valid_from}) >= valid_until ({valid_until})."
248+
)
249+
log.warning_for_need(need, msg)

src/extensions/score_metamodel/metamodel.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,9 @@ needs_types:
261261
# req-Id: tool_req__docs_req_attr_testcov
262262
testcovered: ^(YES|NO)$
263263
hash: ^.*$
264+
# req-Id: tool_req__docs_req_attr_validity_correctness
265+
valid_from: ^v(0|[1-9]\d*)\.(0|[1-9]\d*)(\.(0|[1-9]\d*))?$
266+
valid_until: ^v(0|[1-9]\d*)\.(0|[1-9]\d*)(\.(0|[1-9]\d*))?$
264267
tags:
265268
- requirement
266269
- requirement_excl_process
@@ -291,6 +294,9 @@ needs_types:
291294
# req-Id: tool_req__docs_req_attr_testcov
292295
testcovered: ^(YES|NO)$
293296
hash: ^.*$
297+
# req-Id: tool_req__docs_req_attr_validity_correctness
298+
valid_from: ^v(0|[1-9]\d*)\.(0|[1-9]\d*)(\.(0|[1-9]\d*))?$
299+
valid_until: ^v(0|[1-9]\d*)\.(0|[1-9]\d*)(\.(0|[1-9]\d*))?$
294300
tags:
295301
- requirement
296302
- requirement_excl_process
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
..
2+
# *******************************************************************************
3+
# Copyright (c) 2025 Contributors to the Eclipse Foundation
4+
#
5+
# See the NOTICE file(s) distributed with this work for additional
6+
# information regarding copyright ownership.
7+
#
8+
# This program and the accompanying materials are made available under the
9+
# terms of the Apache License Version 2.0 which is available at
10+
# https://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# SPDX-License-Identifier: Apache-2.0
13+
# *******************************************************************************
14+
#CHECK: check_validity_consistency
15+
16+
17+
#EXPECT: feat_req__random_id1: inconsistent validity: valid_from (v1.0) >= valid_until (v0.5).
18+
19+
.. feat_req:: from after until
20+
:id: feat_req__random_id1
21+
:valid_from: v1.0
22+
:valid_until: v0.5
23+
24+
25+
#EXPECT-NOT: feat_req__random_id2: inconsistent validity: valid_from (v0.5) >= valid_until (v1.0).
26+
27+
.. feat_req:: until after from
28+
:id: feat_req__random_id2
29+
:valid_from: v0.5
30+
:valid_until: v1.0
31+
32+
33+
#EXPECT: stkh_req__random_id1: inconsistent validity: valid_from (v1.0.1) >= valid_until (v0.5).
34+
35+
.. stkh_req:: from after until for stakeholder requirement
36+
:id: stkh_req__random_id1
37+
:valid_from: v1.0.1
38+
:valid_until: v0.5

src/extensions/score_metamodel/tests/rst/options/test_options_options.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,3 +504,17 @@
504504

505505
.. std_wp:: This is a test
506506
:id: std_wp__test_content
507+
508+
509+
#EXPECT: feat_req__random_id3.valid_from (2035-03): does not follow pattern
510+
511+
.. feat_req:: milestone must be a version
512+
:id: feat_req__random_id3
513+
:valid_from: 2035-03
514+
515+
516+
#EXPECT: feat_req__random_id4.valid_until (2035-03): does not follow pattern
517+
518+
.. feat_req:: milestone must be a version
519+
:id: feat_req__random_id4
520+
:valid_until: 2035-03

src/extensions/score_metamodel/tests/test_check_options.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from score_metamodel.checks.check_options import (
2121
check_extra_options,
2222
check_options,
23+
parse_milestone,
2324
)
2425
from score_metamodel.tests import fake_check_logger, need
2526
from sphinx.application import Sphinx # type: ignore[import-untyped]
@@ -118,3 +119,9 @@ def test_unknown_option_present_in_neither_req_opt_neither_opt_opt(self):
118119
"has these extra options: `other_option`.",
119120
expect_location=False,
120121
)
122+
123+
124+
def test_milestone_parsing():
125+
assert parse_milestone("v0.5") == (0, 5, 0)
126+
assert parse_milestone("v1.0") == (1, 0, 0)
127+
assert parse_milestone("v1.0.1") == (1, 0, 1)

0 commit comments

Comments
 (0)