Skip to content

fix(cmd-version): force tag timestamp to be same time as release commit #1117

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/semantic_release/cli/commands/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)

Expand Down
70 changes: 50 additions & 20 deletions src/semantic_release/gitproject.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -48,23 +49,36 @@ 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
is not reentrant, so once we have "used" it
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:
Expand Down Expand Up @@ -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(
Expand All @@ -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:
Expand Down
Loading