mkdocs-document-dates 3.1__tar.gz → 3.1.2__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 (54) hide show
  1. {mkdocs_document_dates-3.1 → mkdocs_document_dates-3.1.2}/PKG-INFO +30 -17
  2. {mkdocs_document_dates-3.1 → mkdocs_document_dates-3.1.2}/README.md +27 -16
  3. mkdocs_document_dates-3.1.2/mkdocs_document_dates/__init__.py +6 -0
  4. mkdocs_document_dates-3.1.2/mkdocs_document_dates/cache_manager.py +234 -0
  5. {mkdocs_document_dates-3.1 → mkdocs_document_dates-3.1.2}/mkdocs_document_dates/hooks/pre-commit +2 -6
  6. mkdocs_document_dates-3.1.2/mkdocs_document_dates/hooks_installer.py +126 -0
  7. {mkdocs_document_dates-3.1 → mkdocs_document_dates-3.1.2}/mkdocs_document_dates/plugin.py +46 -71
  8. {mkdocs_document_dates-3.1 → mkdocs_document_dates-3.1.2}/mkdocs_document_dates/static/config/user.config.js +35 -0
  9. mkdocs_document_dates-3.1.2/mkdocs_document_dates/static/core/timeago-load.js +5 -0
  10. mkdocs_document_dates-3.1.2/mkdocs_document_dates/static/core/timeago.full.min.js +1 -0
  11. mkdocs_document_dates-3.1.2/mkdocs_document_dates/static/core/timeago.min.js +1 -0
  12. mkdocs_document_dates-3.1.2/mkdocs_document_dates/static/languages/ar.json +6 -0
  13. mkdocs_document_dates-3.1.2/mkdocs_document_dates/static/languages/de.json +6 -0
  14. mkdocs_document_dates-3.1.2/mkdocs_document_dates/static/languages/en.json +6 -0
  15. mkdocs_document_dates-3.1.2/mkdocs_document_dates/static/languages/es.json +6 -0
  16. mkdocs_document_dates-3.1.2/mkdocs_document_dates/static/languages/fr.json +6 -0
  17. mkdocs_document_dates-3.1.2/mkdocs_document_dates/static/languages/ja.json +6 -0
  18. mkdocs_document_dates-3.1.2/mkdocs_document_dates/static/languages/ko.json +6 -0
  19. mkdocs_document_dates-3.1.2/mkdocs_document_dates/static/languages/ru.json +6 -0
  20. mkdocs_document_dates-3.1.2/mkdocs_document_dates/static/languages/zh.json +6 -0
  21. mkdocs_document_dates-3.1.2/mkdocs_document_dates/static/languages/zh_tw.json +6 -0
  22. {mkdocs_document_dates-3.1 → mkdocs_document_dates-3.1.2}/mkdocs_document_dates.egg-info/PKG-INFO +30 -17
  23. {mkdocs_document_dates-3.1 → mkdocs_document_dates-3.1.2}/mkdocs_document_dates.egg-info/SOURCES.txt +3 -0
  24. {mkdocs_document_dates-3.1 → mkdocs_document_dates-3.1.2}/setup.py +29 -2
  25. mkdocs_document_dates-3.1/mkdocs_document_dates/__init__.py +0 -11
  26. mkdocs_document_dates-3.1/mkdocs_document_dates/cache_manager.py +0 -215
  27. mkdocs_document_dates-3.1/mkdocs_document_dates/hooks_installer.py +0 -128
  28. mkdocs_document_dates-3.1/mkdocs_document_dates/static/languages/ar.json +0 -20
  29. mkdocs_document_dates-3.1/mkdocs_document_dates/static/languages/de.json +0 -20
  30. mkdocs_document_dates-3.1/mkdocs_document_dates/static/languages/en.json +0 -20
  31. mkdocs_document_dates-3.1/mkdocs_document_dates/static/languages/es.json +0 -20
  32. mkdocs_document_dates-3.1/mkdocs_document_dates/static/languages/fr.json +0 -20
  33. mkdocs_document_dates-3.1/mkdocs_document_dates/static/languages/ja.json +0 -20
  34. mkdocs_document_dates-3.1/mkdocs_document_dates/static/languages/ko.json +0 -20
  35. mkdocs_document_dates-3.1/mkdocs_document_dates/static/languages/ru.json +0 -20
  36. mkdocs_document_dates-3.1/mkdocs_document_dates/static/languages/zh.json +0 -20
  37. mkdocs_document_dates-3.1/mkdocs_document_dates/static/languages/zh_tw.json +0 -20
  38. {mkdocs_document_dates-3.1 → mkdocs_document_dates-3.1.2}/LICENSE +0 -0
  39. {mkdocs_document_dates-3.1 → mkdocs_document_dates-3.1.2}/mkdocs_document_dates/static/config/user.config.css +0 -0
  40. {mkdocs_document_dates-3.1 → mkdocs_document_dates-3.1.2}/mkdocs_document_dates/static/core/core.css +0 -0
  41. {mkdocs_document_dates-3.1 → mkdocs_document_dates-3.1.2}/mkdocs_document_dates/static/core/core.js +0 -0
  42. {mkdocs_document_dates-3.1 → mkdocs_document_dates-3.1.2}/mkdocs_document_dates/static/tippy/backdrop.css +0 -0
  43. {mkdocs_document_dates-3.1 → mkdocs_document_dates-3.1.2}/mkdocs_document_dates/static/tippy/light.css +0 -0
  44. {mkdocs_document_dates-3.1 → mkdocs_document_dates-3.1.2}/mkdocs_document_dates/static/tippy/material.css +0 -0
  45. {mkdocs_document_dates-3.1 → mkdocs_document_dates-3.1.2}/mkdocs_document_dates/static/tippy/popper.min.js +0 -0
  46. {mkdocs_document_dates-3.1 → mkdocs_document_dates-3.1.2}/mkdocs_document_dates/static/tippy/scale.css +0 -0
  47. {mkdocs_document_dates-3.1 → mkdocs_document_dates-3.1.2}/mkdocs_document_dates/static/tippy/shift-away.css +0 -0
  48. {mkdocs_document_dates-3.1 → mkdocs_document_dates-3.1.2}/mkdocs_document_dates/static/tippy/tippy.css +0 -0
  49. {mkdocs_document_dates-3.1 → mkdocs_document_dates-3.1.2}/mkdocs_document_dates/static/tippy/tippy.umd.min.js +0 -0
  50. {mkdocs_document_dates-3.1 → mkdocs_document_dates-3.1.2}/mkdocs_document_dates.egg-info/dependency_links.txt +0 -0
  51. {mkdocs_document_dates-3.1 → mkdocs_document_dates-3.1.2}/mkdocs_document_dates.egg-info/entry_points.txt +0 -0
  52. {mkdocs_document_dates-3.1 → mkdocs_document_dates-3.1.2}/mkdocs_document_dates.egg-info/requires.txt +0 -0
  53. {mkdocs_document_dates-3.1 → mkdocs_document_dates-3.1.2}/mkdocs_document_dates.egg-info/top_level.txt +0 -0
  54. {mkdocs_document_dates-3.1 → mkdocs_document_dates-3.1.2}/setup.cfg +0 -0
@@ -1,9 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mkdocs-document-dates
3
- Version: 3.1
3
+ Version: 3.1.2
4
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
5
  Home-page: https://github.com/jaywhj/mkdocs-document-dates
6
6
  Author: Aaron Wang
7
+ License: MIT
7
8
  Classifier: Programming Language :: Python :: 3
8
9
  Classifier: License :: OSI Approved :: MIT License
9
10
  Classifier: Operating System :: OS Independent
@@ -16,6 +17,7 @@ Dynamic: classifier
16
17
  Dynamic: description
17
18
  Dynamic: description-content-type
18
19
  Dynamic: home-page
20
+ Dynamic: license
19
21
  Dynamic: license-file
20
22
  Dynamic: requires-dist
21
23
  Dynamic: requires-python
@@ -70,7 +72,7 @@ plugins:
70
72
  - document-dates:
71
73
  position: top # Display position: top (after title) bottom (end of document), default: bottom
72
74
  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
75
+ locale: en # Localization: zh zh_TW en es fr de ar ja ko ru, default: en
74
76
  date_format: '%Y-%m-%d' # Date format, Supports all Python datetime format strings, e.g., %Y-%m-%d, %b %d, %Y, etc
75
77
  time_format: '%H:%M:%S' # Time format (valid only if type=datetime)
76
78
  exclude: # List of excluded files
@@ -120,38 +122,49 @@ email: e-name@gmail.com
120
122
 
121
123
  ## Customization
122
124
 
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):
125
+ 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):
124
126
 
125
127
  - Style & Theme: `docs/assets/document_dates/user.config.css`
126
128
  - Properties & Animations: `docs/assets/document_dates/user.config.js`
127
129
  - 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
+ - timeago.js localization: `timeago.min.js` only contains English and Chinese by default, if you need to load other languages, you can configure it as below (choose one):
131
+ - In user.config.js, refer to [the demo commented out](https://github.com/jaywhj/mkdocs-document-dates/blob/main/mkdocs_document_dates/static/config/user.config.js) at the bottom, translate it into your local language
132
+ - In mkdocs.yml, add the following two lines to configure the full version of `timeago.full.min.js` to load all languages at once
133
+
134
+ ```yaml
135
+ extra_javascript:
136
+ - assets/document_dates/core/timeago.full.min.js
137
+ - assets/document_dates/core/timeago-load.js
138
+ ```
130
139
 
131
140
  ## Other Tips
132
141
 
133
142
  - 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
143
+ - `docs/.dates_cache.jsonl`, cache file
144
+ - `docs/.gitattributes`, merge mechanism for cache file in case of multi-person collaboration
145
+ - 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. In addition, the installation of Git Hooks is automatically triggered when the plugin is installed, without any manual intervention!
146
+
147
+ <br />
139
148
 
140
149
  ## Development Stories (Optional)
141
150
 
142
- A dispensable, insignificant little plug-in, friends who have nothing to do can take a look \^\_\^
151
+ A dispensable, insignificant little plug-in, friends who have time can take a look \^\_\^
143
152
 
144
153
  - **Origin**:
145
154
  - 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
155
  - **Iteration**:
147
156
  - 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
157
  - 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!
158
+ - 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. Storing in Front Matter makes sense and is 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
159
  - **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'
160
+ 1. When to read and store original 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!
161
+ - 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 git action occurs, such as every time commit is performed
162
+ 2. How to install Git Hooks automatically? When and how are they triggered? Installing packages from PyPI via pip doesn't have a standard post-install hook mechanism
163
+ - Workaround: After analyzing the flow of pip installing packages from PyPI, I found that when compiling and installing through the source package (sdist), setuptools will be called to handle it, so we can find a way to implant the installation script in the process of setuptools, i.e., we can add a custom script in setup.py
164
+ 3. How to design a cross-platform hook? To execute a python script, we need to explicitly specify the python interpreter, and the user's python environment varies depending on the operating system, the way python is installed, and the configuration, so how can we ensure that it works properly in all environments?
165
+ - Solution: I considered using a shell script, but since I'd have to call back to python eventually, it's easier to use a python script. We can detect the user's python environment when the hook is installed, and then dynamically set the hook's shebang line to set the correct python interpreter
166
+ 4. How can I ensure that a single cache file does not conflict when collaborating with multi-person?
167
+ - Workaround: use JSONL (JSON Lines) instead of JSON, and with the merge strategy 'merge=union'
155
168
  - **Improve**:
156
169
  - 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
170
  - 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
@@ -159,4 +172,4 @@ A dispensable, insignificant little plug-in, friends who have nothing to do can
159
172
  - Personalization: almost everything can be customized, whether it's icons, styles, themes, or features, it's all fully customizable
160
173
  - In addition, it has good compatibility and extensibility, and works well in WIN7, mobile devices, old Safari, etc
161
174
  - **The Last Secret**:
162
- - I'm not a programmer, my main job is marketing, can you believe it? (Feel free to leave a comment)
175
+ - Programming is a hobby, and I'm a marketer of 8 years (Feel free to leave a comment)
@@ -47,7 +47,7 @@ plugins:
47
47
  - document-dates:
48
48
  position: top # Display position: top (after title) bottom (end of document), default: bottom
49
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
50
+ locale: en # Localization: zh zh_TW en es fr de ar ja ko ru, default: en
51
51
  date_format: '%Y-%m-%d' # Date format, Supports all Python datetime format strings, e.g., %Y-%m-%d, %b %d, %Y, etc
52
52
  time_format: '%H:%M:%S' # Time format (valid only if type=datetime)
53
53
  exclude: # List of excluded files
@@ -97,38 +97,49 @@ email: e-name@gmail.com
97
97
 
98
98
  ## Customization
99
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):
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):
101
101
 
102
102
  - Style & Theme: `docs/assets/document_dates/user.config.css`
103
103
  - Properties & Animations: `docs/assets/document_dates/user.config.js`
104
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
105
+ - timeago.js localization: `timeago.min.js` only contains English and Chinese by default, if you need to load other languages, you can configure it as below (choose one):
106
+ - In user.config.js, refer to [the demo commented out](https://github.com/jaywhj/mkdocs-document-dates/blob/main/mkdocs_document_dates/static/config/user.config.js) at the bottom, translate it into your local language
107
+ - In mkdocs.yml, add the following two lines to configure the full version of `timeago.full.min.js` to load all languages at once
108
+
109
+ ```yaml
110
+ extra_javascript:
111
+ - assets/document_dates/core/timeago.full.min.js
112
+ - assets/document_dates/core/timeago-load.js
113
+ ```
107
114
 
108
115
  ## Other Tips
109
116
 
110
117
  - 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
118
+ - `docs/.dates_cache.jsonl`, cache file
119
+ - `docs/.gitattributes`, merge mechanism for cache file in case of multi-person collaboration
120
+ - 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. In addition, the installation of Git Hooks is automatically triggered when the plugin is installed, without any manual intervention!
121
+
122
+ <br />
116
123
 
117
124
  ## Development Stories (Optional)
118
125
 
119
- A dispensable, insignificant little plug-in, friends who have nothing to do can take a look \^\_\^
126
+ A dispensable, insignificant little plug-in, friends who have time can take a look \^\_\^
120
127
 
121
128
  - **Origin**:
122
129
  - 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
130
  - **Iteration**:
124
131
  - 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
132
  - 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!
133
+ - 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. Storing in Front Matter makes sense and is 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
134
  - **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'
135
+ 1. When to read and store original 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!
136
+ - 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 git action occurs, such as every time commit is performed
137
+ 2. How to install Git Hooks automatically? When and how are they triggered? Installing packages from PyPI via pip doesn't have a standard post-install hook mechanism
138
+ - Workaround: After analyzing the flow of pip installing packages from PyPI, I found that when compiling and installing through the source package (sdist), setuptools will be called to handle it, so we can find a way to implant the installation script in the process of setuptools, i.e., we can add a custom script in setup.py
139
+ 3. How to design a cross-platform hook? To execute a python script, we need to explicitly specify the python interpreter, and the user's python environment varies depending on the operating system, the way python is installed, and the configuration, so how can we ensure that it works properly in all environments?
140
+ - Solution: I considered using a shell script, but since I'd have to call back to python eventually, it's easier to use a python script. We can detect the user's python environment when the hook is installed, and then dynamically set the hook's shebang line to set the correct python interpreter
141
+ 4. How can I ensure that a single cache file does not conflict when collaborating with multi-person?
142
+ - Workaround: use JSONL (JSON Lines) instead of JSON, and with the merge strategy 'merge=union'
132
143
  - **Improve**:
133
144
  - 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
145
  - 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
@@ -136,4 +147,4 @@ A dispensable, insignificant little plug-in, friends who have nothing to do can
136
147
  - Personalization: almost everything can be customized, whether it's icons, styles, themes, or features, it's all fully customizable
137
148
  - In addition, it has good compatibility and extensibility, and works well in WIN7, mobile devices, old Safari, etc
138
149
  - **The Last Secret**:
139
- - I'm not a programmer, my main job is marketing, can you believe it? (Feel free to leave a comment)
150
+ - Programming is a hobby, and I'm a marketer of 8 years (Feel free to leave a comment)
@@ -0,0 +1,6 @@
1
+ """MkDocs Document Dates Plugin."""
2
+
3
+ from .hooks_installer import install
4
+
5
+ # 在包被导入时自动执行 hooks 安装
6
+ install()
@@ -0,0 +1,234 @@
1
+ import os
2
+ import json
3
+ import logging
4
+ import platform
5
+ import subprocess
6
+ from datetime import datetime
7
+ from pathlib import Path
8
+
9
+ # 配置日志等级 (INFO WARNING ERROR)
10
+ logging.basicConfig(
11
+ level=logging.WARNING,
12
+ format='%(levelname)s: %(message)s'
13
+ )
14
+
15
+ def find_mkdocs_projects():
16
+ try:
17
+ git_root = Path(subprocess.check_output(
18
+ ['git', 'rev-parse', '--show-toplevel'],
19
+ text=True, encoding='utf-8'
20
+ ).strip())
21
+
22
+ projects = []
23
+ # 遍历 git_root 及子目录, 寻找 mkdocs.yml 文件
24
+ for config_file in git_root.rglob('mkdocs.y*ml'):
25
+ if config_file.name.lower() in ('mkdocs.yml', 'mkdocs.yaml'):
26
+ projects.append(config_file.parent)
27
+
28
+ if not projects:
29
+ logging.info("No MkDocs projects found in the repository")
30
+ return projects
31
+ except subprocess.CalledProcessError as e:
32
+ logging.error(f"Failed to find the Git repository root: {e}")
33
+ return []
34
+ except Exception as e:
35
+ logging.error(f"Unexpected error while searching for MkDocs projects: {e}")
36
+ return []
37
+
38
+ def get_file_creation_time(file_path):
39
+ try:
40
+ stat = os.stat(file_path)
41
+ system = platform.system().lower()
42
+ if system.startswith('win'): # Windows
43
+ return datetime.fromtimestamp(stat.st_ctime)
44
+ elif system == 'darwin': # macOS
45
+ try:
46
+ return datetime.fromtimestamp(stat.st_birthtime)
47
+ except AttributeError:
48
+ return datetime.fromtimestamp(stat.st_ctime)
49
+ else: # Linux, 没有创建时间,使用修改时间
50
+ return datetime.fromtimestamp(stat.st_mtime)
51
+ except (OSError, ValueError) as e:
52
+ logging.error(f"Failed to get file creation time for {file_path}: {e}")
53
+ return datetime.now()
54
+
55
+ def get_git_first_commit_time(file_path):
56
+ try:
57
+ # git log --reverse --format="%aI" --date=iso -- {file_path} | head -n 1
58
+ result = subprocess.run(['git', 'log', '--reverse', '--format=%aI', '--', file_path], capture_output=True, text=True)
59
+ if result.returncode == 0:
60
+ commits = result.stdout.strip().split('\n')
61
+ if commits and commits[0]:
62
+ return datetime.fromisoformat(commits[0]).replace(tzinfo=None)
63
+ except Exception as e:
64
+ logging.info(f"Error getting git first commit time for {file_path}: {e}")
65
+ return None
66
+
67
+ def setup_gitattributes(docs_dir):
68
+ updated = False
69
+ try:
70
+ gitattributes_path = docs_dir / '.gitattributes'
71
+ union_config_line = ".dates_cache.jsonl merge=union"
72
+ if gitattributes_path.exists():
73
+ with open(gitattributes_path, 'r', encoding='utf-8') as f:
74
+ content = f.read()
75
+
76
+ if union_config_line not in content:
77
+ with open(gitattributes_path, 'a', encoding='utf-8') as f:
78
+ f.write(f"\n{union_config_line}\n")
79
+ updated = True
80
+ else:
81
+ with open(gitattributes_path, 'w', encoding='utf-8') as f:
82
+ f.write(f"{union_config_line}\n")
83
+ updated = True
84
+
85
+ if updated:
86
+ subprocess.run(["git", "add", str(gitattributes_path)], check=True)
87
+ logging.info(f"Updated .gitattributes file: {gitattributes_path}")
88
+ except (IOError, OSError) as e:
89
+ logging.error(f"Failed to read/write .gitattributes file: {e}")
90
+ except Exception as e:
91
+ logging.error(f"Failed to add .gitattributes to git: {e}")
92
+
93
+ return updated
94
+
95
+ def read_json_cache(cache_file):
96
+ dates_cache = {}
97
+ if cache_file.exists():
98
+ try:
99
+ with open(cache_file, "r", encoding='utf-8') as f:
100
+ dates_cache = json.load(f)
101
+ except (IOError, json.JSONDecodeError) as e:
102
+ logging.error(f"Failed to read existing JSON cache file: {e}")
103
+ return dates_cache
104
+
105
+ def read_jsonl_cache(jsonl_file):
106
+ dates_cache = {}
107
+ if jsonl_file.exists():
108
+ try:
109
+ with open(jsonl_file, "r", encoding='utf-8') as f:
110
+ for line in f:
111
+ try:
112
+ entry = json.loads(line.strip())
113
+ if entry and isinstance(entry, dict) and len(entry) == 1:
114
+ file_path, file_info = next(iter(entry.items()))
115
+ dates_cache[file_path] = file_info
116
+ except (json.JSONDecodeError, StopIteration) as e:
117
+ logging.warning(f"Skipping invalid JSONL line: {e}")
118
+ except IOError as e:
119
+ logging.error(f"Failed to read existing JSONL cache file: {e}")
120
+ return dates_cache
121
+
122
+ def write_jsonl_cache(jsonl_file, dates_cache, tracked_files):
123
+ try:
124
+ # 使用临时文件写入,然后替换原文件,避免写入过程中的问题
125
+ temp_file = jsonl_file.with_suffix('.jsonl.tmp')
126
+ with open(temp_file, "w", encoding='utf-8') as f:
127
+ for file_path in tracked_files:
128
+ if file_path in dates_cache:
129
+ entry = {file_path: dates_cache[file_path]}
130
+ f.write(json.dumps(entry, ensure_ascii=False) + '\n')
131
+
132
+ # 替换原文件
133
+ temp_file.replace(jsonl_file)
134
+
135
+ # 将文件添加到git
136
+ subprocess.run(["git", "add", str(jsonl_file)], check=True)
137
+ logging.info(f"Successfully updated JSONL cache file: {jsonl_file}")
138
+ return True
139
+ except (IOError, json.JSONDecodeError) as e:
140
+ logging.error(f"Failed to write JSONL cache file {jsonl_file}: {e}")
141
+ return False
142
+ except subprocess.CalledProcessError as e:
143
+ logging.error(f"Failed to add JSONL cache file to git: {e}")
144
+ return False
145
+
146
+ def update_cache():
147
+ global_updated = False
148
+ try:
149
+ for project_dir in find_mkdocs_projects():
150
+ try:
151
+ project_updated = False
152
+
153
+ docs_dir = project_dir / 'docs'
154
+ if not docs_dir.exists():
155
+ logging.error(f"Document directory does not exist: {docs_dir}")
156
+ continue
157
+
158
+ # 设置.gitattributes文件
159
+ gitattributes_updated = setup_gitattributes(docs_dir)
160
+ if gitattributes_updated:
161
+ global_updated = True
162
+
163
+ # 获取docs目录下已跟踪(tracked)的markdown文件
164
+ cmd = ["git", "ls-files", "*.md"]
165
+ result = subprocess.run(cmd, cwd=docs_dir, capture_output=True, text=True, check=True)
166
+ tracked_files = result.stdout.splitlines() if result.stdout else []
167
+
168
+ if not tracked_files:
169
+ logging.info(f"No tracked markdown files found in {docs_dir}")
170
+ continue
171
+
172
+ # 读取旧的JSON缓存文件(如果存在)
173
+ json_cache_file = docs_dir / '.dates_cache.json'
174
+ json_dates_cache = read_json_cache(json_cache_file)
175
+
176
+ # 读取新的JSONL缓存文件(如果存在)
177
+ jsonl_cache_file = docs_dir / '.dates_cache.jsonl'
178
+ jsonl_dates_cache = read_jsonl_cache(jsonl_cache_file)
179
+
180
+ # 根据 git已跟踪的文件来更新
181
+ for file_path in tracked_files:
182
+ try:
183
+ rel_path = file_path
184
+ full_path = docs_dir / rel_path
185
+
186
+ # 如果文件已在JSONL缓存中,跳过
187
+ if rel_path in jsonl_dates_cache:
188
+ continue
189
+
190
+ # 处理新文件或迁移旧JSON缓存
191
+ if rel_path in json_dates_cache:
192
+ jsonl_dates_cache[rel_path] = json_dates_cache[rel_path]
193
+ project_updated = True
194
+ elif full_path.exists():
195
+ created_time = get_file_creation_time(full_path)
196
+ if not jsonl_cache_file.exists() and not json_cache_file.exists():
197
+ git_time = get_git_first_commit_time(full_path)
198
+ if git_time is not None:
199
+ created_time = min(created_time, git_time)
200
+ jsonl_dates_cache[rel_path] = {
201
+ "created": created_time.isoformat()
202
+ }
203
+ project_updated = True
204
+ except Exception as e:
205
+ logging.error(f"Error processing file {file_path}: {e}")
206
+ continue
207
+
208
+ # 标记删除不再跟踪的文件
209
+ files_to_remove = set(jsonl_dates_cache.keys()) - set(tracked_files)
210
+ if files_to_remove:
211
+ project_updated = True
212
+ logging.info(f"Removing {len(files_to_remove)} untracked files from cache")
213
+
214
+ # 如果有更新,写入JSONL缓存文件
215
+ if project_updated or not jsonl_cache_file.exists():
216
+ if write_jsonl_cache(jsonl_cache_file, jsonl_dates_cache, tracked_files):
217
+ global_updated = True
218
+ else:
219
+ logging.info(f"No updates needed for {jsonl_cache_file}")
220
+ except subprocess.CalledProcessError as e:
221
+ logging.error(f"Failed to execute git command: {e}")
222
+ continue
223
+ except Exception as e:
224
+ logging.error(f"Error processing project directory {project_dir}: {e}")
225
+ continue
226
+
227
+ except Exception as e:
228
+ logging.error(f"Unexpected error in update_cache: {e}")
229
+
230
+ return global_updated
231
+
232
+
233
+ if __name__ == "__main__":
234
+ update_cache()
@@ -1,16 +1,12 @@
1
1
  #!/usr/bin/env python3
2
2
 
3
3
  import sys
4
- import logging
5
4
 
6
5
  try:
7
6
  from mkdocs_document_dates.cache_manager import update_cache
8
7
  except ImportError:
8
+ # 正常退出(0 状态码),不影响 git 的后续动作
9
9
  sys.exit(0)
10
10
 
11
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)
12
+ update_cache()
@@ -0,0 +1,126 @@
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 check_git_available():
47
+ try:
48
+ subprocess.run(['git', '--version'], check=True, capture_output=True, encoding='utf-8')
49
+ return True
50
+ except Exception:
51
+ logging.warning("Git not detected, skip hooks installation")
52
+ return False
53
+
54
+ def setup_hooks_directory():
55
+ config_dir = get_config_dir() / 'mkdocs-document-dates' / 'hooks'
56
+ try:
57
+ config_dir.mkdir(parents=True, exist_ok=True)
58
+ os.chmod(config_dir, 0o755)
59
+ return config_dir
60
+ except PermissionError:
61
+ logging.error(f"No permission to create directory: {config_dir}")
62
+ return None
63
+ except Exception as e:
64
+ logging.error(f"Failed to create directory {config_dir}: {str(e)}")
65
+ return None
66
+
67
+ def install_hook_file(source_hook, target_dir):
68
+ target_hook_path = target_dir / source_hook.name
69
+ try:
70
+ # 读取并更新hook文件内容
71
+ with open(source_hook, 'r', encoding='utf-8') as f_in:
72
+ content = f_in.read()
73
+
74
+ # 更新shebang行
75
+ shebang = detect_python_interpreter()
76
+ if content.startswith('#!'):
77
+ content = shebang + os.linesep + content[content.find('\n'):]
78
+ else:
79
+ content = shebang + os.linesep + content
80
+
81
+ # 写入并设置权限
82
+ with open(target_hook_path, 'w', encoding='utf-8') as f_out:
83
+ f_out.write(content)
84
+ os.chmod(target_hook_path, 0o755)
85
+ return True
86
+ except Exception as e:
87
+ logging.error(f"Failed to create hook file {target_hook_path}: {str(e)}")
88
+ return False
89
+
90
+ def configure_git_hooks(hooks_dir):
91
+ try:
92
+ subprocess.run(
93
+ ['git', 'config', '--global', 'core.hooksPath', str(hooks_dir)],
94
+ check=True, capture_output=True, encoding='utf-8'
95
+ )
96
+ logging.info(f"Git hooks successfully installed in: {hooks_dir}")
97
+ return True
98
+ except Exception as e:
99
+ logging.error(f"Failed to set git hooks path: {str(e)}")
100
+ return False
101
+
102
+ def install():
103
+ try:
104
+ # 检查git是否可用
105
+ if not check_git_available():
106
+ return False
107
+
108
+ # 创建hooks目录
109
+ hooks_dir = setup_hooks_directory()
110
+ if not hooks_dir:
111
+ return False
112
+
113
+ # 安装hook文件
114
+ source_hook = Path(__file__).parent / 'hooks' / 'pre-commit'
115
+ if not install_hook_file(source_hook, hooks_dir):
116
+ return False
117
+
118
+ # 配置git hooks路径
119
+ return configure_git_hooks(hooks_dir)
120
+
121
+ except Exception as e:
122
+ logging.error(f"Unexpected error during hooks installation: {str(e)}")
123
+ return False
124
+
125
+ if __name__ == '__main__':
126
+ install()