mkdocs-document-dates 3.0.0__tar.gz → 3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. mkdocs_document_dates-3.1/PKG-INFO +162 -0
  2. mkdocs_document_dates-3.1/README.md +139 -0
  3. mkdocs_document_dates-3.1/mkdocs_document_dates/cache_manager.py +215 -0
  4. mkdocs_document_dates-3.1/mkdocs_document_dates/hooks/pre-commit +16 -0
  5. mkdocs_document_dates-3.1/mkdocs_document_dates/hooks_installer.py +128 -0
  6. mkdocs_document_dates-3.1/mkdocs_document_dates/plugin.py +520 -0
  7. mkdocs_document_dates-3.1/mkdocs_document_dates/static/config/user.config.css +73 -0
  8. mkdocs_document_dates-3.1/mkdocs_document_dates/static/config/user.config.js +45 -0
  9. mkdocs_document_dates-3.0.0/mkdocs_document_dates/static/document-dates.config.css → mkdocs_document_dates-3.1/mkdocs_document_dates/static/core/core.css +29 -7
  10. mkdocs_document_dates-3.1/mkdocs_document_dates/static/core/core.js +165 -0
  11. mkdocs_document_dates-3.1/mkdocs_document_dates/static/languages/ar.json +20 -0
  12. mkdocs_document_dates-3.1/mkdocs_document_dates/static/languages/de.json +20 -0
  13. mkdocs_document_dates-3.1/mkdocs_document_dates/static/languages/en.json +20 -0
  14. mkdocs_document_dates-3.1/mkdocs_document_dates/static/languages/es.json +20 -0
  15. mkdocs_document_dates-3.1/mkdocs_document_dates/static/languages/fr.json +20 -0
  16. mkdocs_document_dates-3.1/mkdocs_document_dates/static/languages/ja.json +20 -0
  17. mkdocs_document_dates-3.1/mkdocs_document_dates/static/languages/ko.json +20 -0
  18. mkdocs_document_dates-3.1/mkdocs_document_dates/static/languages/ru.json +20 -0
  19. mkdocs_document_dates-3.1/mkdocs_document_dates/static/languages/zh.json +20 -0
  20. mkdocs_document_dates-3.1/mkdocs_document_dates/static/languages/zh_tw.json +20 -0
  21. mkdocs_document_dates-3.1/mkdocs_document_dates.egg-info/PKG-INFO +162 -0
  22. {mkdocs_document_dates-3.0.0 → mkdocs_document_dates-3.1}/mkdocs_document_dates.egg-info/SOURCES.txt +15 -13
  23. {mkdocs_document_dates-3.0.0 → mkdocs_document_dates-3.1}/setup.py +7 -6
  24. mkdocs_document_dates-3.0.0/PKG-INFO +0 -124
  25. mkdocs_document_dates-3.0.0/README.md +0 -101
  26. mkdocs_document_dates-3.0.0/mkdocs_document_dates/hooks/pre-commit +0 -88
  27. mkdocs_document_dates-3.0.0/mkdocs_document_dates/hooks_installer.py +0 -73
  28. mkdocs_document_dates-3.0.0/mkdocs_document_dates/lang/__init__.py +0 -22
  29. mkdocs_document_dates-3.0.0/mkdocs_document_dates/lang/ar.py +0 -19
  30. mkdocs_document_dates-3.0.0/mkdocs_document_dates/lang/de.py +0 -19
  31. mkdocs_document_dates-3.0.0/mkdocs_document_dates/lang/en.py +0 -19
  32. mkdocs_document_dates-3.0.0/mkdocs_document_dates/lang/es.py +0 -19
  33. mkdocs_document_dates-3.0.0/mkdocs_document_dates/lang/fr.py +0 -19
  34. mkdocs_document_dates-3.0.0/mkdocs_document_dates/lang/ja.py +0 -19
  35. mkdocs_document_dates-3.0.0/mkdocs_document_dates/lang/ko.py +0 -19
  36. mkdocs_document_dates-3.0.0/mkdocs_document_dates/lang/ru.py +0 -19
  37. mkdocs_document_dates-3.0.0/mkdocs_document_dates/lang/zh.py +0 -19
  38. mkdocs_document_dates-3.0.0/mkdocs_document_dates/lang/zh_tw.py +0 -19
  39. mkdocs_document_dates-3.0.0/mkdocs_document_dates/plugin.py +0 -277
  40. mkdocs_document_dates-3.0.0/mkdocs_document_dates/static/document-dates.config.js +0 -64
  41. mkdocs_document_dates-3.0.0/mkdocs_document_dates.egg-info/PKG-INFO +0 -124
  42. {mkdocs_document_dates-3.0.0 → mkdocs_document_dates-3.1}/LICENSE +0 -0
  43. {mkdocs_document_dates-3.0.0 → mkdocs_document_dates-3.1}/mkdocs_document_dates/__init__.py +0 -0
  44. {mkdocs_document_dates-3.0.0 → mkdocs_document_dates-3.1}/mkdocs_document_dates/static/tippy/backdrop.css +0 -0
  45. {mkdocs_document_dates-3.0.0 → mkdocs_document_dates-3.1}/mkdocs_document_dates/static/tippy/light.css +0 -0
  46. {mkdocs_document_dates-3.0.0 → mkdocs_document_dates-3.1}/mkdocs_document_dates/static/tippy/material.css +0 -0
  47. {mkdocs_document_dates-3.0.0 → mkdocs_document_dates-3.1}/mkdocs_document_dates/static/tippy/popper.min.js +0 -0
  48. {mkdocs_document_dates-3.0.0 → mkdocs_document_dates-3.1}/mkdocs_document_dates/static/tippy/scale.css +0 -0
  49. {mkdocs_document_dates-3.0.0 → mkdocs_document_dates-3.1}/mkdocs_document_dates/static/tippy/shift-away.css +0 -0
  50. {mkdocs_document_dates-3.0.0 → mkdocs_document_dates-3.1}/mkdocs_document_dates/static/tippy/tippy.css +0 -0
  51. {mkdocs_document_dates-3.0.0 → mkdocs_document_dates-3.1}/mkdocs_document_dates/static/tippy/tippy.umd.min.js +0 -0
  52. {mkdocs_document_dates-3.0.0 → mkdocs_document_dates-3.1}/mkdocs_document_dates.egg-info/dependency_links.txt +0 -0
  53. {mkdocs_document_dates-3.0.0 → mkdocs_document_dates-3.1}/mkdocs_document_dates.egg-info/entry_points.txt +0 -0
  54. {mkdocs_document_dates-3.0.0 → mkdocs_document_dates-3.1}/mkdocs_document_dates.egg-info/requires.txt +0 -0
  55. {mkdocs_document_dates-3.0.0 → mkdocs_document_dates-3.1}/mkdocs_document_dates.egg-info/top_level.txt +0 -0
  56. {mkdocs_document_dates-3.0.0 → mkdocs_document_dates-3.1}/setup.cfg +0 -0
@@ -0,0 +1,162 @@
1
+ Metadata-Version: 2.4
2
+ Name: mkdocs-document-dates
3
+ Version: 3.1
4
+ Summary: An easy-to-use, lightweight MkDocs plugin for displaying the exact creation time, last modification time and author info of markdown documents.
5
+ Home-page: https://github.com/jaywhj/mkdocs-document-dates
6
+ Author: Aaron Wang
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.7
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: mkdocs>=1.0.0
14
+ Dynamic: author
15
+ Dynamic: classifier
16
+ Dynamic: description
17
+ Dynamic: description-content-type
18
+ Dynamic: home-page
19
+ Dynamic: license-file
20
+ Dynamic: requires-dist
21
+ Dynamic: requires-python
22
+ Dynamic: summary
23
+
24
+ # mkdocs-document-dates
25
+
26
+ English | [简体中文](README_zh.md)
27
+
28
+
29
+
30
+ An easy-to-use, lightweight MkDocs plugin for displaying the <mark>exact</mark> creation time, last modification time and author info of markdown documents.
31
+
32
+ ## Features
33
+
34
+ - Work in any environment, for Git-less environments, CI/CD environments (e.g. GitHub Actions), one-person collaboration, multi-person collaboration, etc
35
+ - Support for manually specifying time and author info in `Front Matter`
36
+ - Support for multiple time formats (date, datetime, timeago)
37
+ - Support for document exclusion mode
38
+ - Flexible display position (top or bottom)
39
+ - Elegant styling (fully customizable)
40
+ - Supports Tooltip Hover Tips
41
+ - Intelligent repositioning to always float optimally in view
42
+ - Supports automatic theme switching following Material's light/dark color scheme
43
+ - Support for customizing themes, styles, animations
44
+ - Compatible with mouse, keyboard and **touch** (mobile) to trigger hover
45
+ - Multi-language support, cross-platform support (Windows, macOS, Linux)
46
+
47
+ ## Showcases
48
+
49
+ ![render](render.gif)
50
+
51
+ ## Installation
52
+
53
+ ```bash
54
+ pip install mkdocs-document-dates
55
+ ```
56
+
57
+ ## Configuration
58
+
59
+ Just add the plugin to your `mkdocs.yml`:
60
+
61
+ ```yaml
62
+ plugins:
63
+ - document-dates
64
+ ```
65
+
66
+ Or, personalize the configuration:
67
+
68
+ ```yaml
69
+ plugins:
70
+ - document-dates:
71
+ position: top # Display position: top (after title) bottom (end of document), default: bottom
72
+ type: date # Date type: date datetime timeago, default: date
73
+ locale: en # Localization: zh zh_tw en es fr de ar ja ko ru, default: en
74
+ date_format: '%Y-%m-%d' # Date format, Supports all Python datetime format strings, e.g., %Y-%m-%d, %b %d, %Y, etc
75
+ time_format: '%H:%M:%S' # Time format (valid only if type=datetime)
76
+ exclude: # List of excluded files
77
+ - temp.md # Exclude specific file
78
+ - private/* # Exclude all files in private directory, including subdirectories
79
+ - drafts/*.md # Exclude all markdown files in the current directory drafts, but not subdirectories
80
+
81
+ show_author: true # Whether to display author: true false, default: true
82
+
83
+ ```
84
+
85
+ ## Specify time manually
86
+
87
+ The plugin will automatically get the exact time of the document, will automatically cache the creation time, but of course, you can also specify it manually in `Front Matter`
88
+
89
+ Priority: `Front Matter` > `Cache Files` > `File System Timestamps`
90
+
91
+ ```yaml
92
+ ---
93
+ created: 2023-01-01
94
+ modified: 2025-02-23
95
+ ---
96
+
97
+ # Document Title
98
+ ```
99
+
100
+ - `created` can be replaced with: `created, date, creation`
101
+ - `modified` can be replaced with: `modified, updated, last_modified, last_updated`
102
+
103
+ ## Specify author manually
104
+
105
+ The plugin will automatically get the author of the document, will parse the email and make a link, also you can specify it manually in `Front Matter`
106
+
107
+ Priority: `Front Matter` > `Git Author Info` > `PC Username`
108
+
109
+ ```yaml
110
+ ---
111
+ author: any-name
112
+ email: e-name@gmail.com
113
+ ---
114
+
115
+ # Document Title
116
+ ```
117
+
118
+ - `author` can be replaced with: `author, name`
119
+ - `email` can be replaced with: `email, mail`
120
+
121
+ ## Customization
122
+
123
+ The plugin supports deep customization, such as icon style, font style, theme color, animation type, dividing line, etc. All of it can be customized by modifying the code in the corresponding file (I've already written the code and comments, you just need to turn on the switch and change the value, it's super easy):
124
+
125
+ - Style & Theme: `docs/assets/document_dates/user.config.css`
126
+ - Properties & Animations: `docs/assets/document_dates/user.config.js`
127
+ - Localized languages: `docs/assets/document_dates/languages/` , refer to the template file `en.json` for any additions or modifications
128
+
129
+ Note: Due to the redesign of the configuration file update mechanism, the configuration file starting with document-dates.config in the previous version has been canceled and replaced by user.config
130
+
131
+ ## Other Tips
132
+
133
+ - In order to get the exact creation time, a separate cache file is used to store the creation time of the file, located in the doc folder (hidden by default), please don't delete it:
134
+ - `docs/.dates_cache.jsonl`, cache file (.json without the l is the old cache file and will be automatically migrated to .jsonl after any git commit)
135
+ - `docs/.gitattributes`, configuration file (merge mechanism for cache file in case of multi-person collaboration)
136
+ - The Git Hooks mechanism is used to automatically trigger the storing of the cache (every time executes a git commit), and the cached file is automatically committed along with it
137
+ - The installation of Git Hooks is automatically triggered when the plugin is installed
138
+ - The above actions are fully automated without any manual intervention, applicable to any environment, and have been tested and validated in Git-less environments, CI/CD environments (e.g., GitHub Actions), one-person collaborations, multi-person collaborations, etc
139
+
140
+ ## Development Stories (Optional)
141
+
142
+ A dispensable, insignificant little plug-in, friends who have nothing to do can take a look \^\_\^
143
+
144
+ - **Origin**:
145
+ - Because [mkdocs-git-revision-date-localized-plugin](https://github.com/timvink/mkdocs-git-revision-date-localized-plugin), a great project. When I used it at the end of 2024, I found that I couldn't use it locally because my mkdocs documentation was not included in git management, then I don't understand why not read the file system time, but to use the git time, and raised an issue to the author, but didn't get a reply for about a week (the author had a reply later, nice guy, I guess he was busy at the time), and then I thought, there is nothing to do during the Chinese New Year, and now AI is so hot, why not with the help of the AI try it out for myself, it was born, born in February 2025
146
+ - **Iteration**:
147
+ - After development, I understood why not use filesystem time, because files will be rebuilt when they go through git checkout or clone, resulting in the loss of original timestamp information, and there are many solutions:
148
+ - Method 1: Use the last git commit time as the last update time, and the first git commit time as the creation (there is a margin of error, but it's acceptable), mkdocs-git-revision-date-localized-plugin does this
149
+ - Method 2: You can cache the original time, and then read the cache subsequently. The cache can be in Front Matter of the source document or in a separate file, I chose the latter. Existing in the Front Matter is very reasonable and simple, but this will modify the source content of the document, although it doesn't have any impact on the body, but I still want to ensure the originality of the data!
150
+ - **Difficulty**:
151
+ 1. When to read and store raw time? This is just a plugin for mkdocs, with very limited access and permissions, mkdocs provides only build and serve, so in case a user commits directly without executing build or serve (e.g., when using a CI/CD build system), then you won't be able to retrieve the time information of the file, not to mention caching it!
152
+ - Let's take a straight shot: the Git Hooks mechanism was found, prompted by the AI, which can trigger a custom script when a specific action occurs, such as every time commit is performed
153
+ 2. How can I ensure that a single cache file does not conflict when collaborating with multi-person?
154
+ - My solution: use JSONL (JSON Lines) instead of JSON, and with the merge strategy 'merge=union'
155
+ - **Improve**:
156
+ - Since it has been re-developed, it will be designed in the direction of **excellent products**, and the pursuit of the ultimate **ease of use, simplicity and personalization**
157
+ - Ease of use: don't let users do things manually if you can, e.g., auto-install Git Hooks, auto-cache, auto-commit, provide customized templates, etc
158
+ - Simplicity: no unnecessary configurations, the less the simpler, e.g. git account information, repo information, etc., are not required
159
+ - Personalization: almost everything can be customized, whether it's icons, styles, themes, or features, it's all fully customizable
160
+ - In addition, it has good compatibility and extensibility, and works well in WIN7, mobile devices, old Safari, etc
161
+ - **The Last Secret**:
162
+ - I'm not a programmer, my main job is marketing, can you believe it? (Feel free to leave a comment)
@@ -0,0 +1,139 @@
1
+ # mkdocs-document-dates
2
+
3
+ English | [简体中文](README_zh.md)
4
+
5
+
6
+
7
+ An easy-to-use, lightweight MkDocs plugin for displaying the <mark>exact</mark> creation time, last modification time and author info of markdown documents.
8
+
9
+ ## Features
10
+
11
+ - Work in any environment, for Git-less environments, CI/CD environments (e.g. GitHub Actions), one-person collaboration, multi-person collaboration, etc
12
+ - Support for manually specifying time and author info in `Front Matter`
13
+ - Support for multiple time formats (date, datetime, timeago)
14
+ - Support for document exclusion mode
15
+ - Flexible display position (top or bottom)
16
+ - Elegant styling (fully customizable)
17
+ - Supports Tooltip Hover Tips
18
+ - Intelligent repositioning to always float optimally in view
19
+ - Supports automatic theme switching following Material's light/dark color scheme
20
+ - Support for customizing themes, styles, animations
21
+ - Compatible with mouse, keyboard and **touch** (mobile) to trigger hover
22
+ - Multi-language support, cross-platform support (Windows, macOS, Linux)
23
+
24
+ ## Showcases
25
+
26
+ ![render](render.gif)
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ pip install mkdocs-document-dates
32
+ ```
33
+
34
+ ## Configuration
35
+
36
+ Just add the plugin to your `mkdocs.yml`:
37
+
38
+ ```yaml
39
+ plugins:
40
+ - document-dates
41
+ ```
42
+
43
+ Or, personalize the configuration:
44
+
45
+ ```yaml
46
+ plugins:
47
+ - document-dates:
48
+ position: top # Display position: top (after title) bottom (end of document), default: bottom
49
+ type: date # Date type: date datetime timeago, default: date
50
+ locale: en # Localization: zh zh_tw en es fr de ar ja ko ru, default: en
51
+ date_format: '%Y-%m-%d' # Date format, Supports all Python datetime format strings, e.g., %Y-%m-%d, %b %d, %Y, etc
52
+ time_format: '%H:%M:%S' # Time format (valid only if type=datetime)
53
+ exclude: # List of excluded files
54
+ - temp.md # Exclude specific file
55
+ - private/* # Exclude all files in private directory, including subdirectories
56
+ - drafts/*.md # Exclude all markdown files in the current directory drafts, but not subdirectories
57
+
58
+ show_author: true # Whether to display author: true false, default: true
59
+
60
+ ```
61
+
62
+ ## Specify time manually
63
+
64
+ The plugin will automatically get the exact time of the document, will automatically cache the creation time, but of course, you can also specify it manually in `Front Matter`
65
+
66
+ Priority: `Front Matter` > `Cache Files` > `File System Timestamps`
67
+
68
+ ```yaml
69
+ ---
70
+ created: 2023-01-01
71
+ modified: 2025-02-23
72
+ ---
73
+
74
+ # Document Title
75
+ ```
76
+
77
+ - `created` can be replaced with: `created, date, creation`
78
+ - `modified` can be replaced with: `modified, updated, last_modified, last_updated`
79
+
80
+ ## Specify author manually
81
+
82
+ The plugin will automatically get the author of the document, will parse the email and make a link, also you can specify it manually in `Front Matter`
83
+
84
+ Priority: `Front Matter` > `Git Author Info` > `PC Username`
85
+
86
+ ```yaml
87
+ ---
88
+ author: any-name
89
+ email: e-name@gmail.com
90
+ ---
91
+
92
+ # Document Title
93
+ ```
94
+
95
+ - `author` can be replaced with: `author, name`
96
+ - `email` can be replaced with: `email, mail`
97
+
98
+ ## Customization
99
+
100
+ The plugin supports deep customization, such as icon style, font style, theme color, animation type, dividing line, etc. All of it can be customized by modifying the code in the corresponding file (I've already written the code and comments, you just need to turn on the switch and change the value, it's super easy):
101
+
102
+ - Style & Theme: `docs/assets/document_dates/user.config.css`
103
+ - Properties & Animations: `docs/assets/document_dates/user.config.js`
104
+ - Localized languages: `docs/assets/document_dates/languages/` , refer to the template file `en.json` for any additions or modifications
105
+
106
+ Note: Due to the redesign of the configuration file update mechanism, the configuration file starting with document-dates.config in the previous version has been canceled and replaced by user.config
107
+
108
+ ## Other Tips
109
+
110
+ - In order to get the exact creation time, a separate cache file is used to store the creation time of the file, located in the doc folder (hidden by default), please don't delete it:
111
+ - `docs/.dates_cache.jsonl`, cache file (.json without the l is the old cache file and will be automatically migrated to .jsonl after any git commit)
112
+ - `docs/.gitattributes`, configuration file (merge mechanism for cache file in case of multi-person collaboration)
113
+ - The Git Hooks mechanism is used to automatically trigger the storing of the cache (every time executes a git commit), and the cached file is automatically committed along with it
114
+ - The installation of Git Hooks is automatically triggered when the plugin is installed
115
+ - The above actions are fully automated without any manual intervention, applicable to any environment, and have been tested and validated in Git-less environments, CI/CD environments (e.g., GitHub Actions), one-person collaborations, multi-person collaborations, etc
116
+
117
+ ## Development Stories (Optional)
118
+
119
+ A dispensable, insignificant little plug-in, friends who have nothing to do can take a look \^\_\^
120
+
121
+ - **Origin**:
122
+ - Because [mkdocs-git-revision-date-localized-plugin](https://github.com/timvink/mkdocs-git-revision-date-localized-plugin), a great project. When I used it at the end of 2024, I found that I couldn't use it locally because my mkdocs documentation was not included in git management, then I don't understand why not read the file system time, but to use the git time, and raised an issue to the author, but didn't get a reply for about a week (the author had a reply later, nice guy, I guess he was busy at the time), and then I thought, there is nothing to do during the Chinese New Year, and now AI is so hot, why not with the help of the AI try it out for myself, it was born, born in February 2025
123
+ - **Iteration**:
124
+ - After development, I understood why not use filesystem time, because files will be rebuilt when they go through git checkout or clone, resulting in the loss of original timestamp information, and there are many solutions:
125
+ - Method 1: Use the last git commit time as the last update time, and the first git commit time as the creation (there is a margin of error, but it's acceptable), mkdocs-git-revision-date-localized-plugin does this
126
+ - Method 2: You can cache the original time, and then read the cache subsequently. The cache can be in Front Matter of the source document or in a separate file, I chose the latter. Existing in the Front Matter is very reasonable and simple, but this will modify the source content of the document, although it doesn't have any impact on the body, but I still want to ensure the originality of the data!
127
+ - **Difficulty**:
128
+ 1. When to read and store raw time? This is just a plugin for mkdocs, with very limited access and permissions, mkdocs provides only build and serve, so in case a user commits directly without executing build or serve (e.g., when using a CI/CD build system), then you won't be able to retrieve the time information of the file, not to mention caching it!
129
+ - Let's take a straight shot: the Git Hooks mechanism was found, prompted by the AI, which can trigger a custom script when a specific action occurs, such as every time commit is performed
130
+ 2. How can I ensure that a single cache file does not conflict when collaborating with multi-person?
131
+ - My solution: use JSONL (JSON Lines) instead of JSON, and with the merge strategy 'merge=union'
132
+ - **Improve**:
133
+ - Since it has been re-developed, it will be designed in the direction of **excellent products**, and the pursuit of the ultimate **ease of use, simplicity and personalization**
134
+ - Ease of use: don't let users do things manually if you can, e.g., auto-install Git Hooks, auto-cache, auto-commit, provide customized templates, etc
135
+ - Simplicity: no unnecessary configurations, the less the simpler, e.g. git account information, repo information, etc., are not required
136
+ - Personalization: almost everything can be customized, whether it's icons, styles, themes, or features, it's all fully customizable
137
+ - In addition, it has good compatibility and extensibility, and works well in WIN7, mobile devices, old Safari, etc
138
+ - **The Last Secret**:
139
+ - I'm not a programmer, my main job is marketing, can you believe it? (Feel free to leave a comment)
@@ -0,0 +1,215 @@
1
+ import os
2
+ import sys
3
+ import json
4
+ import logging
5
+ import platform
6
+ import subprocess
7
+ from datetime import datetime
8
+ from pathlib import Path
9
+
10
+ # 配置日志等级 (INFO WARNING ERROR)
11
+ logging.basicConfig(
12
+ level=logging.WARNING,
13
+ format='%(levelname)s: %(message)s'
14
+ )
15
+
16
+ def find_mkdocs_projects():
17
+ try:
18
+ git_root = Path(subprocess.check_output(
19
+ ['git', 'rev-parse', '--show-toplevel'],
20
+ text=True, encoding='utf-8'
21
+ ).strip())
22
+
23
+ projects = []
24
+ # 遍历 git_root 及子目录, 寻找 mkdocs.yml 文件
25
+ for config_file in git_root.rglob('mkdocs.y*ml'):
26
+ if config_file.name.lower() in ('mkdocs.yml', 'mkdocs.yaml'):
27
+ projects.append(config_file.parent)
28
+
29
+ if not projects:
30
+ logging.info("No MkDocs projects found in the repository")
31
+ return projects
32
+ except subprocess.CalledProcessError as e:
33
+ logging.error(f"Failed to find the Git repository root: {e}")
34
+ return []
35
+ except Exception as e:
36
+ logging.error(f"Unexpected error while searching for MkDocs projects: {e}")
37
+ return []
38
+
39
+ def get_file_creation_time(file_path):
40
+ try:
41
+ stat = os.stat(file_path)
42
+ system = platform.system().lower()
43
+ if system.startswith('win'): # Windows
44
+ return datetime.fromtimestamp(stat.st_ctime)
45
+ elif system == 'darwin': # macOS
46
+ try:
47
+ return datetime.fromtimestamp(stat.st_birthtime)
48
+ except AttributeError:
49
+ return datetime.fromtimestamp(stat.st_ctime)
50
+ else: # Linux, 没有创建时间,使用修改时间
51
+ return datetime.fromtimestamp(stat.st_mtime)
52
+ except (OSError, ValueError) as e:
53
+ logging.error(f"Failed to get file creation time for {file_path}: {e}")
54
+ return datetime.now()
55
+
56
+ def setup_gitattributes(docs_dir):
57
+ gitattributes_path = docs_dir / '.gitattributes'
58
+ union_config_line = ".dates_cache.jsonl merge=union"
59
+ updated = False
60
+
61
+ if gitattributes_path.exists():
62
+ with open(gitattributes_path, 'r', encoding='utf-8') as f:
63
+ content = f.read()
64
+
65
+ if union_config_line not in content:
66
+ with open(gitattributes_path, 'a', encoding='utf-8') as f:
67
+ f.write(f"\n{union_config_line}\n")
68
+ updated = True
69
+ else:
70
+ with open(gitattributes_path, 'w', encoding='utf-8') as f:
71
+ f.write(f"{union_config_line}\n")
72
+ updated = True
73
+
74
+ if updated:
75
+ try:
76
+ subprocess.run(["git", "add", str(gitattributes_path)], check=True)
77
+ logging.info(f"Updated .gitattributes file: {gitattributes_path}")
78
+ except subprocess.CalledProcessError as e:
79
+ logging.error(f"Failed to add .gitattributes to git: {e}")
80
+
81
+ return updated
82
+
83
+ def read_json_cache(cache_file):
84
+ dates_cache = {}
85
+ if cache_file.exists():
86
+ try:
87
+ with open(cache_file, "r", encoding='utf-8') as f:
88
+ dates_cache = json.load(f)
89
+ except (IOError, json.JSONDecodeError) as e:
90
+ logging.error(f"Failed to read existing JSON cache file: {e}")
91
+ return dates_cache
92
+
93
+ def read_jsonl_cache(jsonl_file):
94
+ dates_cache = {}
95
+ if jsonl_file.exists():
96
+ try:
97
+ with open(jsonl_file, "r", encoding='utf-8') as f:
98
+ for line in f:
99
+ try:
100
+ entry = json.loads(line.strip())
101
+ if entry and isinstance(entry, dict) and len(entry) == 1:
102
+ file_path, file_info = next(iter(entry.items()))
103
+ dates_cache[file_path] = file_info
104
+ except (json.JSONDecodeError, StopIteration) as e:
105
+ logging.warning(f"Skipping invalid JSONL line: {e}")
106
+ except IOError as e:
107
+ logging.error(f"Failed to read existing JSONL cache file: {e}")
108
+ return dates_cache
109
+
110
+ def write_jsonl_cache(jsonl_file, dates_cache, tracked_files):
111
+ try:
112
+ # 使用临时文件写入,然后替换原文件,避免写入过程中的问题
113
+ temp_file = jsonl_file.with_suffix('.jsonl.tmp')
114
+ with open(temp_file, "w", encoding='utf-8') as f:
115
+ for file_path in tracked_files:
116
+ if file_path in dates_cache:
117
+ entry = {file_path: dates_cache[file_path]}
118
+ f.write(json.dumps(entry, ensure_ascii=False) + '\n')
119
+
120
+ # 替换原文件
121
+ temp_file.replace(jsonl_file)
122
+
123
+ # 将文件添加到git
124
+ subprocess.run(["git", "add", str(jsonl_file)], check=True)
125
+ logging.info(f"Successfully updated JSONL cache file: {jsonl_file}")
126
+ return True
127
+ except (IOError, json.JSONDecodeError) as e:
128
+ logging.error(f"Failed to write JSONL cache file {jsonl_file}: {e}")
129
+ return False
130
+ except subprocess.CalledProcessError as e:
131
+ logging.error(f"Failed to add JSONL cache file to git: {e}")
132
+ return False
133
+
134
+ def update_cache():
135
+ global_updated = False
136
+ for project_dir in find_mkdocs_projects():
137
+ project_updated = False
138
+
139
+ docs_dir = project_dir / 'docs'
140
+ if not docs_dir.exists():
141
+ logging.error(f"Document directory does not exist: {docs_dir}")
142
+ continue
143
+
144
+ # 设置.gitattributes文件
145
+ gitattributes_updated = setup_gitattributes(docs_dir)
146
+ if gitattributes_updated:
147
+ global_updated = True
148
+
149
+ try:
150
+ # 获取docs目录下已跟踪(tracked)的markdown文件
151
+ cmd = ["git", "ls-files", "*.md"]
152
+ result = subprocess.run(cmd, cwd=docs_dir, capture_output=True, text=True, check=True)
153
+ tracked_files = result.stdout.splitlines() if result.stdout else []
154
+
155
+ if not tracked_files:
156
+ logging.info(f"No tracked markdown files found in {docs_dir}")
157
+ continue
158
+
159
+ # 读取旧的JSON缓存文件(如果存在)
160
+ json_cache_file = docs_dir / '.dates_cache.json'
161
+ json_dates_cache = read_json_cache(json_cache_file)
162
+
163
+ # 读取新的JSONL缓存文件(如果存在)
164
+ jsonl_cache_file = docs_dir / '.dates_cache.jsonl'
165
+ jsonl_dates_cache = read_jsonl_cache(jsonl_cache_file)
166
+
167
+ # 根据 git已跟踪的文件来更新
168
+ for file_path in tracked_files:
169
+ try:
170
+ rel_path = file_path
171
+ full_path = docs_dir / rel_path
172
+
173
+ # 如果文件已在JSONL缓存中,跳过
174
+ if rel_path in jsonl_dates_cache:
175
+ continue
176
+
177
+ # 处理新文件或迁移旧JSON缓存
178
+ if rel_path in json_dates_cache:
179
+ jsonl_dates_cache[rel_path] = json_dates_cache[rel_path]
180
+ project_updated = True
181
+ elif full_path.exists():
182
+ created_time = get_file_creation_time(full_path)
183
+ jsonl_dates_cache[rel_path] = {
184
+ "created": created_time.isoformat()
185
+ }
186
+ project_updated = True
187
+ except Exception as e:
188
+ logging.error(f"Error processing file {file_path}: {e}")
189
+
190
+ # 标记删除不再跟踪的文件
191
+ files_to_remove = set(jsonl_dates_cache.keys()) - set(tracked_files)
192
+ if files_to_remove:
193
+ project_updated = True
194
+ logging.info(f"Removing {len(files_to_remove)} untracked files from cache")
195
+
196
+ # 如果有更新,写入JSONL缓存文件
197
+ if project_updated or not jsonl_cache_file.exists():
198
+ if write_jsonl_cache(jsonl_cache_file, jsonl_dates_cache, tracked_files):
199
+ global_updated = True
200
+ else:
201
+ logging.info(f"No updates needed for {jsonl_cache_file}")
202
+
203
+ except subprocess.CalledProcessError as e:
204
+ logging.error(f"Failed to execute git command: {e}")
205
+ continue
206
+
207
+ return global_updated
208
+
209
+
210
+ if __name__ == "__main__":
211
+ try:
212
+ update_cache()
213
+ except Exception as e:
214
+ logging.error(f"Hook execution failed: {e}")
215
+ sys.exit(1)
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import sys
4
+ import logging
5
+
6
+ try:
7
+ from mkdocs_document_dates.cache_manager import update_cache
8
+ except ImportError:
9
+ sys.exit(0)
10
+
11
+ if __name__ == "__main__":
12
+ try:
13
+ update_cache()
14
+ except Exception as e:
15
+ logging.error(f"Hook execution failed: {e}")
16
+ sys.exit(1)
@@ -0,0 +1,128 @@
1
+ import os
2
+ import sys
3
+ import logging
4
+ import subprocess
5
+ from pathlib import Path
6
+ import platform
7
+
8
+ # 配置日志等级 (INFO WARNING ERROR)
9
+ logging.basicConfig(
10
+ level=logging.WARNING,
11
+ format='%(levelname)s: %(message)s'
12
+ )
13
+
14
+ def get_config_dir():
15
+ if platform.system().lower().startswith('win'):
16
+ return Path(os.getenv('APPDATA', str(Path.home() / 'AppData' / 'Roaming')))
17
+ else:
18
+ return Path.home() / '.config'
19
+
20
+ def check_python_version(interpreter):
21
+ try:
22
+ result = subprocess.run(
23
+ [interpreter, "-c", "import sys; print(sys.version_info >= (3, 7))"],
24
+ capture_output=True, text=True, check=False
25
+ )
26
+ if result.returncode == 0 and result.stdout.strip() == 'True':
27
+ return True
28
+ else:
29
+ logging.warning(f"Low python version, requires python_requires >=3.7")
30
+ return False
31
+ except Exception as e:
32
+ logging.debug(f"Failed to check {interpreter}: {str(e)}")
33
+ return False
34
+
35
+ def detect_python_interpreter():
36
+ # 检查可能的Python解释器
37
+ python_interpreters = ['python3', 'python']
38
+
39
+ for interpreter in python_interpreters:
40
+ if check_python_version(interpreter):
41
+ return f'#!/usr/bin/env {interpreter}'
42
+
43
+ # 如果都失败了,使用当前运行的Python解释器
44
+ return f'#!{sys.executable}'
45
+
46
+ def install():
47
+ try:
48
+ # 检查 git 是否可用
49
+ try:
50
+ subprocess.run(['git', '--version'], check=True, capture_output=True, encoding='utf-8')
51
+ except (subprocess.CalledProcessError, FileNotFoundError):
52
+ logging.warning("Git not detected, skip hooks installation")
53
+ return False
54
+
55
+ # 准备配置目录
56
+ config_dir = get_config_dir() / 'mkdocs-document-dates' / 'hooks'
57
+ try:
58
+ config_dir.mkdir(parents=True, exist_ok=True)
59
+ except PermissionError:
60
+ logging.error(f"No permission to create directory: {config_dir}")
61
+ return False
62
+ except Exception as e:
63
+ logging.error(f"Failed to create directory {config_dir}: {str(e)}")
64
+ return False
65
+
66
+ # 检测Python解释器并获取合适的shebang行
67
+ shebang = detect_python_interpreter()
68
+ logging.info(f"Using shebang: {shebang}")
69
+
70
+ # 安装钩子文件
71
+ hooks_installed = False
72
+ source_hooks_dir = Path(__file__).parent / 'hooks'
73
+ for hook_file in source_hooks_dir.glob('*'):
74
+ if hook_file.is_file() and not hook_file.name.startswith('.'):
75
+ target_hook_path = config_dir / hook_file.name
76
+ try:
77
+ # 读取源文件内容
78
+ with open(hook_file, 'r', encoding='utf-8') as f_in:
79
+ content = f_in.read()
80
+
81
+ # 修改shebang行
82
+ if content.startswith('#!'):
83
+ content = shebang + os.linesep + content[content.find('\n'):]
84
+ else:
85
+ content = shebang + os.linesep + content
86
+
87
+ # 直接写入目标文件
88
+ with open(target_hook_path, 'w', encoding='utf-8') as f_out:
89
+ f_out.write(content)
90
+
91
+ # 设置执行权限
92
+ os.chmod(target_hook_path, 0o755)
93
+ hooks_installed = True
94
+ logging.info(f"Created hook with custom shebang: {hook_file.name}")
95
+ except Exception as e:
96
+ logging.error(f"Failed to create file {target_hook_path}: {str(e)}")
97
+ return False
98
+
99
+ if not hooks_installed:
100
+ logging.warning("No hook files found, the hooks installation failed")
101
+ return False
102
+
103
+ # 设置目录权限
104
+ try:
105
+ os.chmod(config_dir, 0o755)
106
+ except OSError as e:
107
+ logging.warning(f"Failed to set directory permissions: {str(e)}")
108
+
109
+ # 配置全局 git hooks 路径
110
+ try:
111
+ subprocess.run(
112
+ ['git', 'config', '--global', 'core.hooksPath', str(config_dir)],
113
+ check=True,
114
+ capture_output=True,
115
+ encoding='utf-8'
116
+ )
117
+ logging.info(f"Git hooks successfully installed in: {config_dir}")
118
+ return True
119
+ except subprocess.CalledProcessError as e:
120
+ logging.error(f"Failed to set git hooks path: {str(e)}")
121
+ return False
122
+
123
+ except Exception as e:
124
+ logging.error(f"Unexpected error during hooks installation: {str(e)}")
125
+ return False
126
+
127
+ if __name__ == '__main__':
128
+ install()