From 2d34cc25b94f1423922b5f7abca973905344b5f8 Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Wed, 7 Sep 2022 23:58:11 +0200 Subject: [PATCH 01/57] Add cache for page authors --- mkdocs_git_committers_plugin_2/plugin.py | 37 ++++++++++++++++++++++-- setup.py | 2 +- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/mkdocs_git_committers_plugin_2/plugin.py b/mkdocs_git_committers_plugin_2/plugin.py index 84cc41a..cdacfa6 100644 --- a/mkdocs_git_committers_plugin_2/plugin.py +++ b/mkdocs_git_committers_plugin_2/plugin.py @@ -27,6 +27,7 @@ class GitCommittersPlugin(BasePlugin): ('branch', config_options.Type(str, default='master')), ('docs_path', config_options.Type(str, default='docs/')), ('enabled', config_options.Type(bool, default=True)), + ('cache_dir', config_options.Type(str, default='.cache/plugin/git-committers')), ) def __init__(self): @@ -34,6 +35,8 @@ def __init__(self): self.branch = 'master' self.enabled = True self.authors = dict() + self.cache_page_authors = dict() + self.cache_date = '' def on_config(self, config): self.enabled = self.config['enabled'] @@ -61,17 +64,26 @@ def list_contributors(self, path): # Use the last commit and get the date last_commit_date = time.strftime("%Y-%m-%d", time.gmtime(c.authored_date)) + # File not committed yet + if last_commit_date == "": + last_commit_date = datetime.now().strftime("%Y-%m-%d") + return [], last_commit_date + + # Try to leverage the cache + if self.cache_date and time.strptime(last_commit_date, "%Y-%m-%d") < time.strptime(self.cache_date, "%Y-%m-%d"): + return self.cache_page_authors[path]['authors'], self.cache_page_authors[path]['last_commit_date'] + url_contribs = self.githuburl + self.config['repository'] + "/contributors-list/" + self.config['branch'] + "/" + path - LOG.info("Fetching contributors for " + path) + LOG.info("git-committers: fetching contributors for " + path) LOG.debug(" from " + url_contribs) authors=[] try: response = requests.get(url_contribs) response.raise_for_status() except HTTPError as http_err: - LOG.error(f'HTTP error occurred: {http_err}\n(404 is normal if file is not on GitHub yet or Git submodule)') + LOG.error(f'git-committers: HTTP error occurred: {http_err}\n(404 is normal if file is not on GitHub yet or Git submodule)') except Exception as err: - LOG.error(f'Other error occurred: {err}') + LOG.error(f'git-committers: Other error occurred: {err}') else: html = response.text # Parse the HTML @@ -86,6 +98,8 @@ def list_contributors(self, path): avatar = img_tags[0]['src'] avatar = re.sub(r'\?.*$', '', avatar) authors.append({'login':login, 'name': name, 'url': url, 'avatar': avatar}) + # Update global cache_page_authors + self.cache_page_authors[path] = {'last_commit_date': last_commit_date, 'authors': authors} return authors, last_commit_date @@ -104,3 +118,20 @@ def on_page_context(self, context, page, config, nav): self.total_time += (end - start) return context + + def on_post_build(self, config): + LOG.info("git-committers: saving page authors cache file") + json_data = json.dumps({'cache_date': datetime.now().strftime("%Y-%m-%d"), 'page_authors': self.cache_page_authors}) + os.makedirs(self.config['cache_dir'], exist_ok=True) + f = open(self.config['cache_dir'] + "/page-authors.json", "w") + f.write(json_data) + f.close() + + def on_pre_build(self, config): + if os.path.exists(self.config['cache_dir'] + "/page-authors.json"): + LOG.info("git-committers: found page authors cache file - loading it") + f = open(self.config['cache_dir'] + "/page-authors.json", "r") + cache = json.loads(f.read()) + self.cache_date = cache['cache_date'] + self.cache_page_authors = cache['page_authors'] + f.close() diff --git a/setup.py b/setup.py index f07544b..117cf10 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='mkdocs-git-committers-plugin-2', - version='1.0.2', + version='1.1.0', description='An MkDocs plugin to create a list of contributors on the page', long_description='The git-committers plugin will seed the template context with a list of github committers and other useful GIT info such as last modified date', keywords='mkdocs pdf github', From 6d0fd64d73f173e1dbb0ef62e35c02ff179a4ad4 Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Thu, 8 Sep 2022 00:03:12 +0200 Subject: [PATCH 02/57] Add documentation for cache_dir config --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 390812d..f1bf7bb 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ More information about plugins in the [MkDocs documentation][mkdocs-plugins]. - `branch` - The name of the branch to get contributors from. Example: 'master' (default) - `enterprise_hostname` - For GitHub enterprise: the enterprise hostname. - `docs_path` - the path to the documentation folder. Defaults to `docs`. +- `cache_dir` - The path which holds the authors cache file to speed up documentation builds. Defaults to `.cache/plugin/git-committers/`. The cache file is named `page-authors.json.json`. If the token is not set in `mkdocs.yml` it will be read from the `MKDOCS_GIT_COMMITTERS_APIKEY` environment variable. From 26d67365a7620b39c7f1ece7de0ed5148a4a7943 Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Mon, 19 Sep 2022 21:22:23 +0200 Subject: [PATCH 03/57] Fix error when cache is incomplete --- mkdocs_git_committers_plugin_2/plugin.py | 5 +++-- setup.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/mkdocs_git_committers_plugin_2/plugin.py b/mkdocs_git_committers_plugin_2/plugin.py index cdacfa6..b4739dc 100644 --- a/mkdocs_git_committers_plugin_2/plugin.py +++ b/mkdocs_git_committers_plugin_2/plugin.py @@ -70,8 +70,9 @@ def list_contributors(self, path): return [], last_commit_date # Try to leverage the cache - if self.cache_date and time.strptime(last_commit_date, "%Y-%m-%d") < time.strptime(self.cache_date, "%Y-%m-%d"): - return self.cache_page_authors[path]['authors'], self.cache_page_authors[path]['last_commit_date'] + if path in self.cache_page_authors: + if self.cache_date and time.strptime(last_commit_date, "%Y-%m-%d") < time.strptime(self.cache_date, "%Y-%m-%d"): + return self.cache_page_authors[path]['authors'], self.cache_page_authors[path]['last_commit_date'] url_contribs = self.githuburl + self.config['repository'] + "/contributors-list/" + self.config['branch'] + "/" + path LOG.info("git-committers: fetching contributors for " + path) diff --git a/setup.py b/setup.py index 117cf10..c322b57 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='mkdocs-git-committers-plugin-2', - version='1.1.0', + version='1.1.1', description='An MkDocs plugin to create a list of contributors on the page', long_description='The git-committers plugin will seed the template context with a list of github committers and other useful GIT info such as last modified date', keywords='mkdocs pdf github', From 69cb97c0554389760370e2fbc88146fcf6d7d2f4 Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Thu, 13 Oct 2022 11:24:36 +0200 Subject: [PATCH 04/57] Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index f1bf7bb..d40edde 100644 --- a/README.md +++ b/README.md @@ -52,8 +52,6 @@ More information about plugins in the [MkDocs documentation][mkdocs-plugins]. - `docs_path` - the path to the documentation folder. Defaults to `docs`. - `cache_dir` - The path which holds the authors cache file to speed up documentation builds. Defaults to `.cache/plugin/git-committers/`. The cache file is named `page-authors.json.json`. -If the token is not set in `mkdocs.yml` it will be read from the `MKDOCS_GIT_COMMITTERS_APIKEY` environment variable. - ## Usage ### Display Last Commit From bc6ec962fcc21989d0b6ad4815cdc720cdf475fe Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Mon, 27 Mar 2023 22:21:40 +0200 Subject: [PATCH 05/57] Fix path for Windows users --- mkdocs_git_committers_plugin_2/plugin.py | 1 + setup.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/mkdocs_git_committers_plugin_2/plugin.py b/mkdocs_git_committers_plugin_2/plugin.py index b4739dc..b711154 100644 --- a/mkdocs_git_committers_plugin_2/plugin.py +++ b/mkdocs_git_committers_plugin_2/plugin.py @@ -59,6 +59,7 @@ def on_config(self, config): def list_contributors(self, path): last_commit_date = "" + path = path.replace("\\", "/") for c in Commit.iter_items(self.localrepo, self.localrepo.head, path): if not last_commit_date: # Use the last commit and get the date diff --git a/setup.py b/setup.py index c322b57..be6aca6 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='mkdocs-git-committers-plugin-2', - version='1.1.1', + version='1.1.2', description='An MkDocs plugin to create a list of contributors on the page', long_description='The git-committers plugin will seed the template context with a list of github committers and other useful GIT info such as last modified date', keywords='mkdocs pdf github', From 4a6587a2b7df4bdcdb6d13b7021c4c07235687b5 Mon Sep 17 00:00:00 2001 From: Mohamed Firas <62911996+Fir121@users.noreply.github.com> Date: Sun, 6 Aug 2023 21:39:30 +0400 Subject: [PATCH 06/57] allows file exclusions --- mkdocs_git_committers_plugin_2/exclude.py | 43 +++++++++++++++++++++++ mkdocs_git_committers_plugin_2/plugin.py | 8 +++++ 2 files changed, 51 insertions(+) create mode 100644 mkdocs_git_committers_plugin_2/exclude.py diff --git a/mkdocs_git_committers_plugin_2/exclude.py b/mkdocs_git_committers_plugin_2/exclude.py new file mode 100644 index 0000000..9edf811 --- /dev/null +++ b/mkdocs_git_committers_plugin_2/exclude.py @@ -0,0 +1,43 @@ +""" +Module to assist exclude certain files being processed by plugin. +Inspired by https://github.com/apenwarr/mkdocs-exclude +""" +import os +import fnmatch +from typing import List + + +def exclude(src_path: str, globs: List[str]) -> bool: + """ + Determine if a src_path should be excluded. + Supports globs (e.g. folder/* or *.md). + Credits: code adapted from + https://github.com/timvink/mkdocs-git-authors-plugin/blob/master/mkdocs_git_authors_plugin/exclude.py + Args: + src_path (src): Path of file + globs (list): list of globs + Returns: + (bool): whether src_path should be excluded + """ + assert isinstance(src_path, str) + assert isinstance(globs, list) + + for g in globs: + if fnmatch.fnmatchcase(src_path, g): + return True + + # Windows reports filenames as eg. a\\b\\c instead of a/b/c. + # To make the same globs/regexes match filenames on Windows and + # other OSes, let's try matching against converted filenames. + # On the other hand, Unix actually allows filenames to contain + # literal \\ characters (although it is rare), so we won't + # always convert them. We only convert if os.sep reports + # something unusual. Conversely, some future mkdocs might + # report Windows filenames using / separators regardless of + # os.sep, so we *always* test with / above. + if os.sep != "/": + src_path_fix = src_path.replace(os.sep, "/") + if fnmatch.fnmatchcase(src_path_fix, g): + return True + + return False diff --git a/mkdocs_git_committers_plugin_2/plugin.py b/mkdocs_git_committers_plugin_2/plugin.py index b711154..b960a7c 100644 --- a/mkdocs_git_committers_plugin_2/plugin.py +++ b/mkdocs_git_committers_plugin_2/plugin.py @@ -17,6 +17,8 @@ import re from bs4 import BeautifulSoup as bs +from mkdocs_git_committers_plugin_2.exclude import exclude + LOG = logging.getLogger("mkdocs.plugins." + __name__) class GitCommittersPlugin(BasePlugin): @@ -28,6 +30,7 @@ class GitCommittersPlugin(BasePlugin): ('docs_path', config_options.Type(str, default='docs/')), ('enabled', config_options.Type(bool, default=True)), ('cache_dir', config_options.Type(str, default='.cache/plugin/git-committers')), + ("exclude", config_options.Type(list, default=[])), ) def __init__(self): @@ -36,6 +39,7 @@ def __init__(self): self.enabled = True self.authors = dict() self.cache_page_authors = dict() + self.exclude = list() self.cache_date = '' def on_config(self, config): @@ -55,9 +59,13 @@ def on_config(self, config): self.githuburl = "https://github.com/" self.localrepo = Repo(".") self.branch = self.config['branch'] + self.excluded_pages = self.config['exclude'] return config def list_contributors(self, path): + if exclude(path.lstrip(self.config['docs_path']), self.excluded_pages): + return None, None + last_commit_date = "" path = path.replace("\\", "/") for c in Commit.iter_items(self.localrepo, self.localrepo.head, path): From 0afd7465af09ee2acd1c1d81afad1ff0575c45b1 Mon Sep 17 00:00:00 2001 From: Mohamed Firas <62911996+Fir121@users.noreply.github.com> Date: Sun, 6 Aug 2023 21:44:52 +0400 Subject: [PATCH 07/57] updated documentation --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d40edde..2df3b3c 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ More information about plugins in the [MkDocs documentation][mkdocs-plugins]. - `enterprise_hostname` - For GitHub enterprise: the enterprise hostname. - `docs_path` - the path to the documentation folder. Defaults to `docs`. - `cache_dir` - The path which holds the authors cache file to speed up documentation builds. Defaults to `.cache/plugin/git-committers/`. The cache file is named `page-authors.json.json`. +- `exclude` - Specify a list of page source paths (one per line) that should not have author(s) or last commit date included (excluded from processing by this plugin). Default is empty. [Example Usage](https://timvink.github.io/mkdocs-git-authors-plugin/options.html#exclude). ## Usage From 5a22480e999f9535f8545a3d19cb77a76fc6b42c Mon Sep 17 00:00:00 2001 From: GeoJulien Date: Mon, 4 Sep 2023 15:44:23 +0200 Subject: [PATCH 08/57] Add beautifulsoup parser lxml #32 --- requirements.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index d49735b..024a3f5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ -mkdocs>=1.0.3 +beautifulsoup4 gitpython +lxml>=4.9 +mkdocs>=1.0.3 requests -beautifulsoup4 From 4fa04efb83501ce66773645bc2672ba71d3b24c9 Mon Sep 17 00:00:00 2001 From: GeoJulien Date: Mon, 4 Sep 2023 15:54:56 +0200 Subject: [PATCH 09/57] Remove EOL Python versions and improve setup --- setup.py | 57 +++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/setup.py b/setup.py index be6aca6..2cdf35d 100644 --- a/setup.py +++ b/setup.py @@ -1,23 +1,54 @@ +from pathlib import Path +from typing import Union + from setuptools import setup, find_packages +# The directory containing this file +HERE = Path(__file__).parent + +# The text of the README file +README = (HERE / "README.md").read_text() + +def load_requirements(requirements_files: Union[Path, list[Path]]) -> list: + """Helper to load requirements list from a path or a list of paths. + + Args: + requirements_files (Path | list[Path]): path or list to paths of requirements + file(s) + + Returns: + list: list of requirements loaded from file(s) + """ + out_requirements = [] + + if isinstance(requirements_files, Path): + requirements_files = [ + requirements_files, + ] + + for requirements_file in requirements_files: + with requirements_file.open(encoding="UTF-8") as f: + out_requirements += [ + line + for line in f.read().splitlines() + if not line.startswith(("#", "-")) and len(line) + ] + + return out_requirements setup( name='mkdocs-git-committers-plugin-2', version='1.1.2', - description='An MkDocs plugin to create a list of contributors on the page', - long_description='The git-committers plugin will seed the template context with a list of github committers and other useful GIT info such as last modified date', + description='An MkDocs plugin to create a list of contributors on the page. The git-committers plugin will seed the template context with a list of github committers and other useful GIT info such as last modified date', + long_description=README, + long_description_content_type="text/markdown", keywords='mkdocs pdf github', url="iframe.php?url=https%3A%2F%2Fgithub.com%2Fojacques%2Fmkdocs-git-committers-plugin-2%2F", author='Byrne Reese, Olivier Jacques', author_email='byrne@majordojo.com, ojacques2@gmail.com', license='MIT', - python_requires='>=2.7', - install_requires=[ - 'mkdocs>=1.0.3', - 'gitpython', - 'requests', - 'beautifulsoup4' - ], + python_requires='>=3.8,<4', + install_requires=load_requirements(HERE / "requirements.txt"), classifiers=[ 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', @@ -25,10 +56,10 @@ 'License :: OSI Approved :: MIT License', 'Programming Language :: Python', 'Programming Language :: Python :: 3 :: Only', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7' + 'Programming Language :: Python :: 3.8' + 'Programming Language :: Python :: 3.9' + 'Programming Language :: Python :: 3.10' + 'Programming Language :: Python :: 3.11' ], packages=find_packages(), entry_points={ From dad462d0c4cc23e5c3185f234fb0a30238e96d32 Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Wed, 6 Sep 2023 21:22:14 +0200 Subject: [PATCH 10/57] Update version to 1.2.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 2cdf35d..70a5fa0 100644 --- a/setup.py +++ b/setup.py @@ -38,7 +38,7 @@ def load_requirements(requirements_files: Union[Path, list[Path]]) -> list: setup( name='mkdocs-git-committers-plugin-2', - version='1.1.2', + version='1.2.0', description='An MkDocs plugin to create a list of contributors on the page. The git-committers plugin will seed the template context with a list of github committers and other useful GIT info such as last modified date', long_description=README, long_description_content_type="text/markdown", From 4fd1f3138e525baa3e31d235d524cf5f537fc555 Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Wed, 6 Sep 2023 21:25:35 +0200 Subject: [PATCH 11/57] Fix setup syntax --- setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 70a5fa0..28754d3 100644 --- a/setup.py +++ b/setup.py @@ -56,9 +56,9 @@ def load_requirements(requirements_files: Union[Path, list[Path]]) -> list: 'License :: OSI Approved :: MIT License', 'Programming Language :: Python', 'Programming Language :: Python :: 3 :: Only', - 'Programming Language :: Python :: 3.8' - 'Programming Language :: Python :: 3.9' - 'Programming Language :: Python :: 3.10' + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11' ], packages=find_packages(), From 806e422bdf59bbefd995f243be275b9661d78d2f Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Wed, 1 Nov 2023 15:42:37 +0100 Subject: [PATCH 12/57] Fix #38 - now use GraphQL to fetch authors information --- README.md | 28 +++++- mkdocs_git_committers_plugin_2/plugin.py | 104 ++++++++++++++++------- requirements.txt | 3 - setup.py | 2 +- 4 files changed, 96 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 2df3b3c..e94f6b8 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,18 @@ # mkdocs-git-committers-plugin-2 -This is a plugin which is a fork from the original [`mkdocs-git-committers-plugin`](https://github.com/byrnereese/mkdocs-git-committers-plugin) by @byrnereese. +MkDocs plugin for displaying a list of committers associated with a file in +mkdocs. The plugin uses [GitHub's GraphQL +API](https://docs.github.com/en/graphql) to fetch the list of contributors for +each page. -MkDocs plugin for displaying a list of committers associated with a file in mkdocs. +Other MkDocs plugins that use information to fetch authors: + +- [`mkdocs-git-authors-plugin`](https://github.com/timvink/mkdocs-git-authors-plugin) for displaying user names a number of lines contributed (uses local Git information) +- [`mkdocs-git-committers-plugin`](https://github.com/byrnereese/mkdocs-git-committers-plugin) display contributors for a page (uses local Git information, completed with REST GitHub API v3) + +## History + +This is a fork from the original [`mkdocs-git-committers-plugin`](https://github.com/byrnereese/mkdocs-git-committers-plugin) by @byrnereese. I had to create this fork so that it could be uploaded and distributed through PyPi. The package has been renamed to `mkdocs-git-committers-plugin-2`. @@ -12,12 +22,16 @@ This "v2" differs from the original by: - Eliminate the need to match git commit logs with entries in GitHub, and thus GitHub API calls - No more risk of matching the incorrect contributor as the information comes directly from GitHub - last_commit_date is now populated with local git info -- No need for GitHub personal access token, as there are no more GitHub GraphQL API calls +- Use a cache file to speed up following builds: authors are fetched from GitHub for a page only if that page has changed since the last build -All of the above massively improves accuracy and performances. +All of the above improves accuracy and performances. Note: the plugin configuration in `mkdocs.yml` still uses the original `git-committers` sections. +## material for mkdocs theme + +This plugin is integrated in the [material for mkdocs](https://squidfunk.github.io/mkdocs-material/) theme by [Martin Donath](https://github.com/squidfunk). + ## Limitations - Getting the contributors relies on what is available on GitHub. This means that for new files, the build will report no contributors (and informed you with a 404 error which can be ignored) @@ -37,8 +51,13 @@ plugins: - git-committers: repository: organization/repository branch: main + token: !ENV ["MKDOCS_GIT_COMMITTERS_APIKEY"] ``` +If the token is not set in `mkdocs.yml` it will be read from the `MKDOCS_GIT_COMMITTERS_APIKEY` environment variable. + +**Change in 2.0.0: if no token is present, the plugin will NOT add provide git committers.** + > **Note:** 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, but now you have to enable it explicitly. More information about plugins in the [MkDocs documentation][mkdocs-plugins]. @@ -48,6 +67,7 @@ More information about plugins in the [MkDocs documentation][mkdocs-plugins]. - `enabled` - Disables plugin if set to `False` for e.g. local builds (default: `True`) - `repository` - The name of the repository, e.g. 'ojacques/mkdocs-git-committers-plugin-2' - `branch` - The name of the branch to get contributors from. Example: 'master' (default) +- `token` - A github fine-grained token for GitHub GraphQL API calls (classic tokens work too). The token does not need any scope: uncheck everything when creating the GitHub Token at [github.com/settings/personal-access-tokens/new](https://github.com/settings/personal-access-tokens/new), unless you access private repositories. - `enterprise_hostname` - For GitHub enterprise: the enterprise hostname. - `docs_path` - the path to the documentation folder. Defaults to `docs`. - `cache_dir` - The path which holds the authors cache file to speed up documentation builds. Defaults to `.cache/plugin/git-committers/`. The cache file is named `page-authors.json.json`. diff --git a/mkdocs_git_committers_plugin_2/plugin.py b/mkdocs_git_committers_plugin_2/plugin.py index b960a7c..5035c12 100644 --- a/mkdocs_git_committers_plugin_2/plugin.py +++ b/mkdocs_git_committers_plugin_2/plugin.py @@ -1,5 +1,4 @@ import os -import sys import logging from pprint import pprint from timeit import default_timer as timer @@ -13,9 +12,6 @@ import requests, json from requests.exceptions import HTTPError import time -import hashlib -import re -from bs4 import BeautifulSoup as bs from mkdocs_git_committers_plugin_2.exclude import exclude @@ -31,6 +27,7 @@ class GitCommittersPlugin(BasePlugin): ('enabled', config_options.Type(bool, default=True)), ('cache_dir', config_options.Type(str, default='.cache/plugin/git-committers')), ("exclude", config_options.Type(list, default=[])), + ('token', config_options.Type(str, default='')), ) def __init__(self): @@ -49,19 +46,83 @@ def on_config(self, config): return config LOG.info("git-committers plugin ENABLED") + if not self.config['token'] and 'MKDOCS_GIT_COMMITTERS_APIKEY' in os.environ: + self.config['token'] = os.environ['MKDOCS_GIT_COMMITTERS_APIKEY'] + if self.config['token'] and self.config['token'] != '': + self.auth_header = {'Authorization': 'token ' + self.config['token'] } + else: + LOG.warning("git-committers plugin now requires a GitHub token. Set it under 'token' mkdocs.yml config or MKDOCS_GIT_COMMITTERS_APIKEY environment variable.") if not self.config['repository']: LOG.error("git-committers plugin: repository not specified") return config if self.config['enterprise_hostname'] and self.config['enterprise_hostname'] != '': - self.githuburl = "https://" + self.config['enterprise_hostname'] + "/" + self.githuburl = "https://" + self.config['enterprise_hostname'] + "/api/graphql" else: - self.githuburl = "https://github.com/" + self.githuburl = "https://api.github.com/graphql" self.localrepo = Repo(".") self.branch = self.config['branch'] self.excluded_pages = self.config['exclude'] return config + # Get unique contributors for a given path using GitHub GraphQL API + def get_contributors_to_path(self, path): + # Query GraphQL API, and get a list of unique authors + query = { + "query": """ + { + repository(owner: "%s", name: "%s") { + object(expression: "%s") { + ... on Commit { + history(first: 100, path: "%s") { + nodes { + author { + user { + login + name + url + avatarUrl + } + } + } + } + } + } + } + } + """ % (self.config['repository'].split('/')[0], self.config['repository'].split('/')[1], self.branch, path) + } + authors = [] + if not hasattr(self, 'auth_header'): + # No auth token provided: return now + return None + LOG.info("git-committers: fetching contributors for " + path) + LOG.debug(" from " + self.githuburl) + r = requests.post(url=self.githuburl, json=query, headers=self.auth_header) + res = r.json() + #print(res) + if r.status_code == 200: + if res.get('data'): + if res['data']['repository']['object']['history']['nodes']: + for node in res['data']['repository']['object']['history']['nodes']: + # If user is not None (GitHub user was deleted) + if node['author']['user']: + login = node['author']['user']['login'] + if login not in [author['login'] for author in authors]: + authors.append({'login': node['author']['user']['login'], + 'name': node['author']['user']['name'], + 'url': node['author']['user']['url'], + 'avatar': node['author']['user']['avatarUrl']}) + return authors + else: + return [] + else: + LOG.warning("git-committers: Error from GitHub GraphQL call: " + res['errors'][0]['message']) + return [] + else: + return [] + return [] + def list_contributors(self, path): if exclude(path.lstrip(self.config['docs_path']), self.excluded_pages): return None, None @@ -78,38 +139,15 @@ def list_contributors(self, path): last_commit_date = datetime.now().strftime("%Y-%m-%d") return [], last_commit_date - # Try to leverage the cache + # Use the cache if present if cache date is newer than last commit date if path in self.cache_page_authors: if self.cache_date and time.strptime(last_commit_date, "%Y-%m-%d") < time.strptime(self.cache_date, "%Y-%m-%d"): return self.cache_page_authors[path]['authors'], self.cache_page_authors[path]['last_commit_date'] - url_contribs = self.githuburl + self.config['repository'] + "/contributors-list/" + self.config['branch'] + "/" + path - LOG.info("git-committers: fetching contributors for " + path) - LOG.debug(" from " + url_contribs) authors=[] - try: - response = requests.get(url_contribs) - response.raise_for_status() - except HTTPError as http_err: - LOG.error(f'git-committers: HTTP error occurred: {http_err}\n(404 is normal if file is not on GitHub yet or Git submodule)') - except Exception as err: - LOG.error(f'git-committers: Other error occurred: {err}') - else: - html = response.text - # Parse the HTML - soup = bs(html, "lxml") - lis = soup.find_all('li') - for li in lis: - a_tags = li.find_all('a') - login = a_tags[0]['href'].replace("/", "") - url = self.githuburl + login - name = login - img_tags = li.find_all('img') - avatar = img_tags[0]['src'] - avatar = re.sub(r'\?.*$', '', avatar) - authors.append({'login':login, 'name': name, 'url': url, 'avatar': avatar}) - # Update global cache_page_authors - self.cache_page_authors[path] = {'last_commit_date': last_commit_date, 'authors': authors} + authors = self.get_contributors_to_path(path) + + self.cache_page_authors[path] = {'last_commit_date': last_commit_date, 'authors': authors} return authors, last_commit_date diff --git a/requirements.txt b/requirements.txt index 024a3f5..1a97bd7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,2 @@ -beautifulsoup4 -gitpython -lxml>=4.9 mkdocs>=1.0.3 requests diff --git a/setup.py b/setup.py index 28754d3..8289153 100644 --- a/setup.py +++ b/setup.py @@ -38,7 +38,7 @@ def load_requirements(requirements_files: Union[Path, list[Path]]) -> list: setup( name='mkdocs-git-committers-plugin-2', - version='1.2.0', + version='2.0.0', description='An MkDocs plugin to create a list of contributors on the page. The git-committers plugin will seed the template context with a list of github committers and other useful GIT info such as last modified date', long_description=README, long_description_content_type="text/markdown", From f447cd89501d3a2334e58eac1ff1806eae43612c Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Wed, 1 Nov 2023 15:59:49 +0100 Subject: [PATCH 13/57] Create python-publish.yml --- .github/workflows/python-publish.yml | 39 ++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/python-publish.yml diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml new file mode 100644 index 0000000..bdaab28 --- /dev/null +++ b/.github/workflows/python-publish.yml @@ -0,0 +1,39 @@ +# This workflow will upload a Python Package using Twine when a release is created +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries + +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +name: Upload Python Package + +on: + release: + types: [published] + +permissions: + contents: read + +jobs: + deploy: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v3 + with: + python-version: '3.x' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install build + - name: Build package + run: python -m build + - name: Publish package + uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} From ccd3fe7e8c46f71894774d627bfe401f05011862 Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Wed, 1 Nov 2023 16:16:20 +0100 Subject: [PATCH 14/57] Manual package publishing --- .github/workflows/python-publish.yml | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index bdaab28..a22c3a3 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -9,18 +9,22 @@ name: Upload Python Package on: - release: - types: [published] - + workflow_dispatch + permissions: contents: read jobs: - deploy: - + pypi-publish: + name: Upload release to PyPI runs-on: ubuntu-latest - + environment: + name: pypi + url: https://pypi.org/p/mkdocs-git-committers-plugin-2 + permissions: + id-token: write steps: + # retrieve your distributions here - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v3 @@ -32,8 +36,5 @@ jobs: pip install build - name: Build package run: python -m build - - name: Publish package - uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 - with: - user: __token__ - password: ${{ secrets.PYPI_API_TOKEN }} + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 From 50fe762a721646cbdc4d4e4c507d4ef455a3299b Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Wed, 1 Nov 2023 16:31:55 +0100 Subject: [PATCH 15/57] Fix setup.py and auto publish --- requirements.txt | 2 -- setup.py | 84 +++++++++++++++++------------------------------- 2 files changed, 29 insertions(+), 57 deletions(-) delete mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 1a97bd7..0000000 --- a/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -mkdocs>=1.0.3 -requests diff --git a/setup.py b/setup.py index 8289153..d0bbcf0 100644 --- a/setup.py +++ b/setup.py @@ -1,70 +1,44 @@ -from pathlib import Path -from typing import Union - from setuptools import setup, find_packages +import pathlib # The directory containing this file -HERE = Path(__file__).parent +here = pathlib.Path(__file__).parent.resolve() # The text of the README file -README = (HERE / "README.md").read_text() - -def load_requirements(requirements_files: Union[Path, list[Path]]) -> list: - """Helper to load requirements list from a path or a list of paths. - - Args: - requirements_files (Path | list[Path]): path or list to paths of requirements - file(s) - - Returns: - list: list of requirements loaded from file(s) - """ - out_requirements = [] - - if isinstance(requirements_files, Path): - requirements_files = [ - requirements_files, - ] - - for requirements_file in requirements_files: - with requirements_file.open(encoding="UTF-8") as f: - out_requirements += [ - line - for line in f.read().splitlines() - if not line.startswith(("#", "-")) and len(line) - ] - - return out_requirements +long_description = (here / "README.md").read_text(encoding="utf-8") setup( - name='mkdocs-git-committers-plugin-2', - version='2.0.0', - description='An MkDocs plugin to create a list of contributors on the page. The git-committers plugin will seed the template context with a list of github committers and other useful GIT info such as last modified date', - long_description=README, + name="mkdocs-git-committers-plugin-2", + version="2.0.0", + description="An MkDocs plugin to create a list of contributors on the page. The git-committers plugin will seed the template context with a list of github committers and other useful GIT info such as last modified date", + long_description=long_description, long_description_content_type="text/markdown", - keywords='mkdocs pdf github', - url="iframe.php?url=https%3A%2F%2Fgithub.com%2Fojacques%2Fmkdocs-git-committers-plugin-2%2F", - author='Byrne Reese, Olivier Jacques', - author_email='byrne@majordojo.com, ojacques2@gmail.com', - license='MIT', - python_requires='>=3.8,<4', - install_requires=load_requirements(HERE / "requirements.txt"), + keywords="mkdocs, plugin, github, committers", + url="iframe.php?url=https%3A%2F%2Fgithub.com%2Fojacques%2Fmkdocs-git-committers-plugin-2%2F", + author="Byrne Reese, Olivier Jacques", + author_email="byrne@majordojo.com, ojacques2@gmail.com", + license="MIT", + python_requires=">=3.8,<4", + install_requires=[ + "mkdocs>=1.0.3", + "requests" + ], classifiers=[ - 'Development Status :: 4 - Beta', - 'Intended Audience :: Developers', - 'Intended Audience :: Information Technology', - 'License :: OSI Approved :: MIT License', - 'Programming Language :: Python', - 'Programming Language :: Python :: 3 :: Only', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Programming Language :: Python :: 3.11' + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Intended Audience :: Information Technology", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11" ], packages=find_packages(), entry_points={ - 'mkdocs.plugins': [ - 'git-committers = mkdocs_git_committers_plugin_2.plugin:GitCommittersPlugin' + "mkdocs.plugins": [ + "git-committers = mkdocs_git_committers_plugin_2.plugin:GitCommittersPlugin" ] } ) From 893494fdb4bf37680a016390045d454948494e28 Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Wed, 1 Nov 2023 16:43:07 +0100 Subject: [PATCH 16/57] Fix missing dependency --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index d0bbcf0..63aef1f 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,8 @@ python_requires=">=3.8,<4", install_requires=[ "mkdocs>=1.0.3", - "requests" + "requests", + "gitpython" ], classifiers=[ "Development Status :: 4 - Beta", From 0bc0b6d55bb44a6de02c7ca6b07899c184bacf1e Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Wed, 1 Nov 2023 16:49:44 +0100 Subject: [PATCH 17/57] Bump plugin version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 63aef1f..762c039 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setup( name="mkdocs-git-committers-plugin-2", - version="2.0.0", + version="2.0.1", description="An MkDocs plugin to create a list of contributors on the page. The git-committers plugin will seed the template context with a list of github committers and other useful GIT info such as last modified date", long_description=long_description, long_description_content_type="text/markdown", From c5d2dcea6fbd2170254a722c42eb027339e4f80d Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Wed, 1 Nov 2023 17:01:20 +0100 Subject: [PATCH 18/57] Create FUNDING.yml --- .github/FUNDING.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..45115cf --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# These are supported funding model platforms + +github: [ojacques] +custom: ["https://www.paypal.me/ojacques2", Paypal] From 426224133d5c3e7e76c213bcf0db844c0673127b Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Wed, 1 Nov 2023 17:04:14 +0100 Subject: [PATCH 19/57] Update FUNDING.yml --- .github/FUNDING.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 45115cf..a1ef558 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,4 +1,4 @@ # These are supported funding model platforms github: [ojacques] -custom: ["https://www.paypal.me/ojacques2", Paypal] +custom: ["https://www.paypal.me/ojacques2"] From 51ecaf874331c05d152b6c77003a18314b5adcc6 Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Wed, 1 Nov 2023 17:11:28 +0100 Subject: [PATCH 20/57] Reshuffle README sections --- README.md | 63 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index e94f6b8..1e16cd2 100644 --- a/README.md +++ b/README.md @@ -5,39 +5,16 @@ mkdocs. The plugin uses [GitHub's GraphQL API](https://docs.github.com/en/graphql) to fetch the list of contributors for each page. +For ease of use, this plugin is integrated in the [material for +mkdocs](https://squidfunk.github.io/mkdocs-material/) theme by [Martin +Donath](https://github.com/squidfunk). See [Mkdocs material +documentation](https://squidfunk.github.io/mkdocs-material/setup/adding-a-git-repository/#document-contributors). + Other MkDocs plugins that use information to fetch authors: - [`mkdocs-git-authors-plugin`](https://github.com/timvink/mkdocs-git-authors-plugin) for displaying user names a number of lines contributed (uses local Git information) - [`mkdocs-git-committers-plugin`](https://github.com/byrnereese/mkdocs-git-committers-plugin) display contributors for a page (uses local Git information, completed with REST GitHub API v3) -## History - -This is a fork from the original [`mkdocs-git-committers-plugin`](https://github.com/byrnereese/mkdocs-git-committers-plugin) by @byrnereese. - -I had to create this fork so that it could be uploaded and distributed through PyPi. The package has been renamed to `mkdocs-git-committers-plugin-2`. - -This "v2" differs from the original by: - -- Fetch contributors directly from GitHub -- Eliminate the need to match git commit logs with entries in GitHub, and thus GitHub API calls -- No more risk of matching the incorrect contributor as the information comes directly from GitHub -- last_commit_date is now populated with local git info -- Use a cache file to speed up following builds: authors are fetched from GitHub for a page only if that page has changed since the last build - -All of the above improves accuracy and performances. - -Note: the plugin configuration in `mkdocs.yml` still uses the original `git-committers` sections. - -## material for mkdocs theme - -This plugin is integrated in the [material for mkdocs](https://squidfunk.github.io/mkdocs-material/) theme by [Martin Donath](https://github.com/squidfunk). - -## Limitations - -- Getting the contributors relies on what is available on GitHub. This means that for new files, the build will report no contributors (and informed you with a 404 error which can be ignored) - When the file is merged, the contributors will be added normally. -- For now, Git submodule is not supported and will report no contributors. - ## Setup Install the plugin using pip: @@ -73,8 +50,38 @@ More information about plugins in the [MkDocs documentation][mkdocs-plugins]. - `cache_dir` - The path which holds the authors cache file to speed up documentation builds. Defaults to `.cache/plugin/git-committers/`. The cache file is named `page-authors.json.json`. - `exclude` - Specify a list of page source paths (one per line) that should not have author(s) or last commit date included (excluded from processing by this plugin). Default is empty. [Example Usage](https://timvink.github.io/mkdocs-git-authors-plugin/options.html#exclude). +## History + +This is a fork from the original [`mkdocs-git-committers-plugin`](https://github.com/byrnereese/mkdocs-git-committers-plugin) by @byrnereese. + +I had to create this fork so that it could be uploaded and distributed through PyPi. The package has been renamed to `mkdocs-git-committers-plugin-2`. + +This "v2" differs from the original by: + +- Fetch contributors directly from GitHub +- Eliminate the need to match git commit logs with entries in GitHub, and thus GitHub API calls +- No more risk of matching the incorrect contributor as the information comes directly from GitHub +- last_commit_date is now populated with local git info +- Use a cache file to speed up following builds: authors are fetched from GitHub for a page only if that page has changed since the last build + +All of the above improves accuracy and performances. + +Note: the plugin configuration in `mkdocs.yml` still uses the original `git-committers` sections. + +## Limitations + +- Getting the contributors relies on what is available on GitHub. This means that for new files, the build will report no contributors (and informed you with a 404 error which can be ignored) + When the file is merged, the contributors will be added normally. +- For now, Git submodule is not supported and will report no contributors. + ## Usage +You have 2 options to use this plugin: + +1. Use Mkdocs material theme (see [Mkdocs material +documentation](https://squidfunk.github.io/mkdocs-material/setup/adding-a-git-repository/#document-contributors)). +1. Use the plugin directly in your template. See below. + ### Display Last Commit In addition to displaying a list of committers for a file, you can also access From 7ad983daff77452b5b569bb37ceb024f4033f450 Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Wed, 1 Nov 2023 18:17:03 +0100 Subject: [PATCH 21/57] Clarify exclude documentation --- README.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1e16cd2..7c8da92 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,19 @@ More information about plugins in the [MkDocs documentation][mkdocs-plugins]. - `enterprise_hostname` - For GitHub enterprise: the enterprise hostname. - `docs_path` - the path to the documentation folder. Defaults to `docs`. - `cache_dir` - The path which holds the authors cache file to speed up documentation builds. Defaults to `.cache/plugin/git-committers/`. The cache file is named `page-authors.json.json`. -- `exclude` - Specify a list of page source paths (one per line) that should not have author(s) or last commit date included (excluded from processing by this plugin). Default is empty. [Example Usage](https://timvink.github.io/mkdocs-git-authors-plugin/options.html#exclude). +- `exclude` - Specify a list of page source paths (one per line) that should not have author(s) or last commit date included (excluded from processing by this plugin). Default is empty. Examples: + + ``` + # mkdocs.yml + plugins: + - git-committers: + exclude: + - README.md + - subfolder/page.md + - another_page.md + - all_files_inside_folder/* + - folder_and_subfolders/** + ``` ## History From 87d84576767c3158d7ab770123daaff031fcba73 Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Fri, 3 Nov 2023 00:35:33 +0100 Subject: [PATCH 22/57] New: support GitLab projects, use REST API --- README.md | 40 ++++-- mkdocs_git_committers_plugin_2/plugin.py | 149 +++++++++++++---------- setup.py | 4 +- 3 files changed, 118 insertions(+), 75 deletions(-) diff --git a/README.md b/README.md index 7c8da92..9f47712 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ # mkdocs-git-committers-plugin-2 MkDocs plugin for displaying a list of committers associated with a file in -mkdocs. The plugin uses [GitHub's GraphQL -API](https://docs.github.com/en/graphql) to fetch the list of contributors for -each page. +mkdocs. The plugin uses GitHub or GitLab API to fetch the list of contributors +for each page. + +🥳 NEW! Works with GitLab too! For ease of use, this plugin is integrated in the [material for mkdocs](https://squidfunk.github.io/mkdocs-material/) theme by [Martin @@ -23,37 +24,52 @@ Install the plugin using pip: Activate the plugin in `mkdocs.yml`: +For a repository hosted on GitHub: + ```yaml plugins: - git-committers: repository: organization/repository - branch: main - token: !ENV ["MKDOCS_GIT_COMMITTERS_APIKEY"] ``` -If the token is not set in `mkdocs.yml` it will be read from the `MKDOCS_GIT_COMMITTERS_APIKEY` environment variable. +For a repository hosted on GitLab: -**Change in 2.0.0: if no token is present, the plugin will NOT add provide git committers.** +```yaml +plugins: + - git-committers: + gitlab_repository: 12345678 + token: !ENV ["GH_TOKEN"] +``` -> **Note:** 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, but now you have to enable it explicitly. +For a repository hosted on GitLab, you need to provide a token so that the +plugin can access the GitLab API. If the token is not set in `mkdocs.yml` it +will be read from the `MKDOCS_GIT_COMMITTERS_APIKEY` environment variable. -More information about plugins in the [MkDocs documentation][mkdocs-plugins]. +For a repository hosted on GitHub, you can provide a token to increase the rate +limit and go beyond the default 60 requests per hour per IP address. The plugin +will make one request per mkdocs document. The token does not need any scope: +uncheck everything when creating the GitHub Token at +[github.com/settings/personal-access-tokens/new](https://github.com/settings/personal-access-tokens/new), +unless you access private repositories. ## Config - `enabled` - Disables plugin if set to `False` for e.g. local builds (default: `True`) -- `repository` - The name of the repository, e.g. 'ojacques/mkdocs-git-committers-plugin-2' +- `repository` - For GitHub, the name of the repository, e.g. 'ojacques/mkdocs-git-committers-plugin-2' +- `gitlab_repository` - For GitLab, the project ID, e.g. '12345678' - `branch` - The name of the branch to get contributors from. Example: 'master' (default) - `token` - A github fine-grained token for GitHub GraphQL API calls (classic tokens work too). The token does not need any scope: uncheck everything when creating the GitHub Token at [github.com/settings/personal-access-tokens/new](https://github.com/settings/personal-access-tokens/new), unless you access private repositories. -- `enterprise_hostname` - For GitHub enterprise: the enterprise hostname. +- `enterprise_hostname` - For GitHub enterprise: the GitHub enterprise hostname. +- `gitlab_hostname` - For GitLab: the GitLab hostname if different from gitlab.com (self-hosted). - `docs_path` - the path to the documentation folder. Defaults to `docs`. -- `cache_dir` - The path which holds the authors cache file to speed up documentation builds. Defaults to `.cache/plugin/git-committers/`. The cache file is named `page-authors.json.json`. +- `cache_dir` - The path which holds the authors cache file to speed up documentation builds. Defaults to `.cache/plugin/git-committers/`. The cache file is named `page-authors.json`. - `exclude` - Specify a list of page source paths (one per line) that should not have author(s) or last commit date included (excluded from processing by this plugin). Default is empty. Examples: ``` # mkdocs.yml plugins: - git-committers: + repository: organization/repository exclude: - README.md - subfolder/page.md diff --git a/mkdocs_git_committers_plugin_2/plugin.py b/mkdocs_git_committers_plugin_2/plugin.py index 5035c12..932f14c 100644 --- a/mkdocs_git_committers_plugin_2/plugin.py +++ b/mkdocs_git_committers_plugin_2/plugin.py @@ -21,7 +21,9 @@ class GitCommittersPlugin(BasePlugin): config_scheme = ( ('enterprise_hostname', config_options.Type(str, default='')), - ('repository', config_options.Type(str, default='')), + ('gitlab_hostname', config_options.Type(str, default='')), + ('repository', config_options.Type(str, default='')), # For GitHub: owner/repo + ('gitlab_repository', config_options.Type(int, default=0)), # For GitLab: project_id ('branch', config_options.Type(str, default='master')), ('docs_path', config_options.Type(str, default='docs/')), ('enabled', config_options.Type(bool, default=True)), @@ -38,6 +40,10 @@ def __init__(self): self.cache_page_authors = dict() self.exclude = list() self.cache_date = '' + self.last_request_return_code = 0 + self.githuburl = "https://api.github.com" + self.gitlaburl = "https://gitlab.com/api/v4" + self.gitlabauthors = dict() def on_config(self, config): self.enabled = self.config['enabled'] @@ -49,82 +55,101 @@ def on_config(self, config): if not self.config['token'] and 'MKDOCS_GIT_COMMITTERS_APIKEY' in os.environ: self.config['token'] = os.environ['MKDOCS_GIT_COMMITTERS_APIKEY'] - if self.config['token'] and self.config['token'] != '': - self.auth_header = {'Authorization': 'token ' + self.config['token'] } - else: - LOG.warning("git-committers plugin now requires a GitHub token. Set it under 'token' mkdocs.yml config or MKDOCS_GIT_COMMITTERS_APIKEY environment variable.") - if not self.config['repository']: + if not self.config['repository'] and not self.config['gitlab_repository']: LOG.error("git-committers plugin: repository not specified") return config if self.config['enterprise_hostname'] and self.config['enterprise_hostname'] != '': - self.githuburl = "https://" + self.config['enterprise_hostname'] + "/api/graphql" + self.githuburl = "https://" + self.config['enterprise_hostname'] + "/api" + if self.config['gitlab_hostname'] and self.config['gitlab_hostname'] != '': + self.gitlaburl = "https://" + self.config['gitlab_hostname'] + "/api/v4" + # gitlab_repository must be set + if not self.config['gitlab_repository']: + LOG.error("git-committers plugin: gitlab_repository must be set, with the GitLab project ID") + if self.config['token'] and self.config['token'] != '': + if self.config['gitlab_repository']: + self.auth_header = {'PRIVATE-TOKEN': self.config['token'] } + else: + self.auth_header = {'Authorization': 'token ' + self.config['token'] } else: - self.githuburl = "https://api.github.com/graphql" + self.auth_header = None + if self.config['gitlab_repository']: + LOG.error("git-committers plugin: GitLab API requires a token. Set it under 'token' mkdocs.yml config or MKDOCS_GIT_COMMITTERS_APIKEY environment variable.") + else: + LOG.warning("git-committers plugin may require a GitHub or GitLab token if you exceed the API rate limit or for private repositories. Set it under 'token' mkdocs.yml config or MKDOCS_GIT_COMMITTERS_APIKEY environment variable.") self.localrepo = Repo(".") self.branch = self.config['branch'] self.excluded_pages = self.config['exclude'] return config - # Get unique contributors for a given path using GitHub GraphQL API - def get_contributors_to_path(self, path): - # Query GraphQL API, and get a list of unique authors - query = { - "query": """ - { - repository(owner: "%s", name: "%s") { - object(expression: "%s") { - ... on Commit { - history(first: 100, path: "%s") { - nodes { - author { - user { - login - name - url - avatarUrl - } - } - } - } - } - } - } - } - """ % (self.config['repository'].split('/')[0], self.config['repository'].split('/')[1], self.branch, path) - } + # Get unique contributors for a given path + def get_contributors_to_file(self, path): + # We already got a 401 (unauthorized) or 403 (rate limit) error, so we don't try again + if self.last_request_return_code == 403 or self.last_request_return_code == 401: + return [] + if self.config['gitlab_repository']: + # REST endpoint is in the form https://gitlab.com/api/v4/projects/[project ID]/repository/commits?path=[uri-encoded-path]&ref_name=[branch] + url = self.gitlaburl + "/projects/" + str(self.config['gitlab_repository']) + "/repository/commits?path=" + requests.utils.quote(path) + "&ref_name=" + self.branch + else: + # REST endpoint is in the form https://api.github.com/repos/[repository]/commits?path=[uri-encoded-path]&sha=[branch]&per_page=100 + url = self.githuburl + "/repos/" + self.config['repository'] + "/commits?path=" + requests.utils.quote(path) + "&sha=" + self.branch + "&per_page=100" authors = [] - if not hasattr(self, 'auth_header'): - # No auth token provided: return now - return None LOG.info("git-committers: fetching contributors for " + path) - LOG.debug(" from " + self.githuburl) - r = requests.post(url=self.githuburl, json=query, headers=self.auth_header) - res = r.json() - #print(res) + r = requests.get(url=url, headers=self.auth_header) + self.last_request_return_code = r.status_code if r.status_code == 200: - if res.get('data'): - if res['data']['repository']['object']['history']['nodes']: - for node in res['data']['repository']['object']['history']['nodes']: - # If user is not None (GitHub user was deleted) - if node['author']['user']: - login = node['author']['user']['login'] - if login not in [author['login'] for author in authors]: - authors.append({'login': node['author']['user']['login'], - 'name': node['author']['user']['name'], - 'url': node['author']['user']['url'], - 'avatar': node['author']['user']['avatarUrl']}) - return authors - else: - return [] + # Get login, url and avatar for each author. Ensure no duplicates. + res = r.json() + for commit in res: + if not self.config['gitlab_repository']: + # GitHub + if commit['author'] and commit['author']['login'] and commit['author']['login'] not in [author['login'] for author in authors]: + authors.append({'login': commit['author']['login'], + 'name': commit['author']['login'], + 'url': commit['author']['html_url'], + 'avatar': commit['author']['avatar_url']}) else: - LOG.warning("git-committers: Error from GitHub GraphQL call: " + res['errors'][0]['message']) - return [] + # GitLab + if commit['author_name']: + # If author is not already in the list of authors + if commit['author_name'] not in [author['name'] for author in authors]: + # Look for GitLab author in our cache self.gitlabauthors. If not found fetch it from GitLab API and save it in cache. + if commit['author_name'] in self.gitlabauthors: + authors.append({'login': self.gitlabauthors[commit['author_name']]['username'], + 'name': commit['author_name'], + 'url': self.gitlabauthors[commit['author_name']]['web_url'], + 'avatar': self.gitlabauthors[commit['author_name']]['avatar_url']}) + else: + # Fetch author from GitLab API + url = self.gitlaburl + "/users?search=" + requests.utils.quote(commit['author_name']) + r = requests.get(url=url, headers=self.auth_header) + if r.status_code == 200: + res = r.json() + if len(res) > 0: + # Go through all users until we find the one with the same name + for user in res: + if user['name'] == commit['author_name']: + # Save it in cache + self.gitlabauthors[commit['author_name']] = user + authors.append({'login': user['username'], + 'name': user['name'], + 'url': user['web_url'], + 'avatar': user['avatar_url']}) + break + else: + LOG.error("git-committers: " + str(r.status_code) + " " + r.reason) + return authors else: - return [] + LOG.error("git-committers: error fetching contributors for " + path) + if r.status_code == 403 or r.status_code == 401: + LOG.error("git-committers: " + str(r.status_code) + " " + r.reason + " - You may have exceeded the API rate limit or need to be authorized. You can set a token under 'token' mkdocs.yml config or MKDOCS_GIT_COMMITTERS_APIKEY environment variable.") + else: + LOG.error("git-committers: " + str(r.status_code) + " " + r.reason) + return [] return [] def list_contributors(self, path): if exclude(path.lstrip(self.config['docs_path']), self.excluded_pages): + LOG.warning("git-committers: " + path + " is excluded") return None, None last_commit_date = "" @@ -142,10 +167,12 @@ def list_contributors(self, path): # Use the cache if present if cache date is newer than last commit date if path in self.cache_page_authors: if self.cache_date and time.strptime(last_commit_date, "%Y-%m-%d") < time.strptime(self.cache_date, "%Y-%m-%d"): - return self.cache_page_authors[path]['authors'], self.cache_page_authors[path]['last_commit_date'] + # If page_autors in cache is not empty, return it + if self.cache_page_authors[path]['authors']: + return self.cache_page_authors[path]['authors'], self.cache_page_authors[path]['last_commit_date'] authors=[] - authors = self.get_contributors_to_path(path) + authors = self.get_contributors_to_file(path) self.cache_page_authors[path] = {'last_commit_date': last_commit_date, 'authors': authors} diff --git a/setup.py b/setup.py index 762c039..793c41b 100644 --- a/setup.py +++ b/setup.py @@ -9,8 +9,8 @@ setup( name="mkdocs-git-committers-plugin-2", - version="2.0.1", - description="An MkDocs plugin to create a list of contributors on the page. The git-committers plugin will seed the template context with a list of github committers and other useful GIT info such as last modified date", + version="2.1.0", + description="An MkDocs plugin to create a list of contributors on the page. The git-committers plugin will seed the template context with a list of GitHub or GitLab committers and other useful GIT info such as last modified date", long_description=long_description, long_description_content_type="text/markdown", keywords="mkdocs, plugin, github, committers", From 153043d98d33896d7354a2f110bb27e6f2d9de9d Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Fri, 3 Nov 2023 00:40:57 +0100 Subject: [PATCH 23/57] Fix token explanation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9f47712..66c4d1a 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ unless you access private repositories. - `repository` - For GitHub, the name of the repository, e.g. 'ojacques/mkdocs-git-committers-plugin-2' - `gitlab_repository` - For GitLab, the project ID, e.g. '12345678' - `branch` - The name of the branch to get contributors from. Example: 'master' (default) -- `token` - A github fine-grained token for GitHub GraphQL API calls (classic tokens work too). The token does not need any scope: uncheck everything when creating the GitHub Token at [github.com/settings/personal-access-tokens/new](https://github.com/settings/personal-access-tokens/new), unless you access private repositories. +- `token` - A GitHub or GitLab personal access token for REST API calls. The token does not need any scope: uncheck everything when creating the GitHub Token at [github.com/settings/personal-access-tokens/new](https://github.com/settings/personal-access-tokens/new), unless you access private repositories. For GitLab, create a token at [gitlab.com/-/profile/personal_access_tokens](https://gitlab.com/-/profile/personal_access_tokens). - `enterprise_hostname` - For GitHub enterprise: the GitHub enterprise hostname. - `gitlab_hostname` - For GitLab: the GitLab hostname if different from gitlab.com (self-hosted). - `docs_path` - the path to the documentation folder. Defaults to `docs`. From 1c1ff87d5989df70b5f09f5bc9c2ff86c89c619f Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Fri, 3 Nov 2023 15:33:18 +0100 Subject: [PATCH 24/57] Add source attribute github/gitlab to authors --- mkdocs_git_committers_plugin_2/plugin.py | 19 +++++++++++-------- setup.py | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/mkdocs_git_committers_plugin_2/plugin.py b/mkdocs_git_committers_plugin_2/plugin.py index 932f14c..6398ef8 100644 --- a/mkdocs_git_committers_plugin_2/plugin.py +++ b/mkdocs_git_committers_plugin_2/plugin.py @@ -43,7 +43,7 @@ def __init__(self): self.last_request_return_code = 0 self.githuburl = "https://api.github.com" self.gitlaburl = "https://gitlab.com/api/v4" - self.gitlabauthors = dict() + self.gitlabauthors_cache = dict() def on_config(self, config): self.enabled = self.config['enabled'] @@ -106,18 +106,20 @@ def get_contributors_to_file(self, path): authors.append({'login': commit['author']['login'], 'name': commit['author']['login'], 'url': commit['author']['html_url'], - 'avatar': commit['author']['avatar_url']}) + 'avatar': commit['author']['avatar_url'], + 'source': 'github'}) else: # GitLab if commit['author_name']: # If author is not already in the list of authors if commit['author_name'] not in [author['name'] for author in authors]: # Look for GitLab author in our cache self.gitlabauthors. If not found fetch it from GitLab API and save it in cache. - if commit['author_name'] in self.gitlabauthors: - authors.append({'login': self.gitlabauthors[commit['author_name']]['username'], + if commit['author_name'] in self.gitlabauthors_cache: + authors.append({'login': self.gitlabauthors_cache[commit['author_name']]['username'], 'name': commit['author_name'], - 'url': self.gitlabauthors[commit['author_name']]['web_url'], - 'avatar': self.gitlabauthors[commit['author_name']]['avatar_url']}) + 'url': self.gitlabauthors_cache[commit['author_name']]['web_url'], + 'avatar': self.gitlabauthors_cache[commit['author_name']]['avatar_url'], + 'source': 'gitlab'}) else: # Fetch author from GitLab API url = self.gitlaburl + "/users?search=" + requests.utils.quote(commit['author_name']) @@ -129,11 +131,12 @@ def get_contributors_to_file(self, path): for user in res: if user['name'] == commit['author_name']: # Save it in cache - self.gitlabauthors[commit['author_name']] = user + self.gitlabauthors_cache[commit['author_name']] = user authors.append({'login': user['username'], 'name': user['name'], 'url': user['web_url'], - 'avatar': user['avatar_url']}) + 'avatar': user['avatar_url'], + 'source': 'gitlab'}) break else: LOG.error("git-committers: " + str(r.status_code) + " " + r.reason) diff --git a/setup.py b/setup.py index 793c41b..37cf367 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setup( name="mkdocs-git-committers-plugin-2", - version="2.1.0", + version="2.2.0", description="An MkDocs plugin to create a list of contributors on the page. The git-committers plugin will seed the template context with a list of GitHub or GitLab committers and other useful GIT info such as last modified date", long_description=long_description, long_description_content_type="text/markdown", From e3d547452a2e74baab2d061c4589e9459be53f8a Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Fri, 3 Nov 2023 17:22:03 +0100 Subject: [PATCH 25/57] Add committers-source github/gitlab in page context, remove it from author --- mkdocs_git_committers_plugin_2/plugin.py | 16 ++++++++++------ setup.py | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/mkdocs_git_committers_plugin_2/plugin.py b/mkdocs_git_committers_plugin_2/plugin.py index 6398ef8..3d1c963 100644 --- a/mkdocs_git_committers_plugin_2/plugin.py +++ b/mkdocs_git_committers_plugin_2/plugin.py @@ -106,8 +106,8 @@ def get_contributors_to_file(self, path): authors.append({'login': commit['author']['login'], 'name': commit['author']['login'], 'url': commit['author']['html_url'], - 'avatar': commit['author']['avatar_url'], - 'source': 'github'}) + 'avatar': commit['author']['avatar_url'] + }) else: # GitLab if commit['author_name']: @@ -118,8 +118,8 @@ def get_contributors_to_file(self, path): authors.append({'login': self.gitlabauthors_cache[commit['author_name']]['username'], 'name': commit['author_name'], 'url': self.gitlabauthors_cache[commit['author_name']]['web_url'], - 'avatar': self.gitlabauthors_cache[commit['author_name']]['avatar_url'], - 'source': 'gitlab'}) + 'avatar': self.gitlabauthors_cache[commit['author_name']]['avatar_url'] + }) else: # Fetch author from GitLab API url = self.gitlaburl + "/users?search=" + requests.utils.quote(commit['author_name']) @@ -135,8 +135,8 @@ def get_contributors_to_file(self, path): authors.append({'login': user['username'], 'name': user['name'], 'url': user['web_url'], - 'avatar': user['avatar_url'], - 'source': 'gitlab'}) + 'avatar': user['avatar_url'] + }) break else: LOG.error("git-committers: " + str(r.status_code) + " " + r.reason) @@ -192,6 +192,10 @@ def on_page_context(self, context, page, config, nav): context['committers'] = authors if last_commit_date: context['last_commit_date'] = last_commit_date + if not self.config['gitlab_repository']: + context['committers-source'] = 'github' + else: + context['committers-source'] = 'gitlab' end = timer() self.total_time += (end - start) diff --git a/setup.py b/setup.py index 37cf367..88ccc51 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setup( name="mkdocs-git-committers-plugin-2", - version="2.2.0", + version="2.2.1", description="An MkDocs plugin to create a list of contributors on the page. The git-committers plugin will seed the template context with a list of GitHub or GitLab committers and other useful GIT info such as last modified date", long_description=long_description, long_description_content_type="text/markdown", From c28ea8e15ba9b17909a6635521ecdd6a0d7dd9be Mon Sep 17 00:00:00 2001 From: PTKay Date: Fri, 3 Nov 2023 19:34:37 +0000 Subject: [PATCH 26/57] Move exclude check to on_page_context --- mkdocs_git_committers_plugin_2/plugin.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mkdocs_git_committers_plugin_2/plugin.py b/mkdocs_git_committers_plugin_2/plugin.py index 3d1c963..cf03872 100644 --- a/mkdocs_git_committers_plugin_2/plugin.py +++ b/mkdocs_git_committers_plugin_2/plugin.py @@ -151,10 +151,6 @@ def get_contributors_to_file(self, path): return [] def list_contributors(self, path): - if exclude(path.lstrip(self.config['docs_path']), self.excluded_pages): - LOG.warning("git-committers: " + path + " is excluded") - return None, None - last_commit_date = "" path = path.replace("\\", "/") for c in Commit.iter_items(self.localrepo, self.localrepo.head, path): @@ -182,6 +178,10 @@ def list_contributors(self, path): return authors, last_commit_date def on_page_context(self, context, page, config, nav): + if exclude(page.file.src_path, self.excluded_pages): + LOG.warning("git-committers: " + page.file.src_path + " is excluded") + return context + context['committers'] = [] if not self.enabled: return context From d9cd87ab14c6aed9f4db36892c4d8bdc137544cb Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Sat, 4 Nov 2023 10:01:58 +0100 Subject: [PATCH 27/57] Switch to committers_source in the context --- mkdocs_git_committers_plugin_2/plugin.py | 6 +++--- setup.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mkdocs_git_committers_plugin_2/plugin.py b/mkdocs_git_committers_plugin_2/plugin.py index 3d1c963..580b087 100644 --- a/mkdocs_git_committers_plugin_2/plugin.py +++ b/mkdocs_git_committers_plugin_2/plugin.py @@ -75,7 +75,7 @@ def on_config(self, config): if self.config['gitlab_repository']: LOG.error("git-committers plugin: GitLab API requires a token. Set it under 'token' mkdocs.yml config or MKDOCS_GIT_COMMITTERS_APIKEY environment variable.") else: - LOG.warning("git-committers plugin may require a GitHub or GitLab token if you exceed the API rate limit or for private repositories. Set it under 'token' mkdocs.yml config or MKDOCS_GIT_COMMITTERS_APIKEY environment variable.") + LOG.warning("git-committers plugin may require a GitHub token if you exceed the API rate limit or for private repositories. Set it under 'token' mkdocs.yml config or MKDOCS_GIT_COMMITTERS_APIKEY environment variable.") self.localrepo = Repo(".") self.branch = self.config['branch'] self.excluded_pages = self.config['exclude'] @@ -193,9 +193,9 @@ def on_page_context(self, context, page, config, nav): if last_commit_date: context['last_commit_date'] = last_commit_date if not self.config['gitlab_repository']: - context['committers-source'] = 'github' + context['committers_source'] = 'github' else: - context['committers-source'] = 'gitlab' + context['committers_source'] = 'gitlab' end = timer() self.total_time += (end - start) diff --git a/setup.py b/setup.py index 88ccc51..31525f4 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setup( name="mkdocs-git-committers-plugin-2", - version="2.2.1", + version="2.2.2", description="An MkDocs plugin to create a list of contributors on the page. The git-committers plugin will seed the template context with a list of GitHub or GitLab committers and other useful GIT info such as last modified date", long_description=long_description, long_description_content_type="text/markdown", From 6e60b020336ed90590f12c268a9c4fb1595fe49f Mon Sep 17 00:00:00 2001 From: PTKay Date: Sat, 4 Nov 2023 15:02:28 +0000 Subject: [PATCH 28/57] Execute exclusion check only if plugin is enabled --- mkdocs_git_committers_plugin_2/plugin.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mkdocs_git_committers_plugin_2/plugin.py b/mkdocs_git_committers_plugin_2/plugin.py index cf03872..752c6d6 100644 --- a/mkdocs_git_committers_plugin_2/plugin.py +++ b/mkdocs_git_committers_plugin_2/plugin.py @@ -178,13 +178,12 @@ def list_contributors(self, path): return authors, last_commit_date def on_page_context(self, context, page, config, nav): - if exclude(page.file.src_path, self.excluded_pages): - LOG.warning("git-committers: " + page.file.src_path + " is excluded") - return context - context['committers'] = [] if not self.enabled: return context + if exclude(page.file.src_path, self.excluded_pages): + LOG.warning("git-committers: " + page.file.src_path + " is excluded") + return context start = timer() git_path = self.config['docs_path'] + page.file.src_path authors, last_commit_date = self.list_contributors(git_path) From ac4b0752db7e3695d980abd0ffbc106a7211bd8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thor=20K=2E=20H=C3=B8g=C3=A5s?= Date: Sun, 24 Dec 2023 00:07:51 +0000 Subject: [PATCH 29/57] fix: search parent directories for the repo Allows for repositories where their documentation pipeline is not run from the root directory, e.g. a documentation repository where `mkdocs.yml` is at `docs/mkdocs.yml`, along with e.g. `docs/pyproject.toml`. Particularly relevant for non-Python projects. --- mkdocs_git_committers_plugin_2/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs_git_committers_plugin_2/plugin.py b/mkdocs_git_committers_plugin_2/plugin.py index 580b087..66da28a 100644 --- a/mkdocs_git_committers_plugin_2/plugin.py +++ b/mkdocs_git_committers_plugin_2/plugin.py @@ -76,7 +76,7 @@ def on_config(self, config): LOG.error("git-committers plugin: GitLab API requires a token. Set it under 'token' mkdocs.yml config or MKDOCS_GIT_COMMITTERS_APIKEY environment variable.") else: LOG.warning("git-committers plugin may require a GitHub token if you exceed the API rate limit or for private repositories. Set it under 'token' mkdocs.yml config or MKDOCS_GIT_COMMITTERS_APIKEY environment variable.") - self.localrepo = Repo(".") + self.localrepo = Repo(".", search_parent_directories=True) self.branch = self.config['branch'] self.excluded_pages = self.config['exclude'] return config From 64dee9579ec28f22b5da56c8a10c5675f15e7a5c Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Sun, 7 Jan 2024 10:47:53 +0100 Subject: [PATCH 30/57] Prepare new release --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 31525f4..5d68f5a 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setup( name="mkdocs-git-committers-plugin-2", - version="2.2.2", + version="2.2.3", description="An MkDocs plugin to create a list of contributors on the page. The git-committers plugin will seed the template context with a list of GitHub or GitLab committers and other useful GIT info such as last modified date", long_description=long_description, long_description_content_type="text/markdown", From 46780f06a6abe25488a3183ff490170141654090 Mon Sep 17 00:00:00 2001 From: vrenjith Date: Fri, 19 Jan 2024 23:39:55 +0530 Subject: [PATCH 31/57] Include API Version in the URL --- mkdocs_git_committers_plugin_2/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs_git_committers_plugin_2/plugin.py b/mkdocs_git_committers_plugin_2/plugin.py index 00e3f44..26bf6f9 100644 --- a/mkdocs_git_committers_plugin_2/plugin.py +++ b/mkdocs_git_committers_plugin_2/plugin.py @@ -59,7 +59,7 @@ def on_config(self, config): LOG.error("git-committers plugin: repository not specified") return config if self.config['enterprise_hostname'] and self.config['enterprise_hostname'] != '': - self.githuburl = "https://" + self.config['enterprise_hostname'] + "/api" + self.githuburl = "https://" + self.config['enterprise_hostname'] + "/api" + self.config['api_version'] if self.config['gitlab_hostname'] and self.config['gitlab_hostname'] != '': self.gitlaburl = "https://" + self.config['gitlab_hostname'] + "/api/v4" # gitlab_repository must be set From d26cdd84f8bfe9d971a21fed9ceb7347a1dd49ee Mon Sep 17 00:00:00 2001 From: vrenjith Date: Fri, 19 Jan 2024 23:40:51 +0530 Subject: [PATCH 32/57] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 66c4d1a..5081dc2 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ unless you access private repositories. - `token` - A GitHub or GitLab personal access token for REST API calls. The token does not need any scope: uncheck everything when creating the GitHub Token at [github.com/settings/personal-access-tokens/new](https://github.com/settings/personal-access-tokens/new), unless you access private repositories. For GitLab, create a token at [gitlab.com/-/profile/personal_access_tokens](https://gitlab.com/-/profile/personal_access_tokens). - `enterprise_hostname` - For GitHub enterprise: the GitHub enterprise hostname. - `gitlab_hostname` - For GitLab: the GitLab hostname if different from gitlab.com (self-hosted). +- `api_version` - For GithHub Enterprise: The version part that needs to be appended to the URL. E.g. `/v3` - `docs_path` - the path to the documentation folder. Defaults to `docs`. - `cache_dir` - The path which holds the authors cache file to speed up documentation builds. Defaults to `.cache/plugin/git-committers/`. The cache file is named `page-authors.json`. - `exclude` - Specify a list of page source paths (one per line) that should not have author(s) or last commit date included (excluded from processing by this plugin). Default is empty. Examples: From ca9eb9010b85a86881b88ceef64a510caa05c156 Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 15 Feb 2024 12:55:17 +0100 Subject: [PATCH 33/57] change excluded file log level to INFO --- mkdocs_git_committers_plugin_2/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs_git_committers_plugin_2/plugin.py b/mkdocs_git_committers_plugin_2/plugin.py index 00e3f44..48f0b66 100644 --- a/mkdocs_git_committers_plugin_2/plugin.py +++ b/mkdocs_git_committers_plugin_2/plugin.py @@ -182,7 +182,7 @@ def on_page_context(self, context, page, config, nav): if not self.enabled: return context if exclude(page.file.src_path, self.excluded_pages): - LOG.warning("git-committers: " + page.file.src_path + " is excluded") + LOG.info("git-committers: " + page.file.src_path + " is excluded") return context start = timer() git_path = self.config['docs_path'] + page.file.src_path From 2148c8c4d5599282ecc3d814432cccabb331e0d5 Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Sat, 24 Feb 2024 09:23:27 +0100 Subject: [PATCH 34/57] New release --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 5d68f5a..5e65d5f 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setup( name="mkdocs-git-committers-plugin-2", - version="2.2.3", + version="2.3.0", description="An MkDocs plugin to create a list of contributors on the page. The git-committers plugin will seed the template context with a list of GitHub or GitLab committers and other useful GIT info such as last modified date", long_description=long_description, long_description_content_type="text/markdown", From 932c4d8c748ae4e56386f40ff9846bdb3034337b Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Sat, 24 Feb 2024 09:30:56 +0100 Subject: [PATCH 35/57] Remove ref to 404, add contributors --- README.md | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 66c4d1a..557a08b 100644 --- a/README.md +++ b/README.md @@ -55,15 +55,27 @@ unless you access private repositories. ## Config - `enabled` - Disables plugin if set to `False` for e.g. local builds (default: `True`) -- `repository` - For GitHub, the name of the repository, e.g. 'ojacques/mkdocs-git-committers-plugin-2' +- `repository` - For GitHub, the name of the repository, e.g. + 'ojacques/mkdocs-git-committers-plugin-2' - `gitlab_repository` - For GitLab, the project ID, e.g. '12345678' -- `branch` - The name of the branch to get contributors from. Example: 'master' (default) -- `token` - A GitHub or GitLab personal access token for REST API calls. The token does not need any scope: uncheck everything when creating the GitHub Token at [github.com/settings/personal-access-tokens/new](https://github.com/settings/personal-access-tokens/new), unless you access private repositories. For GitLab, create a token at [gitlab.com/-/profile/personal_access_tokens](https://gitlab.com/-/profile/personal_access_tokens). +- `branch` - The name of the branch to get contributors from. Example: 'master' + (default) +- `token` - A GitHub or GitLab personal access token for REST API calls. The + token does not need any scope: uncheck everything when creating the GitHub + Token at + [github.com/settings/personal-access-tokens/new](https://github.com/settings/personal-access-tokens/new), + unless you access private repositories. For GitLab, create a token at + [gitlab.com/-/profile/personal_access_tokens](https://gitlab.com/-/profile/personal_access_tokens). - `enterprise_hostname` - For GitHub enterprise: the GitHub enterprise hostname. -- `gitlab_hostname` - For GitLab: the GitLab hostname if different from gitlab.com (self-hosted). +- `gitlab_hostname` - For GitLab: the GitLab hostname if different from + gitlab.com (self-hosted). - `docs_path` - the path to the documentation folder. Defaults to `docs`. -- `cache_dir` - The path which holds the authors cache file to speed up documentation builds. Defaults to `.cache/plugin/git-committers/`. The cache file is named `page-authors.json`. -- `exclude` - Specify a list of page source paths (one per line) that should not have author(s) or last commit date included (excluded from processing by this plugin). Default is empty. Examples: +- `cache_dir` - The path which holds the authors cache file to speed up + documentation builds. Defaults to `.cache/plugin/git-committers/`. The cache + file is named `page-authors.json`. +- `exclude` - Specify a list of page source paths (one per line) that should not + have author(s) or last commit date included (excluded from processing by this + plugin). Default is empty. Examples: ``` # mkdocs.yml @@ -98,8 +110,7 @@ Note: the plugin configuration in `mkdocs.yml` still uses the original `git-comm ## Limitations -- Getting the contributors relies on what is available on GitHub. This means that for new files, the build will report no contributors (and informed you with a 404 error which can be ignored) - When the file is merged, the contributors will be added normally. +- Getting the contributors relies on what is available on GitHub or GitLab. - For now, Git submodule is not supported and will report no contributors. ## Usage @@ -207,3 +218,10 @@ Thank you to the following contributors: - Nathan Hernandez - Chris Northwood - Martin Donath +- PTKay +- Guts +- Fir121 +- dstockhammer +- thor +- n2N8Z +- barreeeiroo From b3d1a6c09e4e6b5c94bc3ee49a1e73f01cfd2624 Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Sat, 24 Feb 2024 09:48:53 +0100 Subject: [PATCH 36/57] Add support for both GitHub and GitLab self-hosted --- README.md | 3 ++- mkdocs_git_committers_plugin_2/plugin.py | 10 ++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 573ec61..4c817da 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,8 @@ unless you access private repositories. - `enterprise_hostname` - For GitHub enterprise: the GitHub enterprise hostname. - `gitlab_hostname` - For GitLab: the GitLab hostname if different from gitlab.com (self-hosted). -- `api_version` - For GitHub Enterprise: The version part that needs to be appended to the URL. E.g. `/v3` +- `api_version` - For GitHub and GitLab self-hosted, the API version part that needs to be appended to the URL. + Defaults to v4 for GitLab, and nothing for GitHub Enterprise (you may need `v3`). - `docs_path` - the path to the documentation folder. Defaults to `docs`. - `cache_dir` - The path which holds the authors cache file to speed up documentation builds. Defaults to `.cache/plugin/git-committers/`. The cache diff --git a/mkdocs_git_committers_plugin_2/plugin.py b/mkdocs_git_committers_plugin_2/plugin.py index 60d915c..76577d0 100644 --- a/mkdocs_git_committers_plugin_2/plugin.py +++ b/mkdocs_git_committers_plugin_2/plugin.py @@ -59,9 +59,15 @@ def on_config(self, config): LOG.error("git-committers plugin: repository not specified") return config if self.config['enterprise_hostname'] and self.config['enterprise_hostname'] != '': - self.githuburl = "https://" + self.config['enterprise_hostname'] + "/api" + self.config['api_version'] + if not self.config['api_version']: + self.githuburl = "https://" + self.config['enterprise_hostname'] + "/api" + else: + self.githuburl = "https://" + self.config['enterprise_hostname'] + "/api/" + self.config['api_version'] if self.config['gitlab_hostname'] and self.config['gitlab_hostname'] != '': - self.gitlaburl = "https://" + self.config['gitlab_hostname'] + "/api/v4" + if not self.config['api_version']: + self.gitlaburl = "https://" + self.config['gitlab_hostname'] + "/api/v4" + else: + self.gitlaburl = "https://" + self.config['gitlab_hostname'] + "/api/" + self.config['api_version'] # gitlab_repository must be set if not self.config['gitlab_repository']: LOG.error("git-committers plugin: gitlab_repository must be set, with the GitLab project ID") From ab15c14b0aa03acf28ca649b128546fa05a371ce Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Sat, 24 Feb 2024 09:58:14 +0100 Subject: [PATCH 37/57] Add precisions on token --- README.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 4c817da..d69c900 100644 --- a/README.md +++ b/README.md @@ -60,12 +60,18 @@ unless you access private repositories. - `gitlab_repository` - For GitLab, the project ID, e.g. '12345678' - `branch` - The name of the branch to get contributors from. Example: 'master' (default) -- `token` - A GitHub or GitLab personal access token for REST API calls. The - token does not need any scope: uncheck everything when creating the GitHub - Token at - [github.com/settings/personal-access-tokens/new](https://github.com/settings/personal-access-tokens/new), - unless you access private repositories. For GitLab, create a token at - [gitlab.com/-/profile/personal_access_tokens](https://gitlab.com/-/profile/personal_access_tokens). +- `token` - A GitHub or GitLab personal access token for REST API calls. + - For GitHub, token does not need any scope: uncheck everything when creating + the GitHub Token at + [github.com/settings/personal-access-tokens/new](https://github.com/settings/personal-access-tokens/new), + unless you access private repositories. + - For GitLab, a + [project access token](https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html) + scoped to `read_api` is expected to work. That way, the token is limited to + the project and has access to read the repository. You could use a personal + access token at + [gitlab.com/-/profile/personal_access_tokens](https://gitlab.com/-/profile/personal_access_tokens), + but it will grant access to more repositories than you want. - `enterprise_hostname` - For GitHub enterprise: the GitHub enterprise hostname. - `gitlab_hostname` - For GitLab: the GitLab hostname if different from gitlab.com (self-hosted). @@ -114,6 +120,7 @@ Note: the plugin configuration in `mkdocs.yml` still uses the original `git-comm - Getting the contributors relies on what is available on GitHub or GitLab. - For now, Git submodule is not supported and will report no contributors. +- GitLab users may not be properly identified. See [issue #50](https://github.com/ojacques/mkdocs-git-committers-plugin-2/issues/50) ## Usage From 5da612be1b95ed3ce65dda6d8fdadfcfded41db5 Mon Sep 17 00:00:00 2001 From: Andrew Rowson <133028181+andrew-rowson-lseg@users.noreply.github.com> Date: Fri, 5 Apr 2024 10:57:12 +0100 Subject: [PATCH 38/57] fix: Added api_version to config mkdocs build in strict mode fails if you pass in parameters that aren't explicitly listed out in the plugin config. `api_version` needs to be present in `self.config` for the url builder to work, so this just adds the value in, defaulting to None. --- mkdocs_git_committers_plugin_2/plugin.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mkdocs_git_committers_plugin_2/plugin.py b/mkdocs_git_committers_plugin_2/plugin.py index 76577d0..faca5a4 100644 --- a/mkdocs_git_committers_plugin_2/plugin.py +++ b/mkdocs_git_committers_plugin_2/plugin.py @@ -24,6 +24,7 @@ class GitCommittersPlugin(BasePlugin): ('gitlab_hostname', config_options.Type(str, default='')), ('repository', config_options.Type(str, default='')), # For GitHub: owner/repo ('gitlab_repository', config_options.Type(int, default=0)), # For GitLab: project_id + ('api_version', config_options.Type(str, default=None)), ('branch', config_options.Type(str, default='master')), ('docs_path', config_options.Type(str, default='docs/')), ('enabled', config_options.Type(bool, default=True)), From 02a4416892a03828d7768ad61dbf33227b4513a7 Mon Sep 17 00:00:00 2001 From: Johnson Sun Date: Thu, 11 Jul 2024 22:12:53 +0800 Subject: [PATCH 39/57] Update README on private GitHub repo --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index d69c900..bdf5065 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,8 @@ uncheck everything when creating the GitHub Token at [github.com/settings/personal-access-tokens/new](https://github.com/settings/personal-access-tokens/new), unless you access private repositories. +For private GitHub repositories, you only need to allow read-only access to `Contents` and `Metadata` on the target repository. This could be done by setting `Read-only` access of `Permissions > Repository permissions > Contents`. + ## Config - `enabled` - Disables plugin if set to `False` for e.g. local builds (default: `True`) From 2c2ec0e20f92fd7d6d10907303208bb4d30d6b57 Mon Sep 17 00:00:00 2001 From: Johnson Sun Date: Thu, 11 Jul 2024 23:02:55 +0800 Subject: [PATCH 40/57] Include committers in GitHub repos --- mkdocs_git_committers_plugin_2/plugin.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mkdocs_git_committers_plugin_2/plugin.py b/mkdocs_git_committers_plugin_2/plugin.py index faca5a4..d96ed84 100644 --- a/mkdocs_git_committers_plugin_2/plugin.py +++ b/mkdocs_git_committers_plugin_2/plugin.py @@ -115,6 +115,12 @@ def get_contributors_to_file(self, path): 'url': commit['author']['html_url'], 'avatar': commit['author']['avatar_url'] }) + if commit['committer'] and commit['committer']['login'] and commit['committer']['login'] not in [author['login'] for author in authors]: + authors.append({'login': commit['committer']['login'], + 'name': commit['committer']['login'], + 'url': commit['committer']['html_url'], + 'avatar': commit['committer']['avatar_url'] + }) else: # GitLab if commit['author_name']: From 77cb3ec0e888216ff7e9d3c31cc726b8b0ca18ef Mon Sep 17 00:00:00 2001 From: Johnson Sun Date: Fri, 12 Jul 2024 01:29:04 +0800 Subject: [PATCH 41/57] Include coauthors in GitHub repos References: * https://docs.github.com/en/graphql/reference/objects#commit * https://github.com/ojacques/mkdocs-git-committers-plugin-2/commit/806e422bdf59bbefd995f243be275b9661d78d2f --- mkdocs_git_committers_plugin_2/plugin.py | 56 ++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/mkdocs_git_committers_plugin_2/plugin.py b/mkdocs_git_committers_plugin_2/plugin.py index d96ed84..d304453 100644 --- a/mkdocs_git_committers_plugin_2/plugin.py +++ b/mkdocs_git_committers_plugin_2/plugin.py @@ -106,6 +106,7 @@ def get_contributors_to_file(self, path): if r.status_code == 200: # Get login, url and avatar for each author. Ensure no duplicates. res = r.json() + github_coauthors_exist = False for commit in res: if not self.config['gitlab_repository']: # GitHub @@ -121,6 +122,8 @@ def get_contributors_to_file(self, path): 'url': commit['committer']['html_url'], 'avatar': commit['committer']['avatar_url'] }) + if commit['commit'] and commit['commit']['message'] and '\nCo-authored-by:' in commit['commit']['message']: + github_coauthors_exist = True else: # GitLab if commit['author_name']: @@ -153,6 +156,59 @@ def get_contributors_to_file(self, path): break else: LOG.error("git-committers: " + str(r.status_code) + " " + r.reason) + if github_coauthors_exist: + github_coauthors_count = 0 + # Get co-authors info through the GraphQL API, which is not available in the REST API + if self.auth_header is None: + LOG.warning("git-committers: Co-authors exist in commit messages but will not be added, since no GitHub token is provided. Set it under 'token' mkdocs.yml config or MKDOCS_GIT_COMMITTERS_APIKEY environment variable.") + else: + LOG.info("git-committers: fetching contributors for " + path + " using GraphQL API") + # Query GraphQL API, and get a list of unique authors + url = self.githuburl + "/graphql" + query = { + "query": """ + { + repository(owner: "%s", name: "%s") { + object(expression: "%s") { + ... on Commit { + history(first: 100, path: "%s") { + nodes { + authors(first: 100) { + nodes { + user { + login + name + url + avatarUrl + } + } + } + } + } + } + } + } + } + """ % (self.config['repository'].split('/')[0], self.config['repository'].split('/')[1], self.branch, path) + } + r = requests.post(url=url, json=query, headers=self.auth_header) + res = r.json() + if r.status_code == 200: + if res.get('data'): + if res['data']['repository']['object']['history']['nodes']: + for history_node in res['data']['repository']['object']['history']['nodes']: + for author_node in history_node['authors']['nodes']: + # If user is not None (GitHub user was deleted) + if author_node['user']: + if author_node['user']['login'] not in [author['login'] for author in authors]: + authors.append({'login': author_node['user']['login'], + 'name': author_node['user']['name'], + 'url': author_node['user']['url'], + 'avatar': author_node['user']['avatarUrl']}) + github_coauthors_count += 1 + else: + LOG.warning("git-committers: Error from GitHub GraphQL call: " + res['errors'][0]['message']) + LOG.info(f"git-committers: added {github_coauthors_count} co-authors") return authors else: LOG.error("git-committers: error fetching contributors for " + path) From f09b170605d8ea1bcff5c1e049423b258e8ffc2a Mon Sep 17 00:00:00 2001 From: Johnson Sun Date: Fri, 12 Jul 2024 12:31:40 +0800 Subject: [PATCH 42/57] Update README on counting contributors --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index bdf5065..cae63ec 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,11 @@ unless you access private repositories. For private GitHub repositories, you only need to allow read-only access to `Contents` and `Metadata` on the target repository. This could be done by setting `Read-only` access of `Permissions > Repository permissions > Contents`. +## Counting Contributors + +* In GitHub repositories, the commit authors, [committers](https://stackoverflow.com/a/18754896), and [co-authors](https://docs.github.com/en/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/creating-a-commit-with-multiple-authors) are counted as contributors. However, the plugin requires a GitHub token to fetch the list of co-authors. If co-authors exist but no token is provided, the plugin will show a warning and will only display the commit authors and committers. +* In GitLab repositories, only the commit authors are counted as contributors. + ## Config - `enabled` - Disables plugin if set to `False` for e.g. local builds (default: `True`) From f37830adb41c1588b7703f21c2eb8d96ac4f5ec3 Mon Sep 17 00:00:00 2001 From: Johnson Sun Date: Thu, 18 Jul 2024 02:13:51 +0800 Subject: [PATCH 43/57] Support non-recursive submodules for GitHub repos --- README.md | 2 +- mkdocs_git_committers_plugin_2/plugin.py | 32 +++++++++++++++++++++--- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index bdf5065..a5d3057 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,7 @@ Note: the plugin configuration in `mkdocs.yml` still uses the original `git-comm ## Limitations - Getting the contributors relies on what is available on GitHub or GitLab. -- For now, Git submodule is not supported and will report no contributors. +- For now, non-recursive Git submodule is supported for GitHub, while GitLab submodules and recursive submodules will report no contributors. - GitLab users may not be properly identified. See [issue #50](https://github.com/ojacques/mkdocs-git-committers-plugin-2/issues/50) ## Usage diff --git a/mkdocs_git_committers_plugin_2/plugin.py b/mkdocs_git_committers_plugin_2/plugin.py index d96ed84..27dd8f1 100644 --- a/mkdocs_git_committers_plugin_2/plugin.py +++ b/mkdocs_git_committers_plugin_2/plugin.py @@ -12,6 +12,7 @@ import requests, json from requests.exceptions import HTTPError import time +import re from mkdocs_git_committers_plugin_2.exclude import exclude @@ -89,7 +90,7 @@ def on_config(self, config): return config # Get unique contributors for a given path - def get_contributors_to_file(self, path): + def get_contributors_to_file(self, path, submodule_repo=None): # We already got a 401 (unauthorized) or 403 (rate limit) error, so we don't try again if self.last_request_return_code == 403 or self.last_request_return_code == 401: return [] @@ -97,8 +98,10 @@ def get_contributors_to_file(self, path): # REST endpoint is in the form https://gitlab.com/api/v4/projects/[project ID]/repository/commits?path=[uri-encoded-path]&ref_name=[branch] url = self.gitlaburl + "/projects/" + str(self.config['gitlab_repository']) + "/repository/commits?path=" + requests.utils.quote(path) + "&ref_name=" + self.branch else: + # Check git submodule + repository = submodule_repo or self.config['repository'] # REST endpoint is in the form https://api.github.com/repos/[repository]/commits?path=[uri-encoded-path]&sha=[branch]&per_page=100 - url = self.githuburl + "/repos/" + self.config['repository'] + "/commits?path=" + requests.utils.quote(path) + "&sha=" + self.branch + "&per_page=100" + url = self.githuburl + "/repos/" + repository + "/commits?path=" + requests.utils.quote(path) + "&sha=" + self.branch + "&per_page=100" authors = [] LOG.info("git-committers: fetching contributors for " + path) r = requests.get(url=url, headers=self.auth_header) @@ -171,6 +174,25 @@ def list_contributors(self, path): # Use the last commit and get the date last_commit_date = time.strftime("%Y-%m-%d", time.gmtime(c.authored_date)) + # Check if the file is in a git submodule on GitHub + submodule_repo, path_in_submodule = None, None + if last_commit_date == "" and not self.config['gitlab_repository']: + for submodule in self.localrepo.submodules: + if submodule_repo: + break + if not path.startswith(submodule.path): + continue + match = re.match(r"https:\/\/github\.com\/([^\/]+\/[^\/.]+)", submodule.url) + if not match: + LOG.warning("git-committers: Submodule matched but will not be queried, since it isn't a GitHub repo.") + continue + path_in_submodule = path[len(submodule.path)+1:] + for c in Commit.iter_items(submodule.module(), submodule.module().head, path_in_submodule): + if not last_commit_date: + # Use the last commit and get the date + submodule_repo = match.group(1) + last_commit_date = time.strftime("%Y-%m-%d", time.gmtime(c.authored_date)) + # File not committed yet if last_commit_date == "": last_commit_date = datetime.now().strftime("%Y-%m-%d") @@ -184,7 +206,11 @@ def list_contributors(self, path): return self.cache_page_authors[path]['authors'], self.cache_page_authors[path]['last_commit_date'] authors=[] - authors = self.get_contributors_to_file(path) + if not submodule_repo: + authors = self.get_contributors_to_file(path) + else: + LOG.info("git-committers: fetching submodule info for " + path + " from repository " + submodule_repo + " with path " + path_in_submodule) + authors = self.get_contributors_to_file(path_in_submodule, submodule_repo=submodule_repo) self.cache_page_authors[path] = {'last_commit_date': last_commit_date, 'authors': authors} From a512b1a971368fe79d92505974daa6f2ca94464b Mon Sep 17 00:00:00 2001 From: Johnson Sun Date: Tue, 23 Jul 2024 23:22:03 +0800 Subject: [PATCH 44/57] Fix live reload infinite loop when serving locally --- mkdocs_git_committers_plugin_2/plugin.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/mkdocs_git_committers_plugin_2/plugin.py b/mkdocs_git_committers_plugin_2/plugin.py index d96ed84..342735c 100644 --- a/mkdocs_git_committers_plugin_2/plugin.py +++ b/mkdocs_git_committers_plugin_2/plugin.py @@ -45,6 +45,7 @@ def __init__(self): self.githuburl = "https://api.github.com" self.gitlaburl = "https://gitlab.com/api/v4" self.gitlabauthors_cache = dict() + self.should_save_cache = False def on_config(self, config): self.enabled = self.config['enabled'] @@ -185,8 +186,9 @@ def list_contributors(self, path): authors=[] authors = self.get_contributors_to_file(path) - - self.cache_page_authors[path] = {'last_commit_date': last_commit_date, 'authors': authors} + if path not in self.cache_page_authors or self.cache_page_authors[path] != {'last_commit_date': last_commit_date, 'authors': authors}: + self.should_save_cache = True + self.cache_page_authors[path] = {'last_commit_date': last_commit_date, 'authors': authors} return authors, last_commit_date @@ -214,6 +216,8 @@ def on_page_context(self, context, page, config, nav): return context def on_post_build(self, config): + if not self.should_save_cache: + return LOG.info("git-committers: saving page authors cache file") json_data = json.dumps({'cache_date': datetime.now().strftime("%Y-%m-%d"), 'page_authors': self.cache_page_authors}) os.makedirs(self.config['cache_dir'], exist_ok=True) From 935df1b98af763560c0719649d75a7fed896a59f Mon Sep 17 00:00:00 2001 From: Karel Bemelmans Date: Fri, 23 Aug 2024 11:29:16 +0200 Subject: [PATCH 45/57] Show correct default value of docs_path parameter Leaving out the trailing slash breaks the generation process without any error. Took me a while to figure this out. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bdf5065..769962c 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ For private GitHub repositories, you only need to allow read-only access to `Con gitlab.com (self-hosted). - `api_version` - For GitHub and GitLab self-hosted, the API version part that needs to be appended to the URL. Defaults to v4 for GitLab, and nothing for GitHub Enterprise (you may need `v3`). -- `docs_path` - the path to the documentation folder. Defaults to `docs`. +- `docs_path` - the path to the documentation folder. Defaults to `docs/`. - `cache_dir` - The path which holds the authors cache file to speed up documentation builds. Defaults to `.cache/plugin/git-committers/`. The cache file is named `page-authors.json`. From c587f980d27160fdbd32f503681ba7cdd86dc8ea Mon Sep 17 00:00:00 2001 From: rbourgeat Date: Wed, 4 Sep 2024 11:49:11 +0200 Subject: [PATCH 46/57] Fix build error when no avatar --- mkdocs_git_committers_plugin_2/plugin.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mkdocs_git_committers_plugin_2/plugin.py b/mkdocs_git_committers_plugin_2/plugin.py index d96ed84..e36ea2a 100644 --- a/mkdocs_git_committers_plugin_2/plugin.py +++ b/mkdocs_git_committers_plugin_2/plugin.py @@ -113,13 +113,13 @@ def get_contributors_to_file(self, path): authors.append({'login': commit['author']['login'], 'name': commit['author']['login'], 'url': commit['author']['html_url'], - 'avatar': commit['author']['avatar_url'] + 'avatar': commit['author']['avatar_url'] if user['avatar_url'] is not None else '' }) if commit['committer'] and commit['committer']['login'] and commit['committer']['login'] not in [author['login'] for author in authors]: authors.append({'login': commit['committer']['login'], 'name': commit['committer']['login'], 'url': commit['committer']['html_url'], - 'avatar': commit['committer']['avatar_url'] + 'avatar': commit['committer']['avatar_url'] if user['avatar_url'] is not None else '' }) else: # GitLab @@ -131,7 +131,7 @@ def get_contributors_to_file(self, path): authors.append({'login': self.gitlabauthors_cache[commit['author_name']]['username'], 'name': commit['author_name'], 'url': self.gitlabauthors_cache[commit['author_name']]['web_url'], - 'avatar': self.gitlabauthors_cache[commit['author_name']]['avatar_url'] + 'avatar': self.gitlabauthors_cache[commit['author_name']]['avatar_url'] if user['avatar_url'] is not None else '' }) else: # Fetch author from GitLab API @@ -148,7 +148,7 @@ def get_contributors_to_file(self, path): authors.append({'login': user['username'], 'name': user['name'], 'url': user['web_url'], - 'avatar': user['avatar_url'] + 'avatar': user['avatar_url'] if user['avatar_url'] is not None else '' }) break else: From da79a600aa7aa47f50c435769b992c444a2a69bf Mon Sep 17 00:00:00 2001 From: Robert Korzeniec Date: Tue, 1 Oct 2024 16:13:40 +0200 Subject: [PATCH 47/57] Fix KeyError: 'api_version' for self-hosted SCM This is a proposal for a fix in case this plugin is used for GitLab self-hosted or GitHub enterprise repositories. Currently this in case GitLab self-hosted or GitHub enterprise repo is being used with the newest version of this plugin, it would return the following error: ```python INFO - git-committers plugin ENABLED Traceback (most recent call last): File "/usr/local/bin/mkdocs", line 8, in sys.exit(cli()) File "/usr/local/lib/python3.9/site-packages/click/core.py", line 1130, in __call__ return self.main(*args, **kwargs) File "/usr/local/lib/python3.9/site-packages/click/core.py", line 1055, in main rv = self.invoke(ctx) File "/usr/local/lib/python3.9/site-packages/click/core.py", line 1657, in invoke return _process_result(sub_ctx.command.invoke(sub_ctx)) File "/usr/local/lib/python3.9/site-packages/click/core.py", line 1404, in invoke return ctx.invoke(self.callback, **ctx.params) File "/usr/local/lib/python3.9/site-packages/click/core.py", line 760, in invoke return __callback(*args, **kwargs) File "/usr/local/lib/python3.9/site-packages/mkdocs/__main__.py", line 288, in build_command build.build(cfg, dirty=not clean) File "/usr/local/lib/python3.9/site-packages/mkdocs/commands/build.py", line 265, in build config = config.plugins.on_config(config) File "/usr/local/lib/python3.9/site-packages/mkdocs/plugins.py", line 587, in on_config return self.run_event('config', config) File "/usr/local/lib/python3.9/site-packages/mkdocs/plugins.py", line 566, in run_event result = method(item, **kwargs) File "/usr/local/lib/python3.9/site-packages/mkdocs_git_committers_plugin_2/plugin.py", line 67, in on_config if not self.config['api_version']: File "/usr/local/lib/python3.9/collections/__init__.py", line 1058, in __getitem__ raise KeyError(key) KeyError: 'api_version' ``` This change proposes to change the dict key access to a safer version. --- mkdocs_git_committers_plugin_2/plugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mkdocs_git_committers_plugin_2/plugin.py b/mkdocs_git_committers_plugin_2/plugin.py index d96ed84..0afcf5f 100644 --- a/mkdocs_git_committers_plugin_2/plugin.py +++ b/mkdocs_git_committers_plugin_2/plugin.py @@ -60,12 +60,12 @@ def on_config(self, config): LOG.error("git-committers plugin: repository not specified") return config if self.config['enterprise_hostname'] and self.config['enterprise_hostname'] != '': - if not self.config['api_version']: + if not self.config.get('api_version'): self.githuburl = "https://" + self.config['enterprise_hostname'] + "/api" else: self.githuburl = "https://" + self.config['enterprise_hostname'] + "/api/" + self.config['api_version'] if self.config['gitlab_hostname'] and self.config['gitlab_hostname'] != '': - if not self.config['api_version']: + if not self.config.get('api_version'): self.gitlaburl = "https://" + self.config['gitlab_hostname'] + "/api/v4" else: self.gitlaburl = "https://" + self.config['gitlab_hostname'] + "/api/" + self.config['api_version'] From 12f29c408ed1daab1d151866d90ff7cc2f35fa63 Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Thu, 3 Oct 2024 21:43:47 +0200 Subject: [PATCH 48/57] Update contributors --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index a217f23..99ecf52 100644 --- a/README.md +++ b/README.md @@ -241,3 +241,8 @@ Thank you to the following contributors: - thor - n2N8Z - barreeeiroo +- j3soon +- vrenjith +- rkorzeniec +- karelbemelmans +- andrew-rowson-lseg From 33d54e6c883dc25fd5290d9c663a2f409f7eb6b8 Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Thu, 3 Oct 2024 21:49:45 +0200 Subject: [PATCH 49/57] Prepare 2.4.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 5e65d5f..96b8a4e 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setup( name="mkdocs-git-committers-plugin-2", - version="2.3.0", + version="2.4.0", description="An MkDocs plugin to create a list of contributors on the page. The git-committers plugin will seed the template context with a list of GitHub or GitLab committers and other useful GIT info such as last modified date", long_description=long_description, long_description_content_type="text/markdown", From 3f71d231eca0769c8530dca39e893ad80fd07edb Mon Sep 17 00:00:00 2001 From: Onuralp SEZER Date: Fri, 4 Oct 2024 02:04:10 +0300 Subject: [PATCH 50/57] fix: UnboundLocalError user variable fix for github and gitlab Signed-off-by: Onuralp SEZER --- mkdocs_git_committers_plugin_2/plugin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mkdocs_git_committers_plugin_2/plugin.py b/mkdocs_git_committers_plugin_2/plugin.py index 9a8792d..2e86a4d 100644 --- a/mkdocs_git_committers_plugin_2/plugin.py +++ b/mkdocs_git_committers_plugin_2/plugin.py @@ -118,13 +118,13 @@ def get_contributors_to_file(self, path, submodule_repo=None): authors.append({'login': commit['author']['login'], 'name': commit['author']['login'], 'url': commit['author']['html_url'], - 'avatar': commit['author']['avatar_url'] if user['avatar_url'] is not None else '' + 'avatar': commit['author']['avatar_url'] if commit['author']['avatar_url'] is not None else '' }) if commit['committer'] and commit['committer']['login'] and commit['committer']['login'] not in [author['login'] for author in authors]: authors.append({'login': commit['committer']['login'], 'name': commit['committer']['login'], 'url': commit['committer']['html_url'], - 'avatar': commit['committer']['avatar_url'] if user['avatar_url'] is not None else '' + 'avatar': commit['committer']['avatar_url'] if commit['author']['avatar_url'] is not None else '' }) if commit['commit'] and commit['commit']['message'] and '\nCo-authored-by:' in commit['commit']['message']: github_coauthors_exist = True @@ -138,7 +138,7 @@ def get_contributors_to_file(self, path, submodule_repo=None): authors.append({'login': self.gitlabauthors_cache[commit['author_name']]['username'], 'name': commit['author_name'], 'url': self.gitlabauthors_cache[commit['author_name']]['web_url'], - 'avatar': self.gitlabauthors_cache[commit['author_name']]['avatar_url'] if user['avatar_url'] is not None else '' + 'avatar': self.gitlabauthors_cache[commit['author_name']]['avatar_url'] if self.gitlabauthors_cache[commit['author_name']]['avatar_url'] is not None else '' }) else: # Fetch author from GitLab API From 13a4a2f5fd63596e3fbf47bc631254cc564890e8 Mon Sep 17 00:00:00 2001 From: Onuralp SEZER Date: Fri, 4 Oct 2024 14:16:26 +0300 Subject: [PATCH 51/57] Update mkdocs_git_committers_plugin_2/plugin.py Co-authored-by: Galen Rice --- mkdocs_git_committers_plugin_2/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs_git_committers_plugin_2/plugin.py b/mkdocs_git_committers_plugin_2/plugin.py index 2e86a4d..efbbf8c 100644 --- a/mkdocs_git_committers_plugin_2/plugin.py +++ b/mkdocs_git_committers_plugin_2/plugin.py @@ -124,7 +124,7 @@ def get_contributors_to_file(self, path, submodule_repo=None): authors.append({'login': commit['committer']['login'], 'name': commit['committer']['login'], 'url': commit['committer']['html_url'], - 'avatar': commit['committer']['avatar_url'] if commit['author']['avatar_url'] is not None else '' + 'avatar': commit['committer']['avatar_url'] if commit['committer']['avatar_url'] is not None else '' }) if commit['commit'] and commit['commit']['message'] and '\nCo-authored-by:' in commit['commit']['message']: github_coauthors_exist = True From ae1e2da31af37f42181a3ed41b27f939366d3ca1 Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Fri, 4 Oct 2024 19:14:35 +0200 Subject: [PATCH 52/57] Preparing 2.4.1 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 96b8a4e..a7b1858 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setup( name="mkdocs-git-committers-plugin-2", - version="2.4.0", + version="2.4.1", description="An MkDocs plugin to create a list of contributors on the page. The git-committers plugin will seed the template context with a list of GitHub or GitLab committers and other useful GIT info such as last modified date", long_description=long_description, long_description_content_type="text/markdown", From 6a95da320b17ff8d77b708535620fd7b502487b6 Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Thu, 24 Oct 2024 17:05:43 +0200 Subject: [PATCH 53/57] docs: fix token for GitLab --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 99ecf52..7aea84e 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ For a repository hosted on GitLab: plugins: - git-committers: gitlab_repository: 12345678 - token: !ENV ["GH_TOKEN"] + token: !ENV ["CI_JOB_TOKEN"] ``` For a repository hosted on GitLab, you need to provide a token so that the From f4e7167c0b6680147628daafb74226276e748cb2 Mon Sep 17 00:00:00 2001 From: Xiaokang2022 <2951256653@qq.com> Date: Sat, 30 Nov 2024 18:35:33 +0800 Subject: [PATCH 54/57] feat: Add a new option `exclude_committers` to ignore some committers --- mkdocs_git_committers_plugin_2/plugin.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/mkdocs_git_committers_plugin_2/plugin.py b/mkdocs_git_committers_plugin_2/plugin.py index efbbf8c..9ed30cf 100644 --- a/mkdocs_git_committers_plugin_2/plugin.py +++ b/mkdocs_git_committers_plugin_2/plugin.py @@ -31,6 +31,7 @@ class GitCommittersPlugin(BasePlugin): ('enabled', config_options.Type(bool, default=True)), ('cache_dir', config_options.Type(str, default='.cache/plugin/git-committers')), ("exclude", config_options.Type(list, default=[])), + ("exclude_committers", config_options.Type(list, default=[])), ('token', config_options.Type(str, default='')), ) @@ -41,6 +42,7 @@ def __init__(self): self.authors = dict() self.cache_page_authors = dict() self.exclude = list() + self.exclude_committers = list() self.cache_date = '' self.last_request_return_code = 0 self.githuburl = "https://api.github.com" @@ -88,6 +90,7 @@ def on_config(self, config): self.localrepo = Repo(".", search_parent_directories=True) self.branch = self.config['branch'] self.excluded_pages = self.config['exclude'] + self.exclude_committers = self.config['exclude_committers'] return config # Get unique contributors for a given path @@ -268,7 +271,13 @@ def list_contributors(self, path): else: LOG.info("git-committers: fetching submodule info for " + path + " from repository " + submodule_repo + " with path " + path_in_submodule) authors = self.get_contributors_to_file(path_in_submodule, submodule_repo=submodule_repo) - + + for exclude_committer in set(self.exclude_committers): + for author in tuple(authors): + if author["login"] == exclude_committer: + authors.remove(author) + break + if path not in self.cache_page_authors or self.cache_page_authors[path] != {'last_commit_date': last_commit_date, 'authors': authors}: self.should_save_cache = True self.cache_page_authors[path] = {'last_commit_date': last_commit_date, 'authors': authors} From 5809aa5f0dcfe94fb913c8b8a869b49df884869d Mon Sep 17 00:00:00 2001 From: Xiaokang2022 <2951256653@qq.com> Date: Sat, 30 Nov 2024 18:53:43 +0800 Subject: [PATCH 55/57] chore: Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7aea84e..7a8082d 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,7 @@ For private GitHub repositories, you only need to allow read-only access to `Con - all_files_inside_folder/* - folder_and_subfolders/** ``` +- `exclude_committers` - Specify a list of usernames to exclude certain committers. Default is empty. ## History From b9705daa7550e7c29e7d5c9b9a0c52de807fc841 Mon Sep 17 00:00:00 2001 From: Olivier Jacques Date: Thu, 30 Jan 2025 08:29:09 +0100 Subject: [PATCH 56/57] Prepare for 2.5.0 release --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index a7b1858..a55f9cc 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setup( name="mkdocs-git-committers-plugin-2", - version="2.4.1", + version="2.5.0", description="An MkDocs plugin to create a list of contributors on the page. The git-committers plugin will seed the template context with a list of GitHub or GitLab committers and other useful GIT info such as last modified date", long_description=long_description, long_description_content_type="text/markdown", From cda906362b2ff57c63eee16d51f91a77d835d73b Mon Sep 17 00:00:00 2001 From: Yuya Asano <64895419+sukeya@users.noreply.github.com> Date: Tue, 11 Feb 2025 09:49:59 +0000 Subject: [PATCH 57/57] Support JWT for GitHub authentication. Signed-off-by: Yuya Asano <64895419+sukeya@users.noreply.github.com> --- mkdocs_git_committers_plugin_2/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs_git_committers_plugin_2/plugin.py b/mkdocs_git_committers_plugin_2/plugin.py index 9ed30cf..d77ede3 100644 --- a/mkdocs_git_committers_plugin_2/plugin.py +++ b/mkdocs_git_committers_plugin_2/plugin.py @@ -80,7 +80,7 @@ def on_config(self, config): if self.config['gitlab_repository']: self.auth_header = {'PRIVATE-TOKEN': self.config['token'] } else: - self.auth_header = {'Authorization': 'token ' + self.config['token'] } + self.auth_header = {'Authorization': 'Bearer ' + self.config['token'] } else: self.auth_header = None if self.config['gitlab_repository']: