Skip to content

Commit

Permalink
Honor GIT_AUTHOR_DATE from env
Browse files Browse the repository at this point in the history
When invoked from a git hook we can use the environment to detect
the date for a commit instead of just using the current system date.

This makes handling more robust when amending a commit
  • Loading branch information
emzeat committed Jan 9, 2024
1 parent 5bee94c commit 7106440
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 2 deletions.
2 changes: 1 addition & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ indent-string=' '
max-line-length=120

# Maximum number of lines in a module.
max-module-lines=1000
max-module-lines=2000

# Allow the body of a class to be on the same line as the declaration if body
# contains single statement.
Expand Down
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ _WIP_
* Make detection of comment style based on suffix case insensitive
* Fix handling of files with unicode BOM
* Allow selection of latest year vs range of years
* Fix handling of author years when amending a commit

v2.6.2
------
Expand Down
46 changes: 45 additions & 1 deletion license_tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,46 @@ def current_year():
DateUtils._current_year = int(year)
return DateUtils._current_year

@staticmethod
def parse_git_date(git_date: str) -> datetime.datetime:
"""
Returns a datetime parsed from a git date string
as documented at https://git-scm.com/docs/git-commit/2.24.0#_date_formats
"""
# Git internal format
try:
if git_date.startswith('@'):
unix_timestamp, tz_offset = git_date[1:].split(' ', maxsplit=1)
unix_timestamp = int(unix_timestamp)
timezone = datetime.timezone(datetime.datetime.strptime(tz_offset, '%z').utcoffset())
return datetime.datetime.fromtimestamp(unix_timestamp, tz=timezone)
except: # pylint: disable=bare-except
pass

# RFC 2822
try:
from email.utils import parsedate_to_datetime # pylint: disable=import-outside-toplevel
author_date = parsedate_to_datetime(git_date)
if author_date:
return author_date
except: # pylint: disable=bare-except
pass

# ISO 8601
try:
return datetime.datetime.fromisoformat(git_date)
except: # pylint: disable=bare-except
pass

# plain formats
for format in ['%Y.%m.%d', '%m/%d/%Y', '%d.%m.%Y']:
try:
return datetime.datetime.strptime(git_date, format)
except: # pylint: disable=bare-except
pass

raise RuntimeError(f"Not a supported git date format: {git_date}")


class Style(enum.Enum):
"""Enumerates the different known comment styles"""
Expand Down Expand Up @@ -258,7 +298,11 @@ def __init__(self, name: str, year_from: int = None,
self.git_repo = git_repo
self.name_from_git = git_repo is not None
if year_to is None:
year_to = DateUtils.current_year()
git_author_date = os.environ.get('GIT_AUTHOR_DATE', None)
if self.name_from_git and git_author_date:
year_to = DateUtils.parse_git_date(git_author_date).year
else:
year_to = DateUtils.current_year()
self.year_to = year_to
if year_from:
self.year_from = year_from
Expand Down
18 changes: 18 additions & 0 deletions test.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
# limitations under the License.

from asyncio import subprocess
import datetime
import functools
import os
import pathlib
Expand Down Expand Up @@ -95,6 +96,23 @@ def test_recursive_includes(self):
raise


class TestDateUtils(unittest.TestCase):

def test_git_date_format(self):
EXPRESSIONS = [
('Thu, 07 Apr 2005 22:13:13 +0200', 2005, 4, 7), # RFC 2822
('2005-04-07T22:13:13', 2005, 4, 7), # ISO 8601
('1998.02.01', 1998, 2, 1),
('01/02/1987', 1987, 1, 2),
('@1453791344 +0100', 2016, 1, 26), # git internal format
]
for expression, year, month, day in EXPRESSIONS:
date = license_tools.DateUtils.parse_git_date(expression)
self.assertEqual(date.year, year)
self.assertEqual(date.month, month)
self.assertEqual(date.day, day)


def parser_test(file: pathlib.Path):
def file_wrapper(func):
@functools.wraps(func)
Expand Down

0 comments on commit 7106440

Please sign in to comment.