From c6e677f3b774e6d661a3f198eeebff0cda4c1c52 Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Sat, 13 Jun 2020 10:13:27 +0200 Subject: [PATCH 1/3] Add **kwargs to mkdocs events to support future versions --- mkdocs_git_revision_date_localized_plugin/plugin.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mkdocs_git_revision_date_localized_plugin/plugin.py b/mkdocs_git_revision_date_localized_plugin/plugin.py index f046e17..ca6b429 100644 --- a/mkdocs_git_revision_date_localized_plugin/plugin.py +++ b/mkdocs_git_revision_date_localized_plugin/plugin.py @@ -18,7 +18,7 @@ class GitRevisionDateLocalizedPlugin(BasePlugin): ("type", config_options.Type(str, default="date")), ) - def on_config(self, config: config_options.Config) -> dict: + def on_config(self, config: config_options.Config, **kwargs) -> dict: """ Determine which locale to use. @@ -124,7 +124,7 @@ def on_post_page(self, output_content: str, **kwargs) -> str: return output_content[:idx] + extra_js + output_content[idx:] def on_page_markdown( - self, markdown: str, page: Page, config: config_options.Config, files + self, markdown: str, page: Page, config: config_options.Config, files, **kwargs ) -> str: """ Replace jinja2 tags in markdown and templates with the localized dates @@ -159,3 +159,4 @@ def on_page_markdown( markdown, flags=re.IGNORECASE, ) + From dfb859538a31fdcb8d62fc28a86372bccf1522ce Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Tue, 30 Jun 2020 09:13:53 +0200 Subject: [PATCH 2/3] Reformat test page with tag --- tests/fixtures/basic_project/docs/page_with_tag.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/fixtures/basic_project/docs/page_with_tag.md b/tests/fixtures/basic_project/docs/page_with_tag.md index d9d0172..f5638f9 100644 --- a/tests/fixtures/basic_project/docs/page_with_tag.md +++ b/tests/fixtures/basic_project/docs/page_with_tag.md @@ -1,6 +1,3 @@ # test page -this page has a git authors tag. - -Markdown tag: {{ git_revision_date_localized }} - +Tag \{\{ git_revision_date_localized \}\} renders as: {{ git_revision_date_localized }} From 53a949d9bcfde3423f6293431e0969a3ebf129b9 Mon Sep 17 00:00:00 2001 From: Tim Vink Date: Tue, 30 Jun 2020 15:37:58 +0200 Subject: [PATCH 3/3] Add support for timezones and improve docs --- README.md | 28 +++-- .../plugin.py | 5 +- .../util.py | 105 ++++++++++++------ setup.py | 4 +- .../basic_project/mkdocs_theme_language.yml | 10 ++ .../basic_project/mkdocs_theme_locale.yml | 2 +- .../basic_project/mkdocs_theme_timeago.yml | 10 ++ tests/test_builds.py | 69 ++++++++---- 8 files changed, 162 insertions(+), 71 deletions(-) create mode 100644 tests/fixtures/basic_project/mkdocs_theme_language.yml create mode 100644 tests/fixtures/basic_project/mkdocs_theme_timeago.yml diff --git a/README.md b/README.md index b173a81..47464e6 100644 --- a/README.md +++ b/README.md @@ -32,14 +32,17 @@ plugins: > If you have no `plugins` entry in your config file yet, you'll likely also want to add the `search` plugin. MkDocs enables it by default if there is no `plugins` entry set. -### Note when using on CI runners +### Note when using build environments -The plugin needs access to the last commit that touched a file to be able to retrieve the date. If you build your docs using CI then you might need to change your settings: +This plugin needs access to the last commit that touched a specific file to be able to retrieve the date. By default many build environments only retrieve the last commit, which means you might need to: +
+ Change your CI settings + + - github actions: set `fetch_depth` to `0` ([docs](https://github.com/actions/checkout)) + - gitlab runners: set `GIT_DEPTH` to `1000` ([docs](https://docs.gitlab.com/ee/user/project/pipelines/settings.html#git-shallow-clone)) + - bitbucket pipelines: set `clone: depth: full` ([docs](https://support.atlassian.com/bitbucket-cloud/docs/configure-bitbucket-pipelinesyml/)) +
-- github actions: set `fetch_depth` to `0` ([docs](https://github.com/actions/checkout)) -- gitlab runners: set `GIT_DEPTH` to `1000` ([docs](https://docs.gitlab.com/ee/user/project/pipelines/settings.html#git-shallow-clone)) - ----- ## Usage @@ -72,9 +75,10 @@ You can customize the plugin by setting options in `mkdocs.yml`. For example: ```yml plugins: - git-revision-date-localized: - type: timeago - locale: en - fallback_to_build_date: false + type: timeago + time_zone: Europe/Amsterdam + locale: en + fallback_to_build_date: false ``` ### `type` @@ -89,6 +93,10 @@ Default is `date`. To change the date format, set the `type` parameter to one of 20 hours ago # type: timeago ``` +### `time_zone` + +Default is `UTC`. Specify a time zone database name ([reference](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)). This option is especially relevant when using `type: datetime` and `type: iso_datetime`. Note that when using [timeago](http://timeago.yarp.com/) (with `type: timeago`) any difference in time zones between server and client will be handled automatically. + ### `locale` Default is `None`. Specify a two letter [ISO639](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) language code to display dates in your preferred language. @@ -99,7 +107,7 @@ Default is `None`. Specify a two letter [ISO639](https://en.wikipedia.org/wiki/L ### `fallback_to_build_date` -Default is `false`. If set to `true` the plugin will use the time when running `mkdocs build` instead of the git revision date. This means the revision date will be inaccurate, but this can be useful if your build environment has no access to GIT and you want to ignore the Git exceptions during `git log`. +Default is `false`. If set to `true` the plugin will use the time at `mkdocs build` instead of the file's last git revision date. This means the revision date is incorrect, but this can be acceptable if you want your project to also successfully build in environments with no access to GIT. ## Contributing diff --git a/mkdocs_git_revision_date_localized_plugin/plugin.py b/mkdocs_git_revision_date_localized_plugin/plugin.py index ca6b429..cc2c66d 100644 --- a/mkdocs_git_revision_date_localized_plugin/plugin.py +++ b/mkdocs_git_revision_date_localized_plugin/plugin.py @@ -16,6 +16,7 @@ class GitRevisionDateLocalizedPlugin(BasePlugin): ("fallback_to_build_date", config_options.Type(bool, default=False)), ("locale", config_options.Type(str, default=None)), ("type", config_options.Type(str, default="date")), + ("timezone", config_options.Type(str, default="UTC")), ) def on_config(self, config: config_options.Config, **kwargs) -> dict: @@ -33,7 +34,7 @@ def on_config(self, config: config_options.Config, **kwargs) -> dict: Returns: dict: global configuration object """ - self.util = Util(path=config["docs_dir"]) + self.util = Util(path=config["docs_dir"], config=self.config) # Get locale settings - might be added in future mkdocs versions # see: https://github.com/timvink/mkdocs-git-revision-date-localized-plugin/issues/24 @@ -149,6 +150,7 @@ def on_page_markdown( revision_dates = self.util.get_revision_date_for_file( path=page.file.abs_src_path, locale=self.config.get("locale", "en"), + time_zone=self.config.get("time_zone", "UTC"), fallback_to_build_date=self.config.get("fallback_to_build_date"), ) revision_date = revision_dates[self.config["type"]] @@ -159,4 +161,3 @@ def on_page_markdown( markdown, flags=re.IGNORECASE, ) - diff --git a/mkdocs_git_revision_date_localized_plugin/util.py b/mkdocs_git_revision_date_localized_plugin/util.py index 15b8ef6..2cb42df 100644 --- a/mkdocs_git_revision_date_localized_plugin/util.py +++ b/mkdocs_git_revision_date_localized_plugin/util.py @@ -5,16 +5,34 @@ from datetime import datetime # 3rd party -from babel.dates import format_date +from babel.dates import format_date, get_timezone from git import Repo, Git, GitCommandError, GitCommandNotFound class Util: - def __init__(self, path: str = "."): - git_repo = Repo(path, search_parent_directories=True) - self.repo = git_repo.git + def __init__(self, path: str = ".", config={}): - # Checks when running builds on CI + self.fallback_enabled = False + + try: + git_repo = Repo(path, search_parent_directories=True) + self.repo = git_repo.git + except: + if config.get("fallback_to_build_date"): + self.fallback_enabled = True + logging.warning( + "[git-revision-date-localized-plugin] Unable to find a git directory and/or git is not installed." + " Option 'fallback_to_build_date' set to 'true': Falling back to build date" + ) + return None + else: + logging.error( + "[git-revision-date-localized-plugin] Unable to find a git directory and/or git is not installed." + " To ignore this error, set option 'fallback_to_build_date: true'" + ) + raise + + # Checks if user is running builds on CI # See https://github.com/timvink/mkdocs-git-revision-date-localized-plugin/issues/10 if is_shallow_clone(self.repo): n_commits = commit_count(self.repo) @@ -40,39 +58,45 @@ def __init__(self, path: str = "."): """ ) + # TODO add bitbucket + @staticmethod - def _date_formats(unix_timestamp: float, locale="en") -> dict: + def _date_formats( + unix_timestamp: float, locale: str = "en", time_zone: str = "UTC" + ) -> dict: """ Returns different date formats / types. Args: - unix_timestamp (datetiment): a timestamp in seconds since 1970 + unix_timestamp (float): a timestamp in seconds since 1970 locale (str): Locale code of language to use. Defaults to 'en'. + time_zone (str): timezone database name (https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) Returns: dict: different date formats """ - - # Convert to millisecond timestamp - unix_timestamp = int(unix_timestamp) - timestamp_in_ms = unix_timestamp * 1000 - - revision_date = datetime.utcfromtimestamp(unix_timestamp) - logging.debug("Revision date: %s - Locale: %s" % (revision_date, locale)) + utc_revision_date = datetime.utcfromtimestamp(int(unix_timestamp)) + loc_revision_date = utc_revision_date.replace( + tzinfo=get_timezone("UTC") + ).astimezone(get_timezone(time_zone)) return { - "date": format_date(revision_date, format="long", locale=locale), - "datetime": format_date(revision_date, format="long", locale=locale) + "date": format_date(loc_revision_date, format="long", locale=locale), + "datetime": format_date(loc_revision_date, format="long", locale=locale) + " " - + revision_date.strftime("%H:%M:%S"), - "iso_date": revision_date.strftime("%Y-%m-%d"), - "iso_datetime": revision_date.strftime("%Y-%m-%d %H:%M:%S"), + + loc_revision_date.strftime("%H:%M:%S"), + "iso_date": loc_revision_date.strftime("%Y-%m-%d"), + "iso_datetime": loc_revision_date.strftime("%Y-%m-%d %H:%M:%S"), "timeago": "" - % (timestamp_in_ms, locale), + % (loc_revision_date.isoformat(), locale), } def get_revision_date_for_file( - self, path: str, locale: str = "en", fallback_to_build_date: bool = False + self, + path: str, + locale: str = "en", + time_zone: str = "UTC", + fallback_to_build_date: bool = False, ) -> dict: """ Determine localized date variants for a given file @@ -80,49 +104,60 @@ def get_revision_date_for_file( Args: path (str): Location of a markdownfile that is part of a GIT repository locale (str, optional): Locale code of language to use. Defaults to 'en'. + timezone (str): timezone database name (https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) Returns: dict: localized date variants """ + + unix_timestamp = None + # perform git log operation try: - unix_timestamp = self.repo.log(path, n=1, date="short", format="%at") + if not self.fallback_enabled: + # Retrieve author date in UNIX format (%at) + # https://git-scm.com/docs/git-log#Documentation/git-log.txt-ematem + unix_timestamp = self.repo.log(path, n=1, date="short", format="%at") + except GitCommandError as err: if fallback_to_build_date: - unix_timestamp = None logging.warning( - "Unable to read git logs of '%s'." - " Is git log readable?" - " Option 'fallback_to_build_date' enabled: so keep building..." + "[git-revision-date-localized-plugin] Unable to read git logs of '%s'. Is git log readable?" + " Option 'fallback_to_build_date' set to 'true': Falling back to build date" % path ) else: logging.error( - "Unable to read git logs of '%s'. " - "To ignore this error, set option 'fallback_to_build_date: true'" + "[git-revision-date-localized-plugin] Unable to read git logs of '%s'. " + " To ignore this error, set option 'fallback_to_build_date: true'" % path ) raise err except GitCommandNotFound as err: if fallback_to_build_date: - unix_timestamp = None logging.warning( - "Unable to perform command: git log. Is git installed?" - " Option 'fallback_to_build_date' enabled: so keep building..." + "[git-revision-date-localized-plugin] Unable to perform command: 'git log'. Is git installed?" + " Option 'fallback_to_build_date' set to 'true': Falling back to build date" ) else: logging.error( - "Unable to perform command: git log. Is git installed?" - "To ignore this error, set option 'fallback_to_build_date: true'" + "[git-revision-date-localized-plugin] Unable to perform command 'git log'. Is git installed?" + " To ignore this error, set option 'fallback_to_build_date: true'" ) raise err # create timestamp if not unix_timestamp: unix_timestamp = time.time() - logging.warning("%s has no git logs, using current timestamp" % path) + if not self.fallback_enabled: + logging.warning( + "[git-revision-date-localized-plugin] '%s' has no git logs, using current timestamp" + % path + ) - return self._date_formats(unix_timestamp=unix_timestamp, locale=locale) + return self._date_formats( + unix_timestamp=unix_timestamp, time_zone=time_zone, locale=locale + ) def is_shallow_clone(repo: Git) -> bool: diff --git a/setup.py b/setup.py index 06bd1d9..a8e66e1 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( name="mkdocs-git-revision-date-localized-plugin", - version="0.5.2", + version="0.6", description="Mkdocs plugin that enables displaying the localized date of the last git modification of a markdown file.", long_description=long_description, long_description_content_type="text/markdown", @@ -20,7 +20,7 @@ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ], - install_requires=["mkdocs>=0.17", "GitPython", "jinja2", "babel>=2.7.0"], + install_requires=["mkdocs>=1.0", "GitPython", "babel>=2.7.0"], packages=find_packages(), entry_points={ "mkdocs.plugins": [ diff --git a/tests/fixtures/basic_project/mkdocs_theme_language.yml b/tests/fixtures/basic_project/mkdocs_theme_language.yml new file mode 100644 index 0000000..fe6305b --- /dev/null +++ b/tests/fixtures/basic_project/mkdocs_theme_language.yml @@ -0,0 +1,10 @@ +site_name: test gitrevisiondatelocalized_plugin +use_directory_urls: true + +theme: + name: 'material' + language: de + +plugins: + - search + - git-revision-date-localized \ No newline at end of file diff --git a/tests/fixtures/basic_project/mkdocs_theme_locale.yml b/tests/fixtures/basic_project/mkdocs_theme_locale.yml index fe6305b..67bc499 100644 --- a/tests/fixtures/basic_project/mkdocs_theme_locale.yml +++ b/tests/fixtures/basic_project/mkdocs_theme_locale.yml @@ -3,7 +3,7 @@ use_directory_urls: true theme: name: 'material' - language: de + locale: de plugins: - search diff --git a/tests/fixtures/basic_project/mkdocs_theme_timeago.yml b/tests/fixtures/basic_project/mkdocs_theme_timeago.yml new file mode 100644 index 0000000..e853926 --- /dev/null +++ b/tests/fixtures/basic_project/mkdocs_theme_timeago.yml @@ -0,0 +1,10 @@ +site_name: test gitrevisiondatelocalized_plugin +use_directory_urls: true + +theme: + name: 'material' + +plugins: + - search + - git-revision-date-localized: + type: timeago \ No newline at end of file diff --git a/tests/test_builds.py b/tests/test_builds.py index 92ddcb9..72a042b 100644 --- a/tests/test_builds.py +++ b/tests/test_builds.py @@ -10,10 +10,6 @@ """ -# ############################################################################# -# ########## Libraries ############# -# ################################## - # standard lib import logging import os @@ -32,25 +28,25 @@ # package module from mkdocs_git_revision_date_localized_plugin.util import Util -# ############################################################################# +# ################################## # ######## Globals ################# # ################################## -PLUGIN_NAME = "git-revision-date-localized" - # custom log level to get plugin info messages logging.basicConfig(level=logging.INFO) -# ############################################################################# +# ################################## # ########## Helpers ############### # ################################## + + def get_plugin_config_from_mkdocs(mkdocs_path) -> dict: # instanciate plugin cfg_mkdocs = load_config(mkdocs_path) plugins = cfg_mkdocs.get("plugins") - plugin_loaded = plugins.get(PLUGIN_NAME) + plugin_loaded = plugins.get("git-revision-date-localized") cfg = plugin_loaded.on_config(cfg_mkdocs) logging.info("Fixture configuration loaded: " + str(cfg)) @@ -195,7 +191,7 @@ def validate_build(testproject_path, plugin_config: dict = {}): # git revision date tag page_with_tag = testproject_path / "site/page_with_tag/index.html" contents = page_with_tag.read_text(encoding="utf8") - assert re.search(r"Markdown tag\:\s[|\w].+", contents) + assert re.search(r"renders as\:\s[|\w].+", contents) repo = Util(str(testproject_path / "docs")) date_formats = repo.get_revision_date_for_file( @@ -244,23 +240,30 @@ def test_date_formats(): "datetime": "February 22, 2020 18:52:09", "iso_date": "2020-02-22", "iso_datetime": "2020-02-22 18:52:09", - "timeago": "", + "timeago": "", } -def test_missing_git_repo(tmp_path): +def test_git_not_available(tmp_path, recwarn): """ When there is no GIT repo, this should fail """ + testproject_path = setup_clean_mkdocs_folder( - mkdocs_yml_path="tests/fixtures/basic_project/mkdocs.yml", output_path=tmp_path + "tests/fixtures/basic_project/mkdocs.yml", tmp_path ) - result = build_docs_setup(testproject_path) assert ( result.exit_code == 1 ), "'mkdocs build' command succeeded while there is no GIT repo" + # assert there's a no error when fallback to build date is set to true + testproject_path = setup_clean_mkdocs_folder( + "tests/fixtures/basic_project/mkdocs_fallback_to_build_date.yml", tmp_path + ) + result = build_docs_setup(testproject_path) + assert result.exit_code == 0 + def test_build_no_options(tmp_path): # Enable plugin with no extra options set @@ -279,13 +282,19 @@ def test_build_locale_mkdocs(tmp_path): validate_mkdocs_file(tmp_path, "tests/fixtures/basic_project/mkdocs_locale.yml") -def test_material_theme(tmp_path): +def test_build_material_theme_timeago(tmp_path): + validate_mkdocs_file( + tmp_path, "tests/fixtures/basic_project/mkdocs_theme_timeago.yml" + ) + + +def test_build_material_theme(tmp_path): """ When using mkdocs-material theme, test correct working """ # theme set to 'material' with 'language' set to 'de' testproject_path = validate_mkdocs_file( - tmp_path, "tests/fixtures/basic_project/mkdocs_theme_locale.yml" + tmp_path, "tests/fixtures/basic_project/mkdocs_theme_language.yml" ) # In mkdocs-material, a 'last update' should appear @@ -295,6 +304,23 @@ def test_material_theme(tmp_path): assert re.search(r"Letztes Update\:\s[\w].+", contents) +def test_material_theme_locale(tmp_path): + """ + When using mkdocs-material theme, test correct working + """ + # theme set to 'material' with 'locale' set to 'de' + testproject_path = validate_mkdocs_file( + tmp_path, "tests/fixtures/basic_project/mkdocs_theme_locale.yml" + ) + + # In mkdocs-material, a 'last update' should appear + # in english instead of German because you should use 'language' and not locale. + # The date will be in german though + index_file = testproject_path / "site/index.html" + contents = index_file.read_text(encoding="utf8") + assert re.search(r"Last update\:\s[\w].+", contents) + + def test_material_theme_no_locale(tmp_path): """ When using mkdocs-material theme, test correct working @@ -312,12 +338,10 @@ def test_material_theme_no_locale(tmp_path): def test_type_timeago(tmp_path): - # type: 'timeago' validate_mkdocs_file(tmp_path, "tests/fixtures/basic_project/mkdocs_timeago.yml") def test_type_datetime(tmp_path): - # type: 'datetime' validate_mkdocs_file(tmp_path, "tests/fixtures/basic_project/mkdocs_datetime.yml") @@ -328,6 +352,12 @@ def test_type_unknown(tmp_path): ) +def test_build_with_timezone(tmp_path): + validate_mkdocs_file( + tmp_path, "tests/fixtures/basic_project/mkdocs_theme_timeago.yml" + ) + + def test_git_in_docs_dir(tmp_path): """ In https://github.com/timvink/mkdocs-git-revision-date-localized-plugin/pull/31 @@ -411,6 +441,3 @@ def test_low_fetch_depth(tmp_path, caplog): result = build_docs_setup(cloned_folder) assert result.exit_code == 0 assert "Running on github actions might" in caplog.text - - -# TODO: Test correct error messages when GIT is not available