Skip to content

Commit 57fedac

Browse files
authored
Merge pull request #2525 from strictdoc-project/stanislaw/diff_cli
feat(diff/changelog): cli: add generation of diff/changelog from a pair of Git revisions
2 parents 9de36f3 + 28cb2a7 commit 57fedac

File tree

74 files changed

+640
-466
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+640
-466
lines changed

docs/strictdoc_01_user_guide.sdoc

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3947,11 +3947,23 @@ To activate the Diff/Changelog screen, add/edit the strictdoc.toml config file i
39473947
"DIFF"
39483948
]
39493949

3950+
With the feature enabled, the editable web server interface displays the corresponding Diff/Changelog screen, where users can select Git revisions to compare for change tracking.
3951+
3952+
To use the feature with a command-line interface:
3953+
3954+
.. code::
3955+
3956+
strictdoc export . --generate-diff-git="HEAD^..HEAD"
3957+
39503958
.. admonition:: Robust MID-based change tracking
39513959
:class: note
39523960

3953-
For optimal results when using the Diff/Changelog feature in a StrictDoc-based project, it is strongly recommended to enable Machine Identifiers (MIDs) for all project artifacts, such as TEXT, REQUIREMENT, etc.
3954-
Without MIDs, StrictDoc cannot ensure accurate change tracking. If a node lacks an MID, StrictDoc is unable to reliably detect whether it has been modified or relocated in subsequent versions of the documentation tree. For further details, refer to [LINK: SECTION-UG-Machine-identifiers-MID].
3961+
For optimal results when using the Diff/Changelog feature in a StrictDoc-based project, it is strongly recommended to enable Machine Identifiers (MIDs) for all project artifacts, such as TEXT, REQUIREMENT, and others. Without MIDs, StrictDoc relies on change tracking based on node UIDs, which is less reliable due to at least the following two issues:
3962+
3963+
1. The need to maintain UIDs for sections and text nodes, not only for requirements.
3964+
2. Possible changes to node UIDs. If a node UID changes, for example due to a change in project conventions, StrictDoc cannot reliably determine whether the node has been modified or relocated in later versions of the documentation tree.
3965+
3966+
For more details, see [LINK: SECTION-UG-Machine-identifiers-MID].
39553967
<<<
39563968

39573969
[[/SECTION]]

docs/strictdoc_04_release_notes.sdoc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ TITLE: Unreleased
5252
[TEXT]
5353
MID: 12345f3b28ea4bfe9ca8afe6b21826a8
5454
STATEMENT: >>>
55-
This release includes the removal of legacy @sdoc markers, several bug fixes, and enhancements for edge cases.
55+
This release includes an enhancement to the Diff/Changelog feature, the removal of legacy @sdoc markers, several bug fixes, and improvements for edge cases.
56+
57+
The Diff/Changelog feature has been extended to support the new --generate-diff-git option which allows generating the Diff/Changelog screens for a given pair of Git revisions.
5658

5759
The legacy ``@sdoc`` marker has been removed from the codebase. From now on, only the ``@relation`` markers are recognized when linking requirements with source code files.
5860

strictdoc/cli/cli_arg_parser.py

Lines changed: 24 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import argparse
22
import os
3-
from typing import Any, List, Optional
3+
from typing import Any, List, Optional, Tuple
44

55
from strictdoc.cli.command_parser_builder import (
66
CommandParserBuilder,
@@ -127,6 +127,7 @@ def validate(self) -> None:
127127
class ExportCommandConfig:
128128
def __init__(
129129
self,
130+
*,
130131
input_paths: List[str],
131132
output_dir: Optional[str],
132133
config_path: Optional[str],
@@ -142,6 +143,8 @@ def __init__(
142143
reqif_multiline_is_xhtml: bool,
143144
reqif_enable_mid: bool,
144145
view: Optional[str],
146+
generate_diff_git: Optional[str],
147+
generate_diff_dirs: Optional[Tuple[str, str]],
145148
chromedriver: Optional[str],
146149
):
147150
assert isinstance(input_paths, list), f"{input_paths}"
@@ -160,6 +163,8 @@ def __init__(
160163
self.reqif_multiline_is_xhtml: bool = reqif_multiline_is_xhtml
161164
self.reqif_enable_mid: bool = reqif_enable_mid
162165
self.view: Optional[str] = view
166+
self.generate_diff_git: Optional[str] = generate_diff_git
167+
self.generate_diff_dirs: Optional[Tuple[str, str]] = generate_diff_dirs
163168
self.chromedriver: Optional[str] = chromedriver
164169

165170
def get_path_to_config(self) -> str:
@@ -197,18 +202,6 @@ def __init__(self, output_file: str):
197202
self.output_file: str = output_file
198203

199204

200-
class DiffCommandConfig:
201-
def __init__(
202-
self,
203-
path_to_lhs_tree: str,
204-
path_to_rhs_tree: str,
205-
output_dir: Optional[str],
206-
):
207-
self.path_to_lhs_tree: str = path_to_lhs_tree
208-
self.path_to_rhs_tree: str = path_to_rhs_tree
209-
self.output_dir: Optional[str] = output_dir
210-
211-
212205
class SDocArgsParser:
213206
def __init__(self, args: argparse.Namespace):
214207
self.args: argparse.Namespace = args
@@ -250,10 +243,6 @@ def is_dump_grammar_command(self) -> bool:
250243
def is_version_command(self) -> bool:
251244
return str(self.args.command) == "version"
252245

253-
@property
254-
def is_diff_command(self) -> bool:
255-
return str(self.args.command) == "diff"
256-
257246
@property
258247
def is_manage_autouid_command(self) -> bool:
259248
return (
@@ -265,22 +254,24 @@ def get_export_config(self) -> ExportCommandConfig:
265254
project_title: Optional[str] = self.args.project_title
266255

267256
return ExportCommandConfig(
268-
self.args.input_paths,
269-
self.args.output_dir,
270-
self.args.config,
271-
project_title,
272-
self.args.formats,
273-
self.args.fields,
274-
self.args.generate_bundle_document,
275-
self.args.no_parallelization,
276-
self.args.enable_mathjax,
277-
self.args.included_documents,
278-
self.args.filter_nodes,
279-
self.args.reqif_profile,
280-
self.args.reqif_multiline_is_xhtml,
281-
self.args.reqif_enable_mid,
282-
self.args.view,
283-
self.args.chromedriver,
257+
input_paths=self.args.input_paths,
258+
output_dir=self.args.output_dir,
259+
config_path=self.args.config,
260+
project_title=project_title,
261+
formats=self.args.formats,
262+
fields=self.args.fields,
263+
generate_bundle_document=self.args.generate_bundle_document,
264+
no_parallelization=self.args.no_parallelization,
265+
enable_mathjax=self.args.enable_mathjax,
266+
included_documents=self.args.included_documents,
267+
filter_nodes=self.args.filter_nodes,
268+
reqif_profile=self.args.reqif_profile,
269+
reqif_multiline_is_xhtml=self.args.reqif_multiline_is_xhtml,
270+
reqif_enable_mid=self.args.reqif_enable_mid,
271+
view=self.args.view,
272+
generate_diff_git=self.args.generate_diff_git,
273+
generate_diff_dirs=self.args.generate_diff_dirs,
274+
chromedriver=self.args.chromedriver,
284275
)
285276

286277
def get_import_config_reqif(self, _: Any) -> ImportReqIFCommandConfig:
@@ -317,13 +308,6 @@ def get_server_config(self) -> ServerCommandConfig:
317308
def get_dump_grammar_config(self) -> DumpGrammarCommandConfig:
318309
return DumpGrammarCommandConfig(output_file=self.args.output_file)
319310

320-
def get_diff_config(self) -> DiffCommandConfig:
321-
return DiffCommandConfig(
322-
path_to_lhs_tree=self.args.path_to_lhs_tree,
323-
path_to_rhs_tree=self.args.path_to_rhs_tree,
324-
output_dir=self.args.output_dir,
325-
)
326-
327311

328312
def create_sdoc_args_parser(
329313
testing_args: Optional[argparse.Namespace] = None,

strictdoc/cli/command_parser_builder.py

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,17 @@ def _check_reqif_import_markup(markup: Optional[str]) -> str:
5858
return markup
5959

6060

61+
def _check_git_revisions(git_revisions: str) -> str:
62+
if ".." not in git_revisions:
63+
message = (
64+
"Invalid Git revision pair. "
65+
'The expected format is: "<Git revision>..<Git revision>". '
66+
'Example: "HEAD^..HEAD".'
67+
)
68+
raise argparse.ArgumentTypeError(message)
69+
return git_revisions
70+
71+
6172
def _parse_fields(fields: str) -> List[str]:
6273
fields_array = fields.split(",")
6374
return fields_array
@@ -125,7 +136,6 @@ def build(self) -> SDocArgumentParser:
125136
self.add_import_command(command_subparsers)
126137
self.add_version_command(command_subparsers)
127138
self.add_dump_command(command_subparsers)
128-
self.add_diff_command(command_subparsers)
129139

130140
return main_parser
131141

@@ -250,6 +260,24 @@ def add_export_command(
250260
type=str,
251261
help="Choose which view will be exported.",
252262
)
263+
command_parser_export.add_argument(
264+
"--generate-diff-git",
265+
type=_check_git_revisions,
266+
help=(
267+
"Generate Diff/Changelog for a given pair of Git revisions. "
268+
'Example: --generate-diff-git "HEAD^..HEAD"'
269+
),
270+
)
271+
command_parser_export.add_argument(
272+
"--generate-diff-dirs",
273+
"--generate-diff-dirs",
274+
metavar=("OLD_PATH", "NEW_PATH"),
275+
nargs=2,
276+
help=(
277+
"Generate Diff/Changelog for a given pair of local directories. "
278+
'Example: --generate-diff-dirs "./old_path" "./new_path"'
279+
),
280+
)
253281
command_parser_export.add_argument(
254282
"--chromedriver",
255283
type=str,
@@ -458,28 +486,3 @@ def add_manage_command(
458486
),
459487
)
460488
add_config_argument(command_parser_auto_uid)
461-
462-
@staticmethod
463-
def add_diff_command(
464-
command_subparsers: "argparse._SubParsersAction[SDocArgumentParser]",
465-
) -> None:
466-
diff_command_parser = command_subparsers.add_parser(
467-
"diff",
468-
help="Generate Diff between two SDoc project trees.",
469-
formatter_class=formatter,
470-
)
471-
diff_command_parser.add_argument(
472-
"path_to_lhs_tree",
473-
type=str,
474-
help="Path to the left-hand side project tree.",
475-
)
476-
diff_command_parser.add_argument(
477-
"path_to_rhs_tree",
478-
type=str,
479-
help="Path to the right-hand side project tree.",
480-
)
481-
diff_command_parser.add_argument(
482-
"--output-dir",
483-
type=str,
484-
help="A directory where to output the files to.",
485-
)

strictdoc/cli/main.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
from strictdoc import environment
1818
from strictdoc.cli.cli_arg_parser import (
1919
CLIValidationError,
20-
DiffCommandConfig,
2120
DumpGrammarCommandConfig,
2221
ExportCommandConfig,
2322
ImportExcelCommandConfig,
@@ -27,7 +26,6 @@
2726
create_sdoc_args_parser,
2827
)
2928
from strictdoc.commands.about_command import AboutCommand
30-
from strictdoc.commands.diff_command import DiffCommand
3129
from strictdoc.commands.dump_grammar_command import DumpGrammarCommand
3230
from strictdoc.commands.manage_autouid_command import ManageAutoUIDCommand
3331
from strictdoc.commands.version_command import VersionCommand
@@ -139,15 +137,6 @@ def _main(parallelizer: Parallelizer, parser: SDocArgsParser) -> None:
139137
elif parser.is_about_command:
140138
AboutCommand.execute()
141139

142-
elif parser.is_diff_command:
143-
diff_config: DiffCommandConfig = parser.get_diff_config()
144-
project_config = ProjectConfigLoader.load_from_path_or_get_default(
145-
path_to_config=os.getcwd(),
146-
)
147-
DiffCommand.execute(
148-
project_config=project_config, diff_config=diff_config
149-
)
150-
151140
else:
152141
raise NotImplementedError
153142

0 commit comments

Comments
 (0)