From 17596907f58fbc9b73f8348a198a223dff63aabb Mon Sep 17 00:00:00 2001 From: Jonas Bengtsson Date: Sat, 12 Sep 2026 11:13:07 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=A7=20Add=20file=20time=20information?= =?UTF-8?q?=20in=20commit=20message?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../commits/message_builder.py | 25 +++++++++++++------ tests/integration/test_run_cycle.py | 16 ++++++++++++ tests/unit/test_lock_config_message.py | 9 +++++++ 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/git_auto_tracker/commits/message_builder.py b/src/git_auto_tracker/commits/message_builder.py index 87f1a2a..5fdc85f 100644 --- a/src/git_auto_tracker/commits/message_builder.py +++ b/src/git_auto_tracker/commits/message_builder.py @@ -19,21 +19,30 @@ _SUMMARY_LABELS = ( def _format_ts(moment: dt.datetime | None) -> str: + """Render an aware datetime in local time with an explicit offset. + + Local wall-clock time plus the UTC offset, e.g. 2026-09-12 08:00:00+02:00 + (matching the log timestamp style in logging/setup.py, which also uses + local time rather than UTC). + """ if moment is None: return "" - return moment.strftime("%Y-%m-%d %H:%M:%S%z") + return moment.astimezone().isoformat(sep=" ", timespec="seconds") def _author_lines(record) -> list[str]: # noqa: ANN001 - ChangeRecord, avoid cycle lines: list[str] = [] author = record.author - if author is not None: - lines.append(f" Last modified by: {author.identifier}") - ts = _format_ts(author.timestamp) - if ts: - lines.append(f" Modified at: {ts}") - else: - lines.append(" Last modified by: UNKNOWN") + identifier = author.identifier if author is not None else "UNKNOWN" + lines.append(f" Last modified by: {identifier}") + # Filesystem last-modified time (Section 12: file_timestamp); rendered + # in local time like the log timestamps. Resolver-provided timestamps + # (author.timestamp) stay authoritative and are shown as well. + file_ts = _format_ts(record.file_timestamp) + if file_ts: + lines.append(f" Modified at: {file_ts}") + if author is not None and author.timestamp is not None: + lines.append(f" Changed at: {_format_ts(author.timestamp)}") return lines diff --git a/tests/integration/test_run_cycle.py b/tests/integration/test_run_cycle.py index fbe408b..a6fea20 100644 --- a/tests/integration/test_run_cycle.py +++ b/tests/integration/test_run_cycle.py @@ -102,6 +102,22 @@ class TestRunCycle: body = _git_out("log", "-1", "--format=%b", cwd=git_repo) assert "Last modified by: DOMAIN\\jsmith" in body + def test_file_mtime_appears_in_commit_local_time( + self, git_repo: Path, tracker_config: dict, tmp_path: Path + ) -> None: + """Section 12 file_timestamp rendered as 'Modified at' (local time).""" + import re as re_mod + + (git_repo / "config.xml").write_text("", encoding="utf-8") + _run(tracker_config, tmp_path) + body = _git_out("log", "-1", "--format=%b", cwd=git_repo) + # Local-time format with explicit +/-HH:MM offset (Section 15) + assert re_mod.search( + r"Modified at: \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}\b", body + ) + # The timestamp block sits directly under the changed file entry + assert " config.xml\n Last modified by: UNKNOWN\n Modified at:" in body + def test_excluded_files_not_committed( self, git_repo: Path, tracker_config: dict, tmp_path: Path ) -> None: diff --git a/tests/unit/test_lock_config_message.py b/tests/unit/test_lock_config_message.py index ebc34b0..de36a58 100644 --- a/tests/unit/test_lock_config_message.py +++ b/tests/unit/test_lock_config_message.py @@ -3,6 +3,7 @@ from __future__ import annotations import datetime as dt +import re from pathlib import Path import pytest @@ -152,6 +153,7 @@ class TestCommitMessage: ChangeRecord( path="Stations/PS101/project.xml", change_type=ChangeType.MODIFIED, + file_timestamp=dt.datetime(2026, 9, 11, 6, 42, 9, tzinfo=dt.UTC), author=AuthorIdentity( identifier="DOMAIN\\jsmith", source="scada_audit_log", @@ -175,6 +177,13 @@ class TestCommitMessage: assert "Added: 1" in message assert "Last modified by: DOMAIN\\jsmith" in message assert "Last modified by: UNKNOWN" in message + # Filesystem mtime rendered as 'Modified at' in local time with offset + assert " Modified at: " in message + assert re.search( + r"Modified at: \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}\b", message + ) + # Resolver-provided timestamp kept as a separate 'Changed at' line + assert " Changed at: " in message assert "Stations/PS027/old.xml" in message # Trailers last (Section 36) assert "Tracker-Version: 1.0.0" in message