From 9a38d9dc604ded308fa45cf6ee7d26e3abd738c3 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 8 Dec 2024 15:39:45 -0700 Subject: [PATCH] fix(cmd-version): force tag timestamp to be same time as release commit --- src/semantic_release/cli/commands/version.py | 1 + src/semantic_release/gitproject.py | 70 ++++++++++++++------ 2 files changed, 51 insertions(+), 20 deletions(-) diff --git a/src/semantic_release/cli/commands/version.py b/src/semantic_release/cli/commands/version.py index 8c15b7faa..737d13d8d 100644 --- a/src/semantic_release/cli/commands/version.py +++ b/src/semantic_release/cli/commands/version.py @@ -653,6 +653,7 @@ def version( # noqa: C901 project.git_tag( tag_name=new_version.as_tag(), message=new_version.as_tag(), + isotimestamp=commit_date.isoformat(), noop=opts.noop, ) diff --git a/src/semantic_release/gitproject.py b/src/semantic_release/gitproject.py index 25fe19505..ef174d85c 100644 --- a/src/semantic_release/gitproject.py +++ b/src/semantic_release/gitproject.py @@ -3,6 +3,7 @@ from __future__ import annotations from contextlib import nullcontext +from datetime import datetime from logging import getLogger from pathlib import Path from typing import TYPE_CHECKING @@ -48,7 +49,9 @@ def logger(self) -> Logger: return self._logger def _get_custom_environment( - self, repo: Repo + self, + repo: Repo, + custom_vars: dict[str, str] | None = None, ) -> nullcontext[None] | _GeneratorContextManager[None]: """ git.custom_environment is a context manager but @@ -56,15 +59,26 @@ def _get_custom_environment( we need to throw it away and re-create it in order to use it again """ + author_vars = ( + { + "GIT_AUTHOR_NAME": self._commit_author.name, + "GIT_AUTHOR_EMAIL": self._commit_author.email, + "GIT_COMMITTER_NAME": self._commit_author.name, + "GIT_COMMITTER_EMAIL": self._commit_author.email, + } + if self._commit_author + else {} + ) + + custom_env_vars = { + **author_vars, + **(custom_vars or {}), + } + return ( nullcontext() - if not self._commit_author - else repo.git.custom_environment( - GIT_AUTHOR_NAME=self._commit_author.name, - GIT_AUTHOR_EMAIL=self._commit_author.email, - GIT_COMMITTER_NAME=self._commit_author.name, - GIT_COMMITTER_EMAIL=self._commit_author.email, - ) + if not custom_env_vars + else repo.git.custom_environment(**custom_env_vars) ) def is_dirty(self) -> bool: @@ -182,19 +196,32 @@ def git_commit( self.logger.exception(str(err)) raise GitCommitError("Failed to commit changes") from err - def git_tag(self, tag_name: str, message: str, noop: bool = False) -> None: + def git_tag( + self, tag_name: str, message: str, isotimestamp: str, noop: bool = False + ) -> None: + try: + datetime.fromisoformat(isotimestamp) + except ValueError as err: + raise ValueError("Invalid timestamp format") from err + if noop: - command = ( - f"""\ - GIT_AUTHOR_NAME={self._commit_author.name} \\ - GIT_AUTHOR_EMAIL={self._commit_author.email} \\ - GIT_COMMITTER_NAME={self._commit_author.name} \\ - GIT_COMMITTER_EMAIL={self._commit_author.email} \\ - """ - if self._commit_author - else "" + command = str.join( + " ", + [ + f"GIT_COMMITTER_DATE={isotimestamp}", + *( + [ + f"GIT_AUTHOR_NAME={self._commit_author.name}", + f"GIT_AUTHOR_EMAIL={self._commit_author.email}", + f"GIT_COMMITTER_NAME={self._commit_author.name}", + f"GIT_COMMITTER_EMAIL={self._commit_author.email}", + ] + if self._commit_author + else [""] + ), + f"git tag -a {tag_name} -m '{message}'", + ], ) - command += f"git tag -a {tag_name} -m '{message}'" noop_report( indented( @@ -206,7 +233,10 @@ def git_tag(self, tag_name: str, message: str, noop: bool = False) -> None: ) return - with Repo(str(self.project_root)) as repo, self._get_custom_environment(repo): + with Repo(str(self.project_root)) as repo, self._get_custom_environment( + repo, + {"GIT_COMMITTER_DATE": isotimestamp}, + ): try: repo.git.tag("-a", tag_name, m=message) except GitCommandError as err: