Script to generate Markdown files with embedded PlantUML diagrams for GitLab's PlantUML renderer

huangapple go评论95阅读模式
英文:

Script to generate Markdown files with embedded PlantUML diagrams for GitLab's PlantUML renderer

问题

I understand your request. Here's the translated content:

"我正在设置一个存储由多个Markdown文档组成的软件文档的存储库,并希望能够嵌入PlantUML图表。该存储库托管在Gitlab中,其中包括PlantUML渲染器,但不允许预处理,并因此不允许使用!include子句引用其他文件中的图表。

我想要一个Bash或Python脚本,它可以:

  1. 搜索所有的.md文件,并将它们的内容追加到一个新文件"all-docs.md"中。
  2. 在文件"all-docs.md"中搜索所有的!include [FILEPATH]子句,并将位于该文件[FILEPATH]中的@startuml@enduml之间的内容替换为"all-docs.md"中的内容。

例如:

"all-docs.md"在某个部分包含:

  1. 这是Profile类图:
  2. ``plantuml
  3. @startuml
  4. !include ./data-models/profile.puml
  5. Profile o-- UidObject
  6. @enduml

而profile.puml的内容是:

  1. @startuml
  2. class Profile <UidObject> {
  3. + string name
  4. + string email
  5. + string phone
  6. + Date birthDate
  7. }
  8. @enduml

脚本运行后的结果将是在"all-docs.md"中拥有以下内容:

  1. 这是Profile类图:
  2. ``plantuml
  3. @startuml
  4. class Profile <UidObject> {
  5. + string name
  6. + string email
  7. + string phone
  8. + Date birthDate
  9. }
  10. Profile o-- UidObject
  11. @enduml

该存储库具有以下结构。

  1. /
  2. ├── assets/
  3. ├── docs/
  4. ├── uml/
  • "assets/"目录包含各种资源,如图像、图标和其他资源。
  • "docs/"目录包含文档(Markdown文件)。
  • "uml/"目录包含包含用于生成软件文档的图表的PlantUML源文件。"
英文:

I am setting up a repository to store software documentation consisting of several documents which are written in Markdown, and I want to be able to embed PlantUML diagrams in them. The repository is hosted in Gitlab, which includes a PlantUML renderer but does not allow preprocessing and therefore using the !include clause to reference diagrams in other files.

I would like to have a bash or python script that:

  1. Searches all .md files and append their content one after the other in a new file "all-docs.md".
  2. Searches in that file "all-docs.md" for all the !include [FILEPATH] clauses and replace the content which is between @startuml and @enduml from that file [FILEPATH] into "all-docs.md".

For example:

"all-docs.md" contains in certain part:

  1. Here is the Profile class diagram:
  2. ``plantuml
  3. @startuml
  4. !include ./data-models/profile.puml
  5. Profile o-- UidObject
  6. @enduml
  7. ``

And profile.puml content is:

  1. @startuml
  2. class Profile <UidObject> {
  3. + string name
  4. + string email
  5. + string phone
  6. + Date birthDate
  7. }
  8. @enduml

The result after the script will be to have in "all-docs.md":

  1. Here is the Profile class diagram:
  2. ``plantuml
  3. @startuml
  4. class Profile <UidObject> {
  5. + string name
  6. + string email
  7. + string phone
  8. + Date birthDate
  9. }
  10. Profile o-- UidObject
  11. @enduml
  12. ``

The repo has the following structure.

  1. /
  2. ├── assets/
  3. ├── docs/
  4. ├── uml/
  • The assets/ directory contains various assets such as images, icons, and other resources.
  • The docs/ directory contains the documents (markdown files)
  • The uml/ directory contains contains PlantUML source files that are used to generate diagrams for the software documentation.

答案1

得分: 1

A bashfind 解决方案,使用您提供的输入/文件,类似于:

  1. #!/usr/bin/env bash
  2. #: 在所有-docs.md文件中查找并连接docs/目录中的所有.md文件。
  3. find docs/ -type f -name '*.md' -exec sh -c 'cat -- "$@" >> all-docs.md' sh {} +
  4. #: 解析all-docs.md文件并
  5. #: 创建/打印所需的结果/输出。
  6. while IFS= read -r line; do
  7. if [[ $line == "!include"* ]]; then
  8. temp=${line#!include *}
  9. mapfile -t plum < "$temp" &&
  10. unset -v 'plum[-1]' &&
  11. printf '%s\n' "${plum[@]:1}"
  12. else
  13. printf '%s\n' "$line"
  14. fi
  15. done {fd}< all-docs.md
  • 将代码的最后一行替换为
  1. done {fd}< all-docs.md > tempfile && mv tempfile all-docs.md
  • 如果您对输出满意并需要对all-docs.md进行永久更改。

或者直接解析find的输出,而不创建all-docs.md,类似于:

  1. #!/usr/bin/env bash
  2. ##: 在docs/目录中查找以.md结尾的所有文件
  3. ##: 连接所需文件的所有内容。
  4. find docs/ -type f -name '*.md' -exec sh -c 'cat -- "$@"' sh {} + | {
  5. while IFS= read -r line; do
  6. [[ $line != "!include"* ]] && { ##: 如果行中没有模式!include。
  7. printf '%s\n' "$line" ##: 打印行。
  8. }
  9. [[ $line == "!include"* ]] && { ##: 如果行包含模式!include,解析它。
  10. temp=${line#!include *} ##: 将FILE_PATH提取到名为temp的变量中。
  11. if [[ -s "$temp" ]]; then ##: 如果变量是现有的非空文件。
  12. mapfile -u3 -t plum 3< "$temp" && ##: 提取所需结果。
  13. unset -v 'plum[-1]' &&
  14. printf '%s\n' "${plum[@]:1}"
  15. else
  16. printf '%s\n' "$line" ##: 否则只打印行。
  17. fi
  18. }
  19. done
  20. }
  • 如果创建all-docs.md是必须的要求,然后更改最后一行为:
  1. } > all-docs.md
  • mapfile(又名readarray)和{fd}都需要bash v4+
英文:

A bash and find solution with your given input/files, something like:

  1. #!/usr/bin/env bash
  2. #: Find and concatenate all .md files from docs/ directory in all-docs.md file.
  3. find docs/ -type f -name &#39;*.md&#39; -exec sh -c &#39;cat -- &quot;$@&quot; &gt;&gt; all-docs.md&#39; sh {} +
  4. #: Parse the all-docs.md file and
  5. #: create/print the desired result/output.
  6. while IFS= read -ru &quot;$fd&quot; line; do
  7. if [[ $line == &quot;!include&quot;* ]]; then
  8. temp=${line#!include *}
  9. mapfile -t plum &lt; &quot;$temp&quot; &amp;&amp;
  10. unset -v &#39;plum[-1]&#39; &amp;&amp;
  11. printf &#39;%s\n&#39; &quot;${plum[@]:1}&quot;
  12. else
  13. printf &#39;%s\n&#39; &quot;$line&quot;
  14. fi
  15. done {fd}&lt; all-docs.md

  • Replace the last line of the code to
  1. done {fd}&lt; all-docs.md &gt; tempfile &amp;&amp; mv tempfile all-docs.md
  • If you're satisfied with the output and permanent changes needs to be made for all-docs.md.

Or just parse the output of find directly without creating the all-docs.md, something like:

  1. #!/usr/bin/env bash
  2. ##: Find all the files ending in .md in the docs/ directory
  3. ##: conCATenate all the contents of the files in question.
  4. find docs/ -type f -name &#39;*.md&#39; -exec sh -c &#39;cat -- &quot;$@&quot;&#39; sh {} + | {
  5. while IFS= read -r line; do
  6. [[ $line != &quot;!include&quot;* ]] &amp;&amp; { ##: If line does not have the pattern !include.
  7. printf &#39;%s\n&#39; &quot;$line&quot; ##: Print the line as is.
  8. }
  9. [[ $line == &quot;!include&quot;* ]] &amp;&amp; { ##: If line has the pattern !include parse it.
  10. temp=${line#!include *} ##: Extract FILE_PATH in a variable named temp.
  11. if [[ -s &quot;$temp&quot; ]]; then ##: If variable is an existing non-empty file.
  12. mapfile -u3 -t plum 3&lt; &quot;$temp&quot; &amp;&amp; ##: Extract the desired result.
  13. unset -v &#39;plum[-1]&#39; &amp;&amp;
  14. printf &#39;%s\n&#39; &quot;${plum[@]:1}&quot;
  15. else
  16. printf &#39;%s\n&#39; &quot;$line&quot; ##: Otherwise just print the line as is.
  17. fi
  18. }
  19. done
  20. }

If creating the all-docs.md is a must/requirement, then change the last line to:

  1. } &gt; all-docs.md

  • Both mapfile aka readarray and {fd} requires bash v4+

答案2

得分: 0

I was able to find a solution myself using Python.

  1. 使用Python找到了自己的解决方案。
  2. 定义一个名为replace_include_with_content的新函数。该函数以一行作为输入,检查它是否匹配!include子句的模式。如果匹配,它会提取包含路径,构建到相应UML文件的相对路径,读取其内容并返回。否则,它将原样返回该行。
  3. 遍历排序后的Markdown文件,读取它们的内容。在此循环内,遍历每个文件内容的行。对于每一行,我们调用replace_include_with_content函数来检查是否需要替换它。
英文:

I was able to find a solution myself using Python.

  1. Get a list of markdown files in the source "docs" directory and sort them based on their index prefix (files are named "0.introduction.md", "1.general.md", etc):
  2. Define a new function called replace_include_with_content. This function takes a line as input and checks if it matches the pattern for the !include clause. If it does, it extracts the include path, constructs the relative path to the corresponding uml file, reads its content, and returns it. Otherwise, it returns the line as is.
  3. Iterate through the sorted markdown files, read their contents. Within this loop, iterate over the lines of each file's content. For each line, we call the replace_include_with_content function to check if it needs to be replaced.
  1. import os
  2. import re
  3. docs_dir = &quot;docs&quot;
  4. uml_dir = &quot;uml&quot;
  5. output_file = &quot;all-docs.md&quot;
  6. # Get a sorted list of markdown files in the source directory
  7. markdown_files = sorted(
  8. ,
  9. key=lambda x: int(x.split(&quot;.&quot;)[0])
  10. )
  11. # Replace function
  12. def replace_include_with_content(line):
  13. # Check if the line matches the pattern for !include clause
  14. match = re.match(r&quot;!include\s+(.+)&quot;, line)
  15. if match:
  16. include_path = match.group(1)
  17. uml_file_path = os.path.join(uml_dir, include_path)
  18. # Read the content of the referenced UML file
  19. with open(uml_file_path, &quot;r&quot;) as uml_file:
  20. uml_content = uml_file.readlines()
  21. filtered_content = []
  22. include_next_line = False
  23. for uml_line in uml_content:
  24. if uml_line.strip() == &quot;@startuml&quot;:
  25. include_next_line = True
  26. elif uml_line.strip() == &quot;@enduml&quot;:
  27. include_next_line = False
  28. elif include_next_line:
  29. filtered_content.append(uml_line)
  30. return &quot;&quot;.join(filtered_content)
  31. return line
  32. # Open the output file in write mode
  33. with open(output_file, &quot;w&quot;) as outfile:
  34. for file in markdown_files:
  35. # Open each markdown file in read mode
  36. with open(os.path.join(docs_dir, file), &quot;r&quot;) as infile:
  37. content = infile.readlines()
  38. for line in content:
  39. # Replace the !include clause with the content from the referenced UML file
  40. replaced_line = replace_include_with_content(line)
  41. outfile.write(replaced_line)
  42. outfile.write(&quot;\n&quot;)

huangapple
  • 本文由 发表于 2023年5月13日 20:19:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/76242695.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定