From ff6dfaa8f7d1eeaa6ea674664e42d2392bd321bd Mon Sep 17 00:00:00 2001
From: Martin Lehmann
Date: Wed, 31 Jul 2024 14:47:42 +0200
Subject: [PATCH 1/4] feat: Add a Gitlab template for comparing against the
latest tag
---
ci-templates/gitlab/compare-to-tag.yml | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
create mode 100644 ci-templates/gitlab/compare-to-tag.yml
diff --git a/ci-templates/gitlab/compare-to-tag.yml b/ci-templates/gitlab/compare-to-tag.yml
new file mode 100644
index 0000000..f5e0367
--- /dev/null
+++ b/ci-templates/gitlab/compare-to-tag.yml
@@ -0,0 +1,24 @@
+# Copyright DB InfraGO AG and contributors
+# SPDX-License-Identifier: Apache-2.0
+# yaml-language-server: $schema=https://gitlab.com/gitlab-org/gitlab/-/raw/master/app/assets/javascripts/editor/schema/ci.json
+
+compare-to-tag:
+ image: python:3.12
+ script:
+ - oldversion="$(git describe --tags --abbrev=0 || true)"
+ - if ! [[ $oldversion ]]; then echo >&2 No recent tag found to compare against; exit 0; fi
+ - |-
+ # Install capella-diff-tools from PyPI or Github
+ if [[ ${CAPELLA_DIFF_TOOLS_VERSION:-vX.Y.Z} = v*.*.* ]]; then
+ pip install "capella-diff-tools${CAPELLA_DIFF_TOOLS_VERSION:+==$CAPELLA_DIFF_TOOLS_VERSION}"
+ else
+ pip install "git+https://github.com/DSD-DBS/capella-diff-tools.git@$CAPELLA_DIFF_TOOLS_VERSION"
+ fi
+ - capella-diff-tool ${ENTRYPOINT:-.} "$oldversion" HEAD -o model-diff.yml -r model-diff.html
+
+ artifacts:
+ paths: [model-diff.html, model-diff.yml]
+
+variables:
+ CAPELLA_DIFF_TOOLS_VERSION:
+ description: Version of capella-diff-tools to install. Should be the same as the version of this template.
From d9b450f91fd402bf07272ccaccebc9bad19d621d Mon Sep 17 00:00:00 2001
From: Martin Lehmann
Date: Wed, 31 Jul 2024 15:17:51 +0200
Subject: [PATCH 2/4] fix: Don't try to treat files as git repos
---
capella_diff_tools/__main__.py | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/capella_diff_tools/__main__.py b/capella_diff_tools/__main__.py
index c7b33c7..5bb5161 100644
--- a/capella_diff_tools/__main__.py
+++ b/capella_diff_tools/__main__.py
@@ -6,7 +6,6 @@
import datetime
import logging
-import os
import pathlib
import sys
import typing as t
@@ -65,7 +64,7 @@ def main(
"""
logging.basicConfig(level="DEBUG")
model.pop("revision", None)
- model["path"] = _ensure_git(model["path"])
+ _ensure_git(model)
old_model = capellambse.MelodyModel(**model, revision=old_version)
new_model = capellambse.MelodyModel(**model, revision=new_version)
@@ -92,18 +91,19 @@ def main(
report_file.write(report.generate_html(result))
-def _ensure_git(path: str | os.PathLike[str]) -> str:
- proto, path = fh.split_protocol(path)
+def _ensure_git(model: dict[str, t.Any]) -> None:
+ proto, path = fh.split_protocol(model["path"])
if proto == "file":
assert isinstance(path, pathlib.Path)
- path = "git+" + path.resolve().as_uri()
-
- proto, _ = fh.split_protocol(path)
- if proto != "git":
+ path = path.resolve()
+ if "entrypoint" not in model and path.is_file():
+ model["entrypoint"] = path.name
+ path = path.parent
+ model["path"] = "git+" + path.as_uri()
+ elif proto != "git":
raise click.Abort("The 'model' must point to a git repository")
- assert isinstance(path, str)
- return path
+ assert isinstance(model["path"], str)
def _get_revision_info(
From f66f5a6a453846b0ddc4f11073a6792456e81021 Mon Sep 17 00:00:00 2001
From: Martin Lehmann
Date: Wed, 31 Jul 2024 17:33:31 +0200
Subject: [PATCH 3/4] feat: Add commit log to metadata and the report
---
capella_diff_tools/__main__.py | 31 +++++++++++++++++
capella_diff_tools/report.html.jinja | 50 +++++++++++++++++++++++++---
capella_diff_tools/types.py | 1 +
3 files changed, 78 insertions(+), 4 deletions(-)
diff --git a/capella_diff_tools/__main__.py b/capella_diff_tools/__main__.py
index 5bb5161..03eb7ce 100644
--- a/capella_diff_tools/__main__.py
+++ b/capella_diff_tools/__main__.py
@@ -72,6 +72,7 @@ def main(
"model": model,
"old_revision": _get_revision_info(old_model, old_version),
"new_revision": _get_revision_info(new_model, new_version),
+ "commit_log": _get_commit_log(new_model, old_version, new_version),
}
objects = compare.compare_all_objects(old_model, new_model)
diagrams = compare.compare_all_diagrams(old_model, new_model)
@@ -132,6 +133,36 @@ def _get_revision_info(
}
+def _get_commit_log(
+ model: capellambse.MelodyModel,
+ old_version: str,
+ new_version: str,
+) -> list[types.RevisionInfo]:
+ res = model._loader.resources["\x00"]
+ assert isinstance(res, fh.git.GitFileHandler)
+ commits: list[types.RevisionInfo] = []
+ rawlog = res._git(
+ "log",
+ "-z",
+ "--reverse",
+ "--format=format:%H%x00%aN%x00%aI%x00%B",
+ f"{old_version}..{new_version}",
+ encoding="utf-8",
+ ).split("\x00")
+ log = capellambse.helpers.ntuples(4, rawlog)
+ for hash, author, date_str, description in log:
+ commits.append(
+ {
+ "hash": hash,
+ "revision": hash,
+ "author": author,
+ "date": datetime.datetime.fromisoformat(date_str),
+ "description": description.rstrip(),
+ }
+ )
+ return commits
+
+
class CustomYAMLDumper(yaml.SafeDumper):
"""A custom YAML dumper that can serialize markupsafe.Markup."""
diff --git a/capella_diff_tools/report.html.jinja b/capella_diff_tools/report.html.jinja
index e621356..90a8ca1 100644
--- a/capella_diff_tools/report.html.jinja
+++ b/capella_diff_tools/report.html.jinja
@@ -58,7 +58,7 @@
Repository: {{ data["metadata"]["model"]["path"] | e }}
Entry point: {{ data["metadata"]["model"]["entrypoint"] | e }}
- The review of changes covers the following commits:
+ The review of changes covers the following range of commits:
@@ -84,9 +84,9 @@
| {{ data["metadata"]["new_revision"]["date"] | e }} |
- | Commit message |
- {{ data["metadata"]["old_revision"]["description"] | e }} |
- {{ data["metadata"]["new_revision"]["description"] | e }} |
+ Summary |
+ {{ data["metadata"]["old_revision"]["description"].split("\n") | first | e }} |
+ {{ data["metadata"]["new_revision"]["description"].split("\n") | first | e }} |
| Commit ID (hash) |
@@ -96,6 +96,48 @@
+ Detailed information about the contained commits:
+
+
+
+ {% for commit in data.metadata.commit_log -%}
+
+ | {{ commit.hash | e }} |
+ {{ commit.date | e }} |
+ {{ commit.author | e }} |
+ {{ commit.description | e }} |
+
+ {% else -%}
+
+ | {{ data.metadata.old_revision.hash }} |
+ {{ data.metadata.old_revision.date }} |
+ {{ data.metadata.old_revision.author }} |
+ {{ data.metadata.old_revision.description }} |
+
+
+ |
+ Error: The supplied report does not contain the commit log.
+ Detailed information is only available for the first and last commit.
+ |
+
+
+ | {{ data.metadata.new_revision.hash }} |
+ {{ data.metadata.new_revision.date }} |
+ {{ data.metadata.new_revision.author }} |
+ {{ data.metadata.new_revision.description }} |
+
+ {% endfor -%}
+
+
+
+ | Commit ID (hash) |
+ Date & time |
+ Author |
+ Commit message |
+
+
+
+
{% macro pretty_stats(stats) %}
(
{% if stats.created %}+{{stats["created"]}} / {% endif %}
diff --git a/capella_diff_tools/types.py b/capella_diff_tools/types.py
index c3a51f3..672d73c 100644
--- a/capella_diff_tools/types.py
+++ b/capella_diff_tools/types.py
@@ -21,6 +21,7 @@ class Metadata(te.TypedDict):
"""The 'modelinfo' used to load the models, sans the revision key."""
new_revision: RevisionInfo
old_revision: RevisionInfo
+ commit_log: list[RevisionInfo]
class RevisionInfo(te.TypedDict, total=False):
From 1028d581a278fb6c9882de2bfd3e08e19baa36f6 Mon Sep 17 00:00:00 2001
From: Martin Lehmann
Date: Tue, 24 Sep 2024 15:22:46 +0200
Subject: [PATCH 4/4] fix: Actually show the error when the model isn't a git
repo
---
capella_diff_tools/__main__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/capella_diff_tools/__main__.py b/capella_diff_tools/__main__.py
index 03eb7ce..7be3f35 100644
--- a/capella_diff_tools/__main__.py
+++ b/capella_diff_tools/__main__.py
@@ -102,7 +102,7 @@ def _ensure_git(model: dict[str, t.Any]) -> None:
path = path.parent
model["path"] = "git+" + path.as_uri()
elif proto != "git":
- raise click.Abort("The 'model' must point to a git repository")
+ raise click.UsageError("The 'model' must point to a git repository")
assert isinstance(model["path"], str)